Add On-Screen Controls Toggle to Android

This commit is contained in:
Florian Märkl 2019-10-29 17:53:48 +01:00
commit 088414798e
No known key found for this signature in database
GPG key ID: 125BC8A5A6A1E857
9 changed files with 137 additions and 17 deletions

View file

@ -69,6 +69,11 @@ class Preferences(context: Context)
get() = sharedPreferences.getBoolean(discoveryEnabledKey, true)
set(value) { sharedPreferences.edit().putBoolean(discoveryEnabledKey, value).apply() }
val onScreenControlsEnabledKey get() = resources.getString(R.string.preferences_on_screen_controls_enabled_key)
var onScreenControlsEnabled
get() = sharedPreferences.getBoolean(onScreenControlsEnabledKey, true)
set(value) { sharedPreferences.edit().putBoolean(onScreenControlsEnabledKey, value).apply() }
val logVerboseKey get() = resources.getString(R.string.preferences_log_verbose_key)
var logVerbose
get() = sharedPreferences.getBoolean(logVerboseKey, false)

View file

@ -17,22 +17,30 @@
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.View
import android.widget.EditText
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isGone
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
@ -44,20 +52,22 @@ private object StreamQuitDialog: DialogContents()
private object CreateErrorDialog: DialogContents()
private object PinRequestDialog: DialogContents()
class StreamActivity : AppCompatActivity()
class StreamActivity : AppCompatActivity(), View.OnSystemUiVisibilityChangeListener
{
companion object
{
const val EXTRA_CONNECT_INFO = "connect_info"
private const val HIDE_UI_TIMEOUT_MS = 2000L
}
private lateinit var viewModel: StreamViewModel
private val uiVisibilityHandler = Handler()
override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)
viewModel = ViewModelProviders.of(this)[StreamViewModel::class.java]
viewModel = ViewModelProviders.of(this, viewModelFactory { StreamViewModel(Preferences(this)) })[StreamViewModel::class.java]
if(!viewModel.isInitialized)
{
val connectInfo = intent.getParcelableExtra<ConnectInfo>(EXTRA_CONNECT_INFO)
@ -70,11 +80,19 @@ class StreamActivity : AppCompatActivity()
}
setContentView(R.layout.activity_stream)
window.decorView.setOnSystemUiVisibilityChangeListener(this)
viewModel.onScreenControlsEnabled.observe(this, Observer {
if(onScreenControlsSwitch.isChecked != it)
onScreenControlsSwitch.isChecked = it
})
onScreenControlsSwitch.setOnCheckedChangeListener { _, isChecked ->
viewModel.setOnScreenControlsEnabled(isChecked)
showOverlay()
}
viewModel.session.attachToTextureView(textureView)
viewModel.session.state.observe(this, Observer { this.stateChanged(it) })
textureView.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
adjustTextureViewAspect()
}
@ -83,7 +101,11 @@ class StreamActivity : AppCompatActivity()
override fun onAttachFragment(fragment: Fragment)
{
super.onAttachFragment(fragment)
(fragment as? TouchControlsFragment)?.controllerStateCallback = viewModel.session::updateTouchControllerState
if(fragment is TouchControlsFragment)
{
fragment.controllerStateCallback = viewModel.session::updateTouchControllerState
fragment.onScreenControlsEnabled = viewModel.onScreenControlsEnabled
}
}
override fun onResume()
@ -105,6 +127,45 @@ class StreamActivity : AppCompatActivity()
viewModel.session.resume()
}
private val hideSystemUIRunnable = Runnable { hideSystemUI() }
override fun onSystemUiVisibilityChange(visibility: Int)
{
if(visibility and View.SYSTEM_UI_FLAG_FULLSCREEN == 0)
showOverlay()
else
hideOverlay()
}
private fun showOverlay()
{
overlay.isVisible = true
overlay.animate()
.alpha(1.0f)
.setListener(object: AnimatorListenerAdapter()
{
override fun onAnimationEnd(animation: Animator?)
{
overlay.alpha = 1.0f
}
})
uiVisibilityHandler.removeCallbacks(hideSystemUIRunnable)
uiVisibilityHandler.postDelayed(hideSystemUIRunnable, HIDE_UI_TIMEOUT_MS)
}
private fun hideOverlay()
{
overlay.animate()
.alpha(0.0f)
.setListener(object: AnimatorListenerAdapter()
{
override fun onAnimationEnd(animation: Animator?)
{
overlay.isGone = true
}
})
}
override fun onWindowFocusChanged(hasFocus: Boolean)
{
super.onWindowFocusChanged(hasFocus)
@ -114,7 +175,7 @@ class StreamActivity : AppCompatActivity()
private fun hideSystemUI()
{
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

View file

@ -17,17 +17,23 @@
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.common.Preferences
import com.metallic.chiaki.lib.*
class StreamViewModel: ViewModel()
class StreamViewModel(val preferences: Preferences): 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
private var _onScreenControlsEnabled = MutableLiveData<Boolean>(preferences.onScreenControlsEnabled)
val onScreenControlsEnabled: LiveData<Boolean> get() = _onScreenControlsEnabled
fun init(connectInfo: ConnectInfo)
{
if(isInitialized)
@ -40,4 +46,10 @@ class StreamViewModel: ViewModel()
super.onCleared()
_session?.shutdown()
}
fun setOnScreenControlsEnabled(enabled: Boolean)
{
preferences.onScreenControlsEnabled = enabled
_onScreenControlsEnabled.value = enabled
}
}

View file

@ -23,13 +23,15 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import com.metallic.chiaki.R
import com.metallic.chiaki.lib.ControllerState
import kotlinx.android.synthetic.main.fragment_controls.*
class TouchControlsFragment : Fragment()
{
var controllerState = ControllerState()
private var controllerState = ControllerState()
private set(value)
{
val diff = field != value
@ -39,6 +41,7 @@ class TouchControlsFragment : Fragment()
}
var controllerStateCallback: ((ControllerState) -> Unit)? = null
var onScreenControlsEnabled: LiveData<Boolean>? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View
= inflater.inflate(R.layout.fragment_controls, container, false)
@ -74,6 +77,10 @@ class TouchControlsFragment : Fragment()
rightX = quantizeStick(it.x)
rightY = quantizeStick(it.y)
}}
onScreenControlsEnabled?.observe(this, Observer {
view.visibility = if(it) View.VISIBLE else View.GONE
})
}
private fun dpadStateChanged(direction: DPadView.Direction?)

View file

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".stream.StreamActivity">
<TextureView
@ -15,15 +15,45 @@
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
android:layout_gravity="center"/>
<fragment
android:id="@+id/controlsFragment"
android:name="com.metallic.chiaki.touchcontrols.TouchControlsFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
android:layout_height="match_parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/overlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="bottom"
tools:paddingRight="48dp"
tools:paddingTop="25dp"
android:fitsSystemWindows="true">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
android:background="@color/stream_overlay_background"
android:padding="8dp"
android:clickable="true"
android:focusable="false">
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/onScreenControlsSwitch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:text="On-Screen Controls"
app:switchPadding="8dp"
android:layout_marginLeft="8dp"
android:textColor="@color/stream_text"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"

View file

@ -6,6 +6,7 @@
<color name="stream_text">@android:color/white</color>
<color name="stream_background">@android:color/black</color>
<color name="stream_overlay_background">#77000000</color>
<color name="floating_action_button_speed_dial_background">#fafafa</color>
<color name="floating_action_button_speed_dial_tint">#333333</color>

View file

@ -66,6 +66,7 @@
<!-- Don't localize these -->
<string name="preferences_discovery_enabled_key">discovery_enabled</string>
<string name="preferences_on_screen_controls_enabled_key">on_screen_controls_enabled</string>
<string name="preferences_log_verbose_key">log_verbose</string>
<string name="preferences_resolution_key">stream_resolution</string>
<string name="preferences_resolution_title_360p">360p</string>

View file

@ -109,5 +109,7 @@
<item name="colorAccent">@color/accent</item>
<item name="materialAlertDialogTheme">@style/AppTheme.AlertDialog</item>
<item name="android:windowBackground">@color/stream_background</item>
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">true</item>
</style>
</resources>