diff --git a/android/app/src/main/java/com/metallic/chiaki/StreamSession.kt b/android/app/src/main/java/com/metallic/chiaki/StreamSession.kt
deleted file mode 100644
index 4bf7da8..0000000
--- a/android/app/src/main/java/com/metallic/chiaki/StreamSession.kt
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * This file is part of Chiaki.
- *
- * Chiaki is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Chiaki is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Chiaki. If not, see .
- */
-
-package com.metallic.chiaki
-
-import android.graphics.SurfaceTexture
-import android.util.Log
-import android.view.*
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MutableLiveData
-import com.metallic.chiaki.lib.*
-
-sealed class StreamState
-object StreamStateIdle: StreamState()
-object StreamStateConnecting: StreamState()
-object StreamStateConnected: StreamState()
-data class StreamStateCreateError(val error: CreateError): StreamState()
-data class StreamStateQuit(val reason: QuitReason, val reasonString: String?): StreamState()
-data class StreamStateLoginPinRequest(val pinIncorrect: Boolean): StreamState()
-
-class StreamSession(val connectInfo: ConnectInfo)
-{
- var session: Session? = null
- private set
-
- private val _state = MutableLiveData(StreamStateIdle)
- val state: LiveData get() = _state
-
- private val keyControllerState = ControllerState() // from KeyEvents
- private val motionControllerState = ControllerState() // from MotionEvents
- private var touchControllerState = ControllerState()
-
- var surfaceTexture: SurfaceTexture? = null
-
- fun shutdown()
- {
- session?.stop()
- session?.dispose()
- session = null
- _state.value = StreamStateIdle
- //surfaceTexture?.release()
- }
-
- fun pause()
- {
- shutdown()
- }
-
- fun resume()
- {
- if(session != null)
- return
- try
- {
- val session = Session(connectInfo)
- _state.value = StreamStateConnecting
- session.eventCallback = this::eventCallback
- session.start()
- val surfaceTexture = surfaceTexture
- if(surfaceTexture != null)
- session.setSurface(Surface(surfaceTexture))
- this.session = session
- }
- catch(e: CreateError)
- {
- _state.value = StreamStateCreateError(e)
- }
- }
-
- private fun eventCallback(event: Event)
- {
- when(event)
- {
- is ConnectedEvent -> _state.postValue(StreamStateConnected)
- is QuitEvent -> _state.postValue(StreamStateQuit(event.reason, event.reasonString))
- is LoginPinRequestEvent -> _state.postValue(StreamStateLoginPinRequest(event.pinIncorrect))
- }
- }
-
- fun attachToTextureView(textureView: TextureView)
- {
- textureView.surfaceTextureListener = object: TextureView.SurfaceTextureListener {
- override fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int)
- {
- if(surfaceTexture != null)
- return
- surfaceTexture = surface
- session?.setSurface(Surface(surface))
- }
-
- override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean
- {
- // return false if we want to keep the surface texture
- return surfaceTexture == null
- }
-
- override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture, width: Int, height: Int) { }
- override fun onSurfaceTextureUpdated(surface: SurfaceTexture) {}
- }
-
- val surfaceTexture = surfaceTexture
- if(surfaceTexture != null)
- textureView.surfaceTexture = surfaceTexture
- }
-
- fun setLoginPin(pin: String)
- {
- session?.setLoginPin(pin)
- }
-
- fun dispatchKeyEvent(event: KeyEvent): Boolean
- {
- Log.i("StreamSession", "key event $event")
- if(event.action != KeyEvent.ACTION_DOWN && event.action != KeyEvent.ACTION_UP)
- return false
-
- when(event.keyCode)
- {
- KeyEvent.KEYCODE_BUTTON_L2 -> {
- keyControllerState.l2State = if(event.action == KeyEvent.ACTION_DOWN) UByte.MAX_VALUE else 0U
- return true
- }
- KeyEvent.KEYCODE_BUTTON_R2 -> {
- keyControllerState.r2State = if(event.action == KeyEvent.ACTION_DOWN) UByte.MAX_VALUE else 0U
- return true
- }
- }
-
- val buttonMask: UInt = when(event.keyCode)
- {
- // dpad handled by MotionEvents
- //KeyEvent.KEYCODE_DPAD_LEFT -> ControllerState.BUTTON_DPAD_LEFT
- //KeyEvent.KEYCODE_DPAD_RIGHT -> ControllerState.BUTTON_DPAD_RIGHT
- //KeyEvent.KEYCODE_DPAD_UP -> ControllerState.BUTTON_DPAD_UP
- //KeyEvent.KEYCODE_DPAD_DOWN -> ControllerState.BUTTON_DPAD_DOWN
- KeyEvent.KEYCODE_BUTTON_A -> ControllerState.BUTTON_CROSS
- KeyEvent.KEYCODE_BUTTON_B -> ControllerState.BUTTON_MOON
- KeyEvent.KEYCODE_BUTTON_X -> ControllerState.BUTTON_BOX
- KeyEvent.KEYCODE_BUTTON_Y -> ControllerState.BUTTON_PYRAMID
- KeyEvent.KEYCODE_BUTTON_L1 -> ControllerState.BUTTON_L1
- KeyEvent.KEYCODE_BUTTON_R1 -> ControllerState.BUTTON_R1
- KeyEvent.KEYCODE_BUTTON_THUMBL -> ControllerState.BUTTON_L3
- KeyEvent.KEYCODE_BUTTON_THUMBR -> ControllerState.BUTTON_R3
- KeyEvent.KEYCODE_BUTTON_SELECT -> ControllerState.BUTTON_SHARE
- KeyEvent.KEYCODE_BUTTON_START -> ControllerState.BUTTON_OPTIONS
- KeyEvent.KEYCODE_BUTTON_C -> ControllerState.BUTTON_PS
- KeyEvent.KEYCODE_BUTTON_MODE -> ControllerState.BUTTON_PS
- else -> return false
- }
-
- keyControllerState.buttons = keyControllerState.buttons.run {
- when(event.action)
- {
- KeyEvent.ACTION_DOWN -> this or buttonMask
- KeyEvent.ACTION_UP -> this and buttonMask.inv()
- else -> this
- }
- }
-
- sendControllerState()
- return true
- }
-
- fun onGenericMotionEvent(event: MotionEvent): Boolean
- {
- if(event.source and InputDevice.SOURCE_CLASS_JOYSTICK != InputDevice.SOURCE_CLASS_JOYSTICK)
- return false
- fun Float.signedAxis() = (this * Short.MAX_VALUE).toShort()
- fun Float.unsignedAxis() = (this * UByte.MAX_VALUE.toFloat()).toUInt().toUByte()
- motionControllerState.leftX = event.getAxisValue(MotionEvent.AXIS_X).signedAxis()
- motionControllerState.leftY = event.getAxisValue(MotionEvent.AXIS_Y).signedAxis()
- motionControllerState.rightX = event.getAxisValue(MotionEvent.AXIS_Z).signedAxis()
- motionControllerState.rightY = event.getAxisValue(MotionEvent.AXIS_RZ).signedAxis()
- motionControllerState.l2State = event.getAxisValue(MotionEvent.AXIS_LTRIGGER).unsignedAxis()
- motionControllerState.r2State = event.getAxisValue(MotionEvent.AXIS_RTRIGGER).unsignedAxis()
- motionControllerState.buttons = motionControllerState.buttons.let {
- val dpadX = event.getAxisValue(MotionEvent.AXIS_HAT_X)
- val dpadY = event.getAxisValue(MotionEvent.AXIS_HAT_Y)
- val dpadButtons =
- (if(dpadX > 0.5f) ControllerState.BUTTON_DPAD_RIGHT else 0U) or
- (if(dpadX < -0.5f) ControllerState.BUTTON_DPAD_LEFT else 0U) or
- (if(dpadY > 0.5f) ControllerState.BUTTON_DPAD_DOWN else 0U) or
- (if(dpadY < -0.5f) ControllerState.BUTTON_DPAD_UP else 0U)
- it and (ControllerState.BUTTON_DPAD_RIGHT or
- ControllerState.BUTTON_DPAD_LEFT or
- ControllerState.BUTTON_DPAD_DOWN or
- ControllerState.BUTTON_DPAD_UP).inv() or
- dpadButtons
- }
- Log.i("StreamSession", "motionEvent => $motionControllerState")
- sendControllerState()
- return true
- }
-
- fun updateTouchControllerState(controllerState: ControllerState)
- {
- touchControllerState = controllerState
- sendControllerState()
- }
-
- private fun sendControllerState()
- {
- val controllerState = keyControllerState or motionControllerState
-
- // prioritize motion controller's l2 and r2 over key
- // (some controllers send only key, others both but key earlier than full press)
- if(motionControllerState.l2State > 0U)
- controllerState.l2State = motionControllerState.l2State
- if(motionControllerState.r2State > 0U)
- controllerState.r2State = motionControllerState.r2State
-
- session?.setControllerState(controllerState or touchControllerState)
- }
-}
\ No newline at end of file
diff --git a/android/app/src/main/java/com/metallic/chiaki/common/Preferences.kt b/android/app/src/main/java/com/metallic/chiaki/common/Preferences.kt
index cd30456..17d8da4 100644
--- a/android/app/src/main/java/com/metallic/chiaki/common/Preferences.kt
+++ b/android/app/src/main/java/com/metallic/chiaki/common/Preferences.kt
@@ -79,6 +79,11 @@ class Preferences(context: Context)
get() = sharedPreferences.getBoolean(logVerboseKey, false)
set(value) { sharedPreferences.edit().putBoolean(logVerboseKey, value).apply() }
+ val swapCrossMoonKey get() = resources.getString(R.string.preferences_swap_cross_moon_key)
+ var swapCrossMoon
+ get() = sharedPreferences.getBoolean(swapCrossMoonKey, false)
+ set(value) { sharedPreferences.edit().putBoolean(swapCrossMoonKey, value).apply() }
+
val resolutionKey get() = resources.getString(R.string.preferences_resolution_key)
var resolution
get() = sharedPreferences.getString(resolutionKey, resolutionDefault.value)?.let { value ->
diff --git a/android/app/src/main/java/com/metallic/chiaki/session/StreamInput.kt b/android/app/src/main/java/com/metallic/chiaki/session/StreamInput.kt
new file mode 100644
index 0000000..9cd2067
--- /dev/null
+++ b/android/app/src/main/java/com/metallic/chiaki/session/StreamInput.kt
@@ -0,0 +1,127 @@
+package com.metallic.chiaki.session
+
+import android.util.Log
+import android.view.InputDevice
+import android.view.KeyEvent
+import android.view.MotionEvent
+import com.metallic.chiaki.common.Preferences
+import com.metallic.chiaki.lib.ControllerState
+
+class StreamInput(val preferences: Preferences)
+{
+ var controllerStateChangedCallback: ((ControllerState) -> Unit)? = null
+
+ val controllerState: ControllerState get()
+ {
+ val controllerState = keyControllerState or motionControllerState
+
+ // prioritize motion controller's l2 and r2 over key
+ // (some controllers send only key, others both but key earlier than full press)
+ if(motionControllerState.l2State > 0U)
+ controllerState.l2State = motionControllerState.l2State
+ if(motionControllerState.r2State > 0U)
+ controllerState.r2State = motionControllerState.r2State
+
+ return controllerState or touchControllerState
+ }
+
+ private val keyControllerState = ControllerState() // from KeyEvents
+ private val motionControllerState = ControllerState() // from MotionEvents
+ var touchControllerState = ControllerState()
+ set(value)
+ {
+ field = value
+ controllerStateUpdated()
+ }
+
+ private val swapCrossMoon = preferences.swapCrossMoon
+
+ private fun controllerStateUpdated()
+ {
+ controllerStateChangedCallback?.let { it(controllerState) }
+ }
+
+ fun dispatchKeyEvent(event: KeyEvent): Boolean
+ {
+ Log.i("StreamSession", "key event $event")
+ if(event.action != KeyEvent.ACTION_DOWN && event.action != KeyEvent.ACTION_UP)
+ return false
+
+ when(event.keyCode)
+ {
+ KeyEvent.KEYCODE_BUTTON_L2 -> {
+ keyControllerState.l2State = if(event.action == KeyEvent.ACTION_DOWN) UByte.MAX_VALUE else 0U
+ return true
+ }
+ KeyEvent.KEYCODE_BUTTON_R2 -> {
+ keyControllerState.r2State = if(event.action == KeyEvent.ACTION_DOWN) UByte.MAX_VALUE else 0U
+ return true
+ }
+ }
+
+ val buttonMask: UInt = when(event.keyCode)
+ {
+ // dpad handled by MotionEvents
+ //KeyEvent.KEYCODE_DPAD_LEFT -> ControllerState.BUTTON_DPAD_LEFT
+ //KeyEvent.KEYCODE_DPAD_RIGHT -> ControllerState.BUTTON_DPAD_RIGHT
+ //KeyEvent.KEYCODE_DPAD_UP -> ControllerState.BUTTON_DPAD_UP
+ //KeyEvent.KEYCODE_DPAD_DOWN -> ControllerState.BUTTON_DPAD_DOWN
+ KeyEvent.KEYCODE_BUTTON_A -> if(swapCrossMoon) ControllerState.BUTTON_MOON else ControllerState.BUTTON_CROSS
+ KeyEvent.KEYCODE_BUTTON_B -> if(swapCrossMoon) ControllerState.BUTTON_CROSS else ControllerState.BUTTON_MOON
+ KeyEvent.KEYCODE_BUTTON_X -> if(swapCrossMoon) ControllerState.BUTTON_PYRAMID else ControllerState.BUTTON_BOX
+ KeyEvent.KEYCODE_BUTTON_Y -> if(swapCrossMoon) ControllerState.BUTTON_BOX else ControllerState.BUTTON_PYRAMID
+ KeyEvent.KEYCODE_BUTTON_L1 -> ControllerState.BUTTON_L1
+ KeyEvent.KEYCODE_BUTTON_R1 -> ControllerState.BUTTON_R1
+ KeyEvent.KEYCODE_BUTTON_THUMBL -> ControllerState.BUTTON_L3
+ KeyEvent.KEYCODE_BUTTON_THUMBR -> ControllerState.BUTTON_R3
+ KeyEvent.KEYCODE_BUTTON_SELECT -> ControllerState.BUTTON_SHARE
+ KeyEvent.KEYCODE_BUTTON_START -> ControllerState.BUTTON_OPTIONS
+ KeyEvent.KEYCODE_BUTTON_C -> ControllerState.BUTTON_PS
+ KeyEvent.KEYCODE_BUTTON_MODE -> ControllerState.BUTTON_PS
+ else -> return false
+ }
+
+ keyControllerState.buttons = keyControllerState.buttons.run {
+ when(event.action)
+ {
+ KeyEvent.ACTION_DOWN -> this or buttonMask
+ KeyEvent.ACTION_UP -> this and buttonMask.inv()
+ else -> this
+ }
+ }
+
+ controllerStateUpdated()
+ return true
+ }
+
+ fun onGenericMotionEvent(event: MotionEvent): Boolean
+ {
+ if(event.source and InputDevice.SOURCE_CLASS_JOYSTICK != InputDevice.SOURCE_CLASS_JOYSTICK)
+ return false
+ fun Float.signedAxis() = (this * Short.MAX_VALUE).toShort()
+ fun Float.unsignedAxis() = (this * UByte.MAX_VALUE.toFloat()).toUInt().toUByte()
+ motionControllerState.leftX = event.getAxisValue(MotionEvent.AXIS_X).signedAxis()
+ motionControllerState.leftY = event.getAxisValue(MotionEvent.AXIS_Y).signedAxis()
+ motionControllerState.rightX = event.getAxisValue(MotionEvent.AXIS_Z).signedAxis()
+ motionControllerState.rightY = event.getAxisValue(MotionEvent.AXIS_RZ).signedAxis()
+ motionControllerState.l2State = event.getAxisValue(MotionEvent.AXIS_LTRIGGER).unsignedAxis()
+ motionControllerState.r2State = event.getAxisValue(MotionEvent.AXIS_RTRIGGER).unsignedAxis()
+ motionControllerState.buttons = motionControllerState.buttons.let {
+ val dpadX = event.getAxisValue(MotionEvent.AXIS_HAT_X)
+ val dpadY = event.getAxisValue(MotionEvent.AXIS_HAT_Y)
+ val dpadButtons =
+ (if(dpadX > 0.5f) ControllerState.BUTTON_DPAD_RIGHT else 0U) or
+ (if(dpadX < -0.5f) ControllerState.BUTTON_DPAD_LEFT else 0U) or
+ (if(dpadY > 0.5f) ControllerState.BUTTON_DPAD_DOWN else 0U) or
+ (if(dpadY < -0.5f) ControllerState.BUTTON_DPAD_UP else 0U)
+ it and (ControllerState.BUTTON_DPAD_RIGHT or
+ ControllerState.BUTTON_DPAD_LEFT or
+ ControllerState.BUTTON_DPAD_DOWN or
+ ControllerState.BUTTON_DPAD_UP).inv() or
+ dpadButtons
+ }
+ //Log.i("StreamSession", "motionEvent => $motionControllerState")
+ controllerStateUpdated()
+ return true
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/com/metallic/chiaki/session/StreamSession.kt b/android/app/src/main/java/com/metallic/chiaki/session/StreamSession.kt
new file mode 100644
index 0000000..164fafd
--- /dev/null
+++ b/android/app/src/main/java/com/metallic/chiaki/session/StreamSession.kt
@@ -0,0 +1,136 @@
+/*
+ * This file is part of Chiaki.
+ *
+ * Chiaki is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chiaki is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Chiaki. If not, see .
+ */
+
+package com.metallic.chiaki.session
+
+import android.graphics.SurfaceTexture
+import android.util.Log
+import android.view.*
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import com.metallic.chiaki.lib.*
+
+sealed class StreamState
+object StreamStateIdle: StreamState()
+object StreamStateConnecting: StreamState()
+object StreamStateConnected: StreamState()
+data class StreamStateCreateError(val error: CreateError): StreamState()
+data class StreamStateQuit(val reason: QuitReason, val reasonString: String?): StreamState()
+data class StreamStateLoginPinRequest(val pinIncorrect: Boolean): StreamState()
+
+class StreamSession(val connectInfo: ConnectInfo, val input: StreamInput)
+{
+ var session: Session? = null
+ private set
+
+ private val _state = MutableLiveData(StreamStateIdle)
+ val state: LiveData get() = _state
+
+ var surfaceTexture: SurfaceTexture? = null
+
+ init
+ {
+ input.controllerStateChangedCallback = {
+ session?.setControllerState(it)
+ }
+ }
+
+ fun shutdown()
+ {
+ session?.stop()
+ session?.dispose()
+ session = null
+ _state.value = StreamStateIdle
+ //surfaceTexture?.release()
+ }
+
+ fun pause()
+ {
+ shutdown()
+ }
+
+ fun resume()
+ {
+ if(session != null)
+ return
+ try
+ {
+ val session = Session(connectInfo)
+ _state.value = StreamStateConnecting
+ session.eventCallback = this::eventCallback
+ session.start()
+ val surfaceTexture = surfaceTexture
+ if(surfaceTexture != null)
+ session.setSurface(Surface(surfaceTexture))
+ this.session = session
+ }
+ catch(e: CreateError)
+ {
+ _state.value = StreamStateCreateError(e)
+ }
+ }
+
+ private fun eventCallback(event: Event)
+ {
+ when(event)
+ {
+ is ConnectedEvent -> _state.postValue(StreamStateConnected)
+ is QuitEvent -> _state.postValue(
+ StreamStateQuit(
+ event.reason,
+ event.reasonString
+ )
+ )
+ is LoginPinRequestEvent -> _state.postValue(
+ StreamStateLoginPinRequest(
+ event.pinIncorrect
+ )
+ )
+ }
+ }
+
+ fun attachToTextureView(textureView: TextureView)
+ {
+ textureView.surfaceTextureListener = object: TextureView.SurfaceTextureListener {
+ override fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int)
+ {
+ if(surfaceTexture != null)
+ return
+ surfaceTexture = surface
+ session?.setSurface(Surface(surface))
+ }
+
+ override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean
+ {
+ // return false if we want to keep the surface texture
+ return surfaceTexture == null
+ }
+
+ override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture, width: Int, height: Int) { }
+ override fun onSurfaceTextureUpdated(surface: SurfaceTexture) {}
+ }
+
+ val surfaceTexture = surfaceTexture
+ if(surfaceTexture != null)
+ textureView.surfaceTexture = surfaceTexture
+ }
+
+ fun setLoginPin(pin: String)
+ {
+ session?.setLoginPin(pin)
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/com/metallic/chiaki/stream/StreamActivity.kt b/android/app/src/main/java/com/metallic/chiaki/stream/StreamActivity.kt
index 2ddd837..8ba742b 100644
--- a/android/app/src/main/java/com/metallic/chiaki/stream/StreamActivity.kt
+++ b/android/app/src/main/java/com/metallic/chiaki/stream/StreamActivity.kt
@@ -20,14 +20,9 @@ package com.metallic.chiaki.stream
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.app.AlertDialog
-import android.app.Dialog
-import android.content.DialogInterface
import android.graphics.Matrix
import android.os.Bundle
import android.os.Handler
-import android.speech.tts.TextToSpeech
-import android.text.method.Touch
-import android.util.Log
import android.view.KeyEvent
import android.view.MotionEvent
import android.view.View
@@ -38,13 +33,11 @@ import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.lifecycle.*
import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import com.metallic.chiaki.*
import com.metallic.chiaki.R
import com.metallic.chiaki.common.Preferences
import com.metallic.chiaki.common.ext.viewModelFactory
import com.metallic.chiaki.lib.ConnectInfo
-import com.metallic.chiaki.lib.LoginPinRequestEvent
-import com.metallic.chiaki.lib.QuitReason
+import com.metallic.chiaki.session.*
import com.metallic.chiaki.touchcontrols.TouchControlsFragment
import kotlinx.android.synthetic.main.activity_stream.*
@@ -68,18 +61,17 @@ class StreamActivity : AppCompatActivity(), View.OnSystemUiVisibilityChangeListe
{
super.onCreate(savedInstanceState)
- viewModel = ViewModelProviders.of(this, viewModelFactory { StreamViewModel(Preferences(this)) })[StreamViewModel::class.java]
- if(!viewModel.isInitialized)
+ val connectInfo = intent.getParcelableExtra(EXTRA_CONNECT_INFO)
+ if(connectInfo == null)
{
- val connectInfo = intent.getParcelableExtra(EXTRA_CONNECT_INFO)
- if(connectInfo == null)
- {
- finish()
- return
- }
- viewModel.init(connectInfo)
+ finish()
+ return
}
+ viewModel = ViewModelProviders.of(this, viewModelFactory {
+ StreamViewModel(Preferences(this), connectInfo)
+ })[StreamViewModel::class.java]
+
setContentView(R.layout.activity_stream)
window.decorView.setOnSystemUiVisibilityChangeListener(this)
@@ -104,7 +96,7 @@ class StreamActivity : AppCompatActivity(), View.OnSystemUiVisibilityChangeListe
super.onAttachFragment(fragment)
if(fragment is TouchControlsFragment)
{
- fragment.controllerStateCallback = viewModel.session::updateTouchControllerState
+ fragment.controllerStateCallback = { viewModel.input.touchControllerState = it }
fragment.onScreenControlsEnabled = viewModel.onScreenControlsEnabled
}
}
@@ -309,6 +301,6 @@ class StreamActivity : AppCompatActivity(), View.OnSystemUiVisibilityChangeListe
}
}
- override fun dispatchKeyEvent(event: KeyEvent) = viewModel.session.dispatchKeyEvent(event) || super.dispatchKeyEvent(event)
- override fun onGenericMotionEvent(event: MotionEvent) = viewModel.session.onGenericMotionEvent(event) || super.onGenericMotionEvent(event)
+ override fun dispatchKeyEvent(event: KeyEvent) = viewModel.input.dispatchKeyEvent(event) || super.dispatchKeyEvent(event)
+ override fun onGenericMotionEvent(event: MotionEvent) = viewModel.input.onGenericMotionEvent(event) || super.onGenericMotionEvent(event)
}
diff --git a/android/app/src/main/java/com/metallic/chiaki/stream/StreamViewModel.kt b/android/app/src/main/java/com/metallic/chiaki/stream/StreamViewModel.kt
index 205f31a..64bdfdc 100644
--- a/android/app/src/main/java/com/metallic/chiaki/stream/StreamViewModel.kt
+++ b/android/app/src/main/java/com/metallic/chiaki/stream/StreamViewModel.kt
@@ -20,27 +20,20 @@ package com.metallic.chiaki.stream
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
-import com.metallic.chiaki.StreamSession
+import com.metallic.chiaki.session.StreamSession
import com.metallic.chiaki.common.Preferences
import com.metallic.chiaki.lib.*
+import com.metallic.chiaki.session.StreamInput
-class StreamViewModel(val preferences: Preferences): ViewModel()
+class StreamViewModel(val preferences: Preferences, val connectInfo: ConnectInfo): ViewModel()
{
- private var connectInfo: ConnectInfo? = null
private var _session: StreamSession? = null
- val session: StreamSession get() = _session ?: throw UninitializedPropertyAccessException("StreamViewModel not initialized")
- val isInitialized get() = connectInfo != null
+ val input = StreamInput(preferences)
+ val session = StreamSession(connectInfo, input)
private var _onScreenControlsEnabled = MutableLiveData(preferences.onScreenControlsEnabled)
val onScreenControlsEnabled: LiveData get() = _onScreenControlsEnabled
- fun init(connectInfo: ConnectInfo)
- {
- if(isInitialized)
- return
- _session = StreamSession(connectInfo)
- }
-
override fun onCleared()
{
super.onCleared()
diff --git a/android/app/src/main/res/drawable/ic_gamepad.xml b/android/app/src/main/res/drawable/ic_gamepad.xml
new file mode 100644
index 0000000..00918bf
--- /dev/null
+++ b/android/app/src/main/res/drawable/ic_gamepad.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml
index 8d8c1f9..1a5b50c 100644
--- a/android/app/src/main/res/values/strings.xml
+++ b/android/app/src/main/res/values/strings.xml
@@ -58,6 +58,15 @@
Bitrate
Verbose Logging
Warning: This logs a LOT! Don\'t enable for regular use.
+ 360p
+ 540p
+ 720p
+ 1080p
+ 30
+ 60
+ Auto (%d)
+ Swap Cross/Moon and Box/Pyramid Buttons
+ Swap face buttons if default mapping is incorrect (e.g. for 8BitDo controllers)
Are you sure you want to delete the registered console %s with ID %s?
Are you sure you want to delete the console entry for %s?
Keep
@@ -68,14 +77,8 @@
discovery_enabled
on_screen_controls_enabled
log_verbose
+ swap_cross_moon
stream_resolution
- 360p
- 540p
- 720p
- 1080p
stream_fps
- 30
- 60
stream_bitrate
- Auto (%d)
diff --git a/android/app/src/main/res/xml/preferences.xml b/android/app/src/main/res/xml/preferences.xml
index 8b67a47..a46302b 100644
--- a/android/app/src/main/res/xml/preferences.xml
+++ b/android/app/src/main/res/xml/preferences.xml
@@ -14,10 +14,16 @@
app:icon="@drawable/ic_console_simple"/>
+
+