Propagate Touchpad State through TouchControlsFragment on Android

This commit is contained in:
Florian Märkl 2021-01-15 15:32:37 +01:00
commit fa44a3269c
No known key found for this signature in database
GPG key ID: 125BC8A5A6A1E857
4 changed files with 56 additions and 20 deletions

View file

@ -24,6 +24,8 @@ import com.metallic.chiaki.lib.ConnectVideoProfile
import com.metallic.chiaki.session.*
import com.metallic.chiaki.touchcontrols.TouchControlsFragment
import com.metallic.chiaki.touchcontrols.TouchpadOnlyFragment
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.addTo
import kotlin.math.min
private sealed class DialogContents
@ -113,18 +115,25 @@ class StreamActivity : AppCompatActivity(), View.OnSystemUiVisibilityChangeListe
}
}
private val controlsDisposable = CompositeDisposable()
override fun onAttachFragment(fragment: Fragment)
{
super.onAttachFragment(fragment)
if(fragment is TouchControlsFragment)
when(fragment)
{
fragment.controllerStateCallback = { viewModel.input.touchControllerState = it }
fragment.onScreenControlsEnabled = viewModel.onScreenControlsEnabled
}
if(fragment is TouchpadOnlyFragment)
{
fragment.controllerStateCallback = { viewModel.input.touchControllerState = it }
fragment.touchpadOnlyEnabled = viewModel.touchpadOnlyEnabled
is TouchControlsFragment ->
{
fragment.controllerState
.subscribe { viewModel.input.touchControllerState = it }
.addTo(controlsDisposable)
fragment.onScreenControlsEnabled = viewModel.onScreenControlsEnabled
}
is TouchpadOnlyFragment ->
{
fragment.controllerStateCallback = { viewModel.input.touchControllerState = it }
fragment.touchpadOnlyEnabled = viewModel.touchpadOnlyEnabled
}
}
}
@ -141,6 +150,12 @@ class StreamActivity : AppCompatActivity(), View.OnSystemUiVisibilityChangeListe
viewModel.session.pause()
}
override fun onDestroy()
{
super.onDestroy()
controlsDisposable.dispose()
}
private fun reconnect()
{
viewModel.session.shutdown()

View file

@ -11,19 +11,31 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import com.metallic.chiaki.databinding.FragmentControlsBinding
import com.metallic.chiaki.lib.ControllerState
import io.reactivex.Observable
import io.reactivex.Observable.combineLatest
import io.reactivex.subjects.BehaviorSubject
import io.reactivex.subjects.Subject
class TouchControlsFragment : Fragment()
{
private var controllerState = ControllerState()
private var ownControllerState = ControllerState()
private set(value)
{
val diff = field != value
field = value
if(diff)
controllerStateCallback?.let { it(value) }
ownControllerStateSubject.onNext(ownControllerState)
}
var controllerStateCallback: ((ControllerState) -> Unit)? = null
private val ownControllerStateSubject: Subject<ControllerState>
= BehaviorSubject.create<ControllerState>().also { it.onNext(ownControllerState) }
// to delay attaching to the touchpadView until it's available
private val controllerStateProxy: Subject<Observable<ControllerState>>
= BehaviorSubject.create<Observable<ControllerState>>().also { it.onNext(ownControllerStateSubject) }
val controllerState: Observable<ControllerState> get() =
controllerStateProxy.flatMap { it }
var onScreenControlsEnabled: LiveData<Boolean>? = null
private var _binding: FragmentControlsBinding? = null
@ -32,6 +44,9 @@ class TouchControlsFragment : Fragment()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
FragmentControlsBinding.inflate(inflater, container, false).let {
_binding = it
controllerStateProxy.onNext(
combineLatest(ownControllerStateSubject, binding.touchpadView.controllerState) { a, b -> a or b }
)
it.root
}
@ -52,19 +67,19 @@ class TouchControlsFragment : Fragment()
binding.psButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_PS)
binding.touchpadButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_TOUCHPAD)
binding.l2ButtonView.buttonPressedCallback = { controllerState = controllerState.copy().apply { l2State = if(it) 255U else 0U } }
binding.r2ButtonView.buttonPressedCallback = { controllerState = controllerState.copy().apply { r2State = if(it) 255U else 0U } }
binding.l2ButtonView.buttonPressedCallback = { ownControllerState = ownControllerState.copy().apply { l2State = if(it) 255U else 0U } }
binding.r2ButtonView.buttonPressedCallback = { ownControllerState = ownControllerState.copy().apply { r2State = if(it) 255U else 0U } }
val quantizeStick = { f: Float ->
(Short.MAX_VALUE * f).toInt().toShort()
}
binding.leftAnalogStickView.stateChangedCallback = { controllerState = controllerState.copy().apply {
binding.leftAnalogStickView.stateChangedCallback = { ownControllerState = ownControllerState.copy().apply {
leftX = quantizeStick(it.x)
leftY = quantizeStick(it.y)
}}
binding.rightAnalogStickView.stateChangedCallback = { controllerState = controllerState.copy().apply {
binding.rightAnalogStickView.stateChangedCallback = { ownControllerState = ownControllerState.copy().apply {
rightX = quantizeStick(it.x)
rightY = quantizeStick(it.y)
}}
@ -76,7 +91,7 @@ class TouchControlsFragment : Fragment()
private fun dpadStateChanged(direction: DPadView.Direction?)
{
controllerState = controllerState.copy().apply {
ownControllerState = ownControllerState.copy().apply {
buttons = ((buttons
and ControllerState.BUTTON_DPAD_LEFT.inv()
and ControllerState.BUTTON_DPAD_RIGHT.inv()
@ -98,7 +113,7 @@ class TouchControlsFragment : Fragment()
}
private fun buttonStateChanged(buttonMask: UInt) = { pressed: Boolean ->
controllerState = controllerState.copy().apply {
ownControllerState = ownControllerState.copy().apply {
buttons =
if(pressed)
buttons or buttonMask

View file

@ -10,15 +10,20 @@ import android.view.MotionEvent
import android.view.View
import com.metallic.chiaki.R
import com.metallic.chiaki.lib.ControllerState
import io.reactivex.Observable
import io.reactivex.subjects.BehaviorSubject
import io.reactivex.subjects.Subject
class TouchpadView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr)
{
val state: ControllerState = ControllerState()
private val state: ControllerState = ControllerState()
private val pointerTouchIds = mutableMapOf<Int, UByte>()
var stateChangeCallback: ((ControllerState) -> Unit)? = null
private val stateSubject: Subject<ControllerState>
= BehaviorSubject.create<ControllerState>().also { it.onNext(state) }
val controllerState: Observable<ControllerState> get() = stateSubject
private val drawable: Drawable?
@ -81,6 +86,6 @@ class TouchpadView @JvmOverloads constructor(
private fun triggerStateChanged()
{
stateChangeCallback?.let { it(state) }
stateSubject.onNext(state)
}
}

View file

@ -48,6 +48,7 @@
/>
<com.metallic.chiaki.touchcontrols.TouchpadView
android:id="@+id/touchpadView"
android:layout_width="0dp"
android:layout_height="0dp"
app:drawable="@drawable/control_touchpad"