mirror of
https://git.sr.ht/~thestr4ng3r/chiaki
synced 2025-08-20 13:33:13 -07:00
Propagate Touchpad State through TouchControlsFragment on Android
This commit is contained in:
parent
5914ceec77
commit
fa44a3269c
4 changed files with 56 additions and 20 deletions
|
@ -24,6 +24,8 @@ import com.metallic.chiaki.lib.ConnectVideoProfile
|
||||||
import com.metallic.chiaki.session.*
|
import com.metallic.chiaki.session.*
|
||||||
import com.metallic.chiaki.touchcontrols.TouchControlsFragment
|
import com.metallic.chiaki.touchcontrols.TouchControlsFragment
|
||||||
import com.metallic.chiaki.touchcontrols.TouchpadOnlyFragment
|
import com.metallic.chiaki.touchcontrols.TouchpadOnlyFragment
|
||||||
|
import io.reactivex.disposables.CompositeDisposable
|
||||||
|
import io.reactivex.rxkotlin.addTo
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
private sealed class DialogContents
|
private sealed class DialogContents
|
||||||
|
@ -113,18 +115,25 @@ class StreamActivity : AppCompatActivity(), View.OnSystemUiVisibilityChangeListe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val controlsDisposable = CompositeDisposable()
|
||||||
|
|
||||||
override fun onAttachFragment(fragment: Fragment)
|
override fun onAttachFragment(fragment: Fragment)
|
||||||
{
|
{
|
||||||
super.onAttachFragment(fragment)
|
super.onAttachFragment(fragment)
|
||||||
if(fragment is TouchControlsFragment)
|
when(fragment)
|
||||||
{
|
{
|
||||||
fragment.controllerStateCallback = { viewModel.input.touchControllerState = it }
|
is TouchControlsFragment ->
|
||||||
fragment.onScreenControlsEnabled = viewModel.onScreenControlsEnabled
|
{
|
||||||
}
|
fragment.controllerState
|
||||||
if(fragment is TouchpadOnlyFragment)
|
.subscribe { viewModel.input.touchControllerState = it }
|
||||||
{
|
.addTo(controlsDisposable)
|
||||||
fragment.controllerStateCallback = { viewModel.input.touchControllerState = it }
|
fragment.onScreenControlsEnabled = viewModel.onScreenControlsEnabled
|
||||||
fragment.touchpadOnlyEnabled = viewModel.touchpadOnlyEnabled
|
}
|
||||||
|
is TouchpadOnlyFragment ->
|
||||||
|
{
|
||||||
|
fragment.controllerStateCallback = { viewModel.input.touchControllerState = it }
|
||||||
|
fragment.touchpadOnlyEnabled = viewModel.touchpadOnlyEnabled
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,6 +150,12 @@ class StreamActivity : AppCompatActivity(), View.OnSystemUiVisibilityChangeListe
|
||||||
viewModel.session.pause()
|
viewModel.session.pause()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroy()
|
||||||
|
{
|
||||||
|
super.onDestroy()
|
||||||
|
controlsDisposable.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
private fun reconnect()
|
private fun reconnect()
|
||||||
{
|
{
|
||||||
viewModel.session.shutdown()
|
viewModel.session.shutdown()
|
||||||
|
|
|
@ -11,19 +11,31 @@ import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import com.metallic.chiaki.databinding.FragmentControlsBinding
|
import com.metallic.chiaki.databinding.FragmentControlsBinding
|
||||||
import com.metallic.chiaki.lib.ControllerState
|
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()
|
class TouchControlsFragment : Fragment()
|
||||||
{
|
{
|
||||||
private var controllerState = ControllerState()
|
private var ownControllerState = ControllerState()
|
||||||
private set(value)
|
private set(value)
|
||||||
{
|
{
|
||||||
val diff = field != value
|
val diff = field != value
|
||||||
field = value
|
field = value
|
||||||
if(diff)
|
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
|
var onScreenControlsEnabled: LiveData<Boolean>? = null
|
||||||
|
|
||||||
private var _binding: FragmentControlsBinding? = null
|
private var _binding: FragmentControlsBinding? = null
|
||||||
|
@ -32,6 +44,9 @@ class TouchControlsFragment : Fragment()
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
|
||||||
FragmentControlsBinding.inflate(inflater, container, false).let {
|
FragmentControlsBinding.inflate(inflater, container, false).let {
|
||||||
_binding = it
|
_binding = it
|
||||||
|
controllerStateProxy.onNext(
|
||||||
|
combineLatest(ownControllerStateSubject, binding.touchpadView.controllerState) { a, b -> a or b }
|
||||||
|
)
|
||||||
it.root
|
it.root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,19 +67,19 @@ class TouchControlsFragment : Fragment()
|
||||||
binding.psButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_PS)
|
binding.psButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_PS)
|
||||||
binding.touchpadButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_TOUCHPAD)
|
binding.touchpadButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_TOUCHPAD)
|
||||||
|
|
||||||
binding.l2ButtonView.buttonPressedCallback = { controllerState = controllerState.copy().apply { l2State = if(it) 255U else 0U } }
|
binding.l2ButtonView.buttonPressedCallback = { ownControllerState = ownControllerState.copy().apply { l2State = if(it) 255U else 0U } }
|
||||||
binding.r2ButtonView.buttonPressedCallback = { controllerState = controllerState.copy().apply { r2State = if(it) 255U else 0U } }
|
binding.r2ButtonView.buttonPressedCallback = { ownControllerState = ownControllerState.copy().apply { r2State = if(it) 255U else 0U } }
|
||||||
|
|
||||||
val quantizeStick = { f: Float ->
|
val quantizeStick = { f: Float ->
|
||||||
(Short.MAX_VALUE * f).toInt().toShort()
|
(Short.MAX_VALUE * f).toInt().toShort()
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.leftAnalogStickView.stateChangedCallback = { controllerState = controllerState.copy().apply {
|
binding.leftAnalogStickView.stateChangedCallback = { ownControllerState = ownControllerState.copy().apply {
|
||||||
leftX = quantizeStick(it.x)
|
leftX = quantizeStick(it.x)
|
||||||
leftY = quantizeStick(it.y)
|
leftY = quantizeStick(it.y)
|
||||||
}}
|
}}
|
||||||
|
|
||||||
binding.rightAnalogStickView.stateChangedCallback = { controllerState = controllerState.copy().apply {
|
binding.rightAnalogStickView.stateChangedCallback = { ownControllerState = ownControllerState.copy().apply {
|
||||||
rightX = quantizeStick(it.x)
|
rightX = quantizeStick(it.x)
|
||||||
rightY = quantizeStick(it.y)
|
rightY = quantizeStick(it.y)
|
||||||
}}
|
}}
|
||||||
|
@ -76,7 +91,7 @@ class TouchControlsFragment : Fragment()
|
||||||
|
|
||||||
private fun dpadStateChanged(direction: DPadView.Direction?)
|
private fun dpadStateChanged(direction: DPadView.Direction?)
|
||||||
{
|
{
|
||||||
controllerState = controllerState.copy().apply {
|
ownControllerState = ownControllerState.copy().apply {
|
||||||
buttons = ((buttons
|
buttons = ((buttons
|
||||||
and ControllerState.BUTTON_DPAD_LEFT.inv()
|
and ControllerState.BUTTON_DPAD_LEFT.inv()
|
||||||
and ControllerState.BUTTON_DPAD_RIGHT.inv()
|
and ControllerState.BUTTON_DPAD_RIGHT.inv()
|
||||||
|
@ -98,7 +113,7 @@ class TouchControlsFragment : Fragment()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buttonStateChanged(buttonMask: UInt) = { pressed: Boolean ->
|
private fun buttonStateChanged(buttonMask: UInt) = { pressed: Boolean ->
|
||||||
controllerState = controllerState.copy().apply {
|
ownControllerState = ownControllerState.copy().apply {
|
||||||
buttons =
|
buttons =
|
||||||
if(pressed)
|
if(pressed)
|
||||||
buttons or buttonMask
|
buttons or buttonMask
|
||||||
|
|
|
@ -10,15 +10,20 @@ import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import com.metallic.chiaki.R
|
import com.metallic.chiaki.R
|
||||||
import com.metallic.chiaki.lib.ControllerState
|
import com.metallic.chiaki.lib.ControllerState
|
||||||
|
import io.reactivex.Observable
|
||||||
|
import io.reactivex.subjects.BehaviorSubject
|
||||||
|
import io.reactivex.subjects.Subject
|
||||||
|
|
||||||
class TouchpadView @JvmOverloads constructor(
|
class TouchpadView @JvmOverloads constructor(
|
||||||
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
||||||
) : View(context, attrs, defStyleAttr)
|
) : View(context, attrs, defStyleAttr)
|
||||||
{
|
{
|
||||||
val state: ControllerState = ControllerState()
|
private val state: ControllerState = ControllerState()
|
||||||
private val pointerTouchIds = mutableMapOf<Int, UByte>()
|
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?
|
private val drawable: Drawable?
|
||||||
|
|
||||||
|
@ -81,6 +86,6 @@ class TouchpadView @JvmOverloads constructor(
|
||||||
|
|
||||||
private fun triggerStateChanged()
|
private fun triggerStateChanged()
|
||||||
{
|
{
|
||||||
stateChangeCallback?.let { it(state) }
|
stateSubject.onNext(state)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -48,6 +48,7 @@
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<com.metallic.chiaki.touchcontrols.TouchpadView
|
<com.metallic.chiaki.touchcontrols.TouchpadView
|
||||||
|
android:id="@+id/touchpadView"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
app:drawable="@drawable/control_touchpad"
|
app:drawable="@drawable/control_touchpad"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue