diff --git a/android/app/src/main/java/com/metallic/chiaki/common/DisplayHost.kt b/android/app/src/main/java/com/metallic/chiaki/common/DisplayHost.kt index 841da65..0aa1b98 100644 --- a/android/app/src/main/java/com/metallic/chiaki/common/DisplayHost.kt +++ b/android/app/src/main/java/com/metallic/chiaki/common/DisplayHost.kt @@ -23,6 +23,8 @@ sealed class DisplayHost { abstract val registeredHost: RegisteredHost? abstract val host: String + abstract val name: String? + abstract val id: String? } class DiscoveredDisplayHost( @@ -31,6 +33,8 @@ class DiscoveredDisplayHost( ): DisplayHost() { override val host get() = discoveredHost.hostAddr ?: "" + override val name get() = discoveredHost.hostName ?: registeredHost?.ps4Nickname + override val id get() = discoveredHost.hostId ?: registeredHost?.ps4Mac?.toString() } class ManualDisplayHost( @@ -39,4 +43,6 @@ class ManualDisplayHost( ): DisplayHost() { override val host get() = manualHost.host + override val name get() = registeredHost?.ps4Nickname + override val id get() = registeredHost?.ps4Mac?.toString() } \ No newline at end of file diff --git a/android/app/src/main/java/com/metallic/chiaki/common/ext/PublisherLiveData.kt b/android/app/src/main/java/com/metallic/chiaki/common/ext/RxLiveData.kt similarity index 82% rename from android/app/src/main/java/com/metallic/chiaki/common/ext/PublisherLiveData.kt rename to android/app/src/main/java/com/metallic/chiaki/common/ext/RxLiveData.kt index c370604..04ef832 100644 --- a/android/app/src/main/java/com/metallic/chiaki/common/ext/PublisherLiveData.kt +++ b/android/app/src/main/java/com/metallic/chiaki/common/ext/RxLiveData.kt @@ -18,6 +18,9 @@ package com.metallic.chiaki.common.ext import androidx.lifecycle.LiveDataReactiveStreams +import io.reactivex.BackpressureStrategy +import io.reactivex.Observable import org.reactivestreams.Publisher -fun Publisher.toLiveData() = LiveDataReactiveStreams.fromPublisher(this) \ No newline at end of file +fun Publisher.toLiveData() = LiveDataReactiveStreams.fromPublisher(this) +fun Observable.toLiveData() = this.toFlowable(BackpressureStrategy.LATEST).toLiveData() \ No newline at end of file diff --git a/android/app/src/main/java/com/metallic/chiaki/discovery/DiscoveryManager.kt b/android/app/src/main/java/com/metallic/chiaki/discovery/DiscoveryManager.kt index 8f91306..d4fe86b 100644 --- a/android/app/src/main/java/com/metallic/chiaki/discovery/DiscoveryManager.kt +++ b/android/app/src/main/java/com/metallic/chiaki/discovery/DiscoveryManager.kt @@ -19,8 +19,12 @@ package com.metallic.chiaki.discovery import android.util.Log import com.metallic.chiaki.lib.CreateError +import com.metallic.chiaki.lib.DiscoveryHost import com.metallic.chiaki.lib.DiscoveryService import com.metallic.chiaki.lib.DiscoveryServiceOptions +import io.reactivex.Observable +import io.reactivex.subjects.BehaviorSubject +import io.reactivex.subjects.Subject import java.net.InetSocketAddress class DiscoveryManager @@ -35,26 +39,61 @@ class DiscoveryManager private var discoveryService: DiscoveryService? = null - fun start() + private val discoveryActiveSubject: Subject = BehaviorSubject.create().also { it.onNext(false) } + val discoveryActive: Observable get() = discoveryActiveSubject + var active = false + set(value) + { + field = value + discoveryActiveSubject.onNext(value) + updateService() + } + private var paused = false + + private var discoveredHostsSubject: Subject> = BehaviorSubject.create>().also { + it.onNext(listOf()) + }.toSerialized() + val discoveredHosts: Observable> get() = discoveredHostsSubject + + fun resume() { - if(discoveryService != null) - return - try - { - discoveryService = DiscoveryService(DiscoveryServiceOptions( - HOSTS_MAX, DROP_PINGS, PING_MS, InetSocketAddress("255.255.255.255", PORT) - )) - } - catch(e: CreateError) - { - Log.e("DiscoveryManager", "Failed to start Discovery Service: $e") - } + paused = false + updateService() } - fun stop() + fun pause() { - val service = discoveryService ?: return - service.dispose() - discoveryService = null + paused = true + updateService() + } + + fun dispose() + { + active = false + } + + private fun updateService() + { + if(active && !paused && discoveryService == null) + { + try + { + discoveryService = DiscoveryService(DiscoveryServiceOptions( + HOSTS_MAX, DROP_PINGS, PING_MS, InetSocketAddress("255.255.255.255", PORT) + ), discoveredHostsSubject::onNext) + } + catch(e: CreateError) + { + Log.e("DiscoveryManager", "Failed to start Discovery Service: $e") + } + } + else if(discoveryService != null) + { + val service = discoveryService ?: return + service.dispose() + discoveryService = null + discoveredHostsSubject.onNext(listOf()) + discoveryActiveSubject.onNext(false) + } } } \ No newline at end of file diff --git a/android/app/src/main/java/com/metallic/chiaki/lib/Chiaki.kt b/android/app/src/main/java/com/metallic/chiaki/lib/Chiaki.kt index 1f6bf29..5596991 100644 --- a/android/app/src/main/java/com/metallic/chiaki/lib/Chiaki.kt +++ b/android/app/src/main/java/com/metallic/chiaki/lib/Chiaki.kt @@ -212,7 +212,7 @@ data class DiscoveryServiceOptions( class DiscoveryService( options: DiscoveryServiceOptions, - val callback: ((hosts: List) -> Unit)? = null) + val callback: ((hosts: List) -> Unit)?) { private var nativePtr: Long diff --git a/android/app/src/main/java/com/metallic/chiaki/main/DisplayHostRecyclerViewAdapter.kt b/android/app/src/main/java/com/metallic/chiaki/main/DisplayHostRecyclerViewAdapter.kt index 93e7ba9..e7ffb15 100644 --- a/android/app/src/main/java/com/metallic/chiaki/main/DisplayHostRecyclerViewAdapter.kt +++ b/android/app/src/main/java/com/metallic/chiaki/main/DisplayHostRecyclerViewAdapter.kt @@ -21,8 +21,10 @@ import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.metallic.chiaki.R +import com.metallic.chiaki.common.DiscoveredDisplayHost import com.metallic.chiaki.common.DisplayHost import com.metallic.chiaki.common.ext.inflate +import com.metallic.chiaki.lib.DiscoveryHost import kotlinx.android.synthetic.main.item_display_host.view.* class DisplayHostRecyclerViewAdapter: RecyclerView.Adapter() @@ -43,9 +45,24 @@ class DisplayHostRecyclerViewAdapter: RecyclerView.Adapter R.drawable.ic_console_standby + DiscoveryHost.State.READY -> R.drawable.ic_console_ready + else -> R.drawable.ic_console + } + else + R.drawable.ic_console) } } } \ No newline at end of file diff --git a/android/app/src/main/java/com/metallic/chiaki/main/MainActivity.kt b/android/app/src/main/java/com/metallic/chiaki/main/MainActivity.kt index 34edd87..3080a7a 100644 --- a/android/app/src/main/java/com/metallic/chiaki/main/MainActivity.kt +++ b/android/app/src/main/java/com/metallic/chiaki/main/MainActivity.kt @@ -38,6 +38,10 @@ class MainActivity : AppCompatActivity() { private val disposable = CompositeDisposable() + private lateinit var viewModel: MainViewModel + + private var discoveryMenuItem: MenuItem? = null + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -46,7 +50,6 @@ class MainActivity : AppCompatActivity() title = "" setSupportActionBar(toolbar) - addButton.setOnClickListener { Intent(this, TestStartActivity::class.java).also { it.putExtra(TestStartActivity.EXTRA_REVEAL_X, addButton.x + addButton.width * 0.5f) @@ -55,7 +58,7 @@ class MainActivity : AppCompatActivity() } } - val viewModel = ViewModelProviders + viewModel = ViewModelProviders .of(this, viewModelFactory { MainViewModel(getDatabase(this)) }) .get(MainViewModel::class.java) @@ -63,6 +66,10 @@ class MainActivity : AppCompatActivity() hostsRecyclerView.adapter = recyclerViewAdapter hostsRecyclerView.layoutManager = LinearLayoutManager(this) viewModel.displayHosts.observe(this, Observer { recyclerViewAdapter.hosts = it }) + + viewModel.discoveryActive.observe(this, Observer { active -> + discoveryMenuItem?.let { updateDiscoveryMenuItem(it, active) } + }) } override fun onDestroy() @@ -71,14 +78,29 @@ class MainActivity : AppCompatActivity() disposable.dispose() } - override fun onCreateOptionsMenu(menu: Menu?): Boolean + override fun onCreateOptionsMenu(menu: Menu): Boolean { menuInflater.inflate(R.menu.main, menu) + val discoveryItem = menu.findItem(R.id.action_discover) + discoveryMenuItem = discoveryItem + updateDiscoveryMenuItem(discoveryItem, viewModel.discoveryActive.value ?: false) return true } + private fun updateDiscoveryMenuItem(item: MenuItem, active: Boolean) + { + item.isChecked = active + item.setIcon(if(active) R.drawable.ic_discover_on else R.drawable.ic_discover_off) + } + override fun onOptionsItemSelected(item: MenuItem): Boolean = when(item.itemId) { + R.id.action_discover -> + { + viewModel.discoveryManager.active = !(viewModel.discoveryActive.value ?: false) + true + } + R.id.action_settings -> { Intent(this, SettingsActivity::class.java).also { diff --git a/android/app/src/main/java/com/metallic/chiaki/main/MainViewModel.kt b/android/app/src/main/java/com/metallic/chiaki/main/MainViewModel.kt index f5f6aec..fe2336a 100644 --- a/android/app/src/main/java/com/metallic/chiaki/main/MainViewModel.kt +++ b/android/app/src/main/java/com/metallic/chiaki/main/MainViewModel.kt @@ -19,27 +19,36 @@ package com.metallic.chiaki.main import androidx.lifecycle.ViewModel import com.metallic.chiaki.common.AppDatabase +import com.metallic.chiaki.common.DiscoveredDisplayHost import com.metallic.chiaki.common.ManualDisplayHost import com.metallic.chiaki.common.ext.toLiveData import com.metallic.chiaki.discovery.DiscoveryManager +import io.reactivex.rxkotlin.Observables class MainViewModel(val database: AppDatabase): ViewModel() { + val discoveryManager = DiscoveryManager().also { it.active = true /* TODO: from shared preferences */ } + val displayHosts by lazy { - database.manualHostDao().getAll() - .map { - it.map { manualHost -> - ManualDisplayHost(null, manualHost) + Observables.combineLatest(database.manualHostDao().getAll().toObservable(), discoveryManager.discoveredHosts) + { manualHosts, discoveredHosts -> + discoveredHosts.map { + DiscoveredDisplayHost(null /* TODO */, it) + } + + manualHosts.map { + ManualDisplayHost(null /* TODO */, it) } } .toLiveData() } - val discoveryManager = DiscoveryManager().also { it.start() } + val discoveryActive by lazy { + discoveryManager.discoveryActive.toLiveData() + } override fun onCleared() { super.onCleared() - discoveryManager.stop() + discoveryManager.dispose() } } \ No newline at end of file diff --git a/android/app/src/main/res/drawable/ic_discover_off.xml b/android/app/src/main/res/drawable/ic_discover_off.xml index 32baf35..bb34cb6 100644 --- a/android/app/src/main/res/drawable/ic_discover_off.xml +++ b/android/app/src/main/res/drawable/ic_discover_off.xml @@ -1,4 +1,4 @@ - + diff --git a/android/app/src/main/res/drawable/ic_discover_on.xml b/android/app/src/main/res/drawable/ic_discover_on.xml index 7515126..5b2102f 100644 --- a/android/app/src/main/res/drawable/ic_discover_on.xml +++ b/android/app/src/main/res/drawable/ic_discover_on.xml @@ -1,4 +1,4 @@ - + diff --git a/android/app/src/main/res/drawable/ic_launcher_background.xml b/android/app/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index 0d025f9..0000000 --- a/android/app/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/android/app/src/main/res/layout/item_display_host.xml b/android/app/src/main/res/layout/item_display_host.xml index f8936ac..5f5d059 100644 --- a/android/app/src/main/res/layout/item_display_host.xml +++ b/android/app/src/main/res/layout/item_display_host.xml @@ -17,22 +17,29 @@ android:layout_marginBottom="0dp" android:elevation="8dp"> - + + + + + - - Connect Settings Discover Consoles Automatically + Address: %s + ID: %s