Make UI fully usable using a controller

This commit focuses on making the UI completely usable using a controller so that a user won't have to switch between their device's touch screen and a controller constantly.
This commit is contained in:
◱ PixelyIon 2020-05-28 05:39:36 +00:00 committed by ◱ PixelyIon
parent 102f26d08e
commit 8e1f8ae7e9
12 changed files with 119 additions and 18 deletions

View File

@ -15,6 +15,9 @@ android {
abiFilters "arm64-v8a"
}
}
lintOptions {
disable 'IconLocation'
}
kotlinOptions {
jvmTarget = "1.8"
}
@ -52,11 +55,11 @@ android {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.preference:preference:1.1.0'
implementation 'com.google.android.material:material:1.2.0-alpha05'
implementation "androidx.core:core-ktx:1.2.0"
implementation 'androidx.preference:preference:1.1.1'
implementation 'com.google.android.material:material:1.3.0-alpha02'
implementation "androidx.core:core-ktx:1.3.1"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.documentfile:documentfile:1.0.1'
implementation 'info.debatty:java-string-similarity:1.2.1'

View File

@ -46,7 +46,7 @@ class AppDialog(val item : AppItem) : BottomSheetDialogFragment() {
behavior.state = BottomSheetBehavior.STATE_EXPANDED
dialog?.setOnKeyListener { _, keyCode, event ->
if (keyCode == KeyEvent.KEYCODE_BUTTON_B && event.action == KeyEvent.ACTION_DOWN) {
if (keyCode == KeyEvent.KEYCODE_BUTTON_B && event.action == KeyEvent.ACTION_UP) {
dialog?.onBackPressed()
return@setOnKeyListener true
}
@ -79,7 +79,7 @@ class AppDialog(val item : AppItem) : BottomSheetDialogFragment() {
game_pin.setOnClickListener {
val info = ShortcutInfo.Builder(context, item.title)
info.setShortLabel(item.meta.name)
info.setActivity(ComponentName(requireActivity(), EmulationActivity::class.java))
info.setActivity(ComponentName(requireContext(), EmulationActivity::class.java))
info.setIcon(Icon.createWithAdaptiveBitmap(item.icon ?: missingIcon))
val intent = Intent(context, EmulationActivity::class.java)

View File

@ -8,6 +8,7 @@ package emu.skyline
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.view.KeyEvent
import android.view.Menu
import android.view.MenuItem
import android.widget.Toast
@ -176,4 +177,16 @@ class LogActivity : AppCompatActivity() {
shareThread.start()
}
/**
* This handles on calling [onBackPressed] when [KeyEvent.KEYCODE_BUTTON_B] is lifted
*/
override fun onKeyUp(keyCode : Int, event : KeyEvent?) : Boolean {
if (keyCode == KeyEvent.KEYCODE_BUTTON_B) {
onBackPressed()
return true
}
return super.onKeyUp(keyCode, event)
}
}

View File

@ -5,6 +5,7 @@
package emu.skyline
import android.animation.ObjectAnimator
import android.content.Intent
import android.content.SharedPreferences
import android.content.pm.ActivityInfo
@ -17,6 +18,7 @@ import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.widget.SearchView
import androidx.core.animation.doOnEnd
import androidx.documentfile.provider.DocumentFile
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.DividerItemDecoration
@ -169,6 +171,8 @@ class MainActivity : AppCompatActivity(), View.OnClickListener {
else -> AppCompatDelegate.MODE_NIGHT_UNSPECIFIED
})
refresh_fab.setOnClickListener(this)
settings_fab.setOnClickListener(this)
open_fab.setOnClickListener(this)
log_fab.setOnClickListener(this)
@ -187,6 +191,24 @@ class MainActivity : AppCompatActivity(), View.OnClickListener {
super.onScrolled(recyclerView, dx, dy)
}
})
val controllerFabX = controller_fabs.translationX
window.decorView.findViewById<View>(android.R.id.content).viewTreeObserver.addOnTouchModeChangeListener {
if (!it) {
toolbar_layout.setExpanded(false)
controller_fabs.visibility = View.VISIBLE
ObjectAnimator.ofFloat(controller_fabs, "translationX", 0f).apply {
duration = 250
start()
}
} else {
ObjectAnimator.ofFloat(controller_fabs, "translationX", controllerFabX).apply {
duration = 250
start()
}.doOnEnd { controller_fabs.visibility = View.GONE }
}
}
}
private fun setupAppList() {
@ -243,11 +265,16 @@ class MainActivity : AppCompatActivity(), View.OnClickListener {
}
/**
* This handles on-click interaction with [R.id.log_fab], [R.id.open_fab]
* This handles on-click interaction with [R.id.refresh_fab], [R.id.settings_fab], [R.id.log_fab], [R.id.open_fab]
*/
override fun onClick(view : View) {
when (view.id) {
R.id.refresh_fab -> refreshAdapter(false)
R.id.settings_fab -> startActivityForResult(Intent(this, SettingsActivity::class.java), 3)
R.id.log_fab -> startActivity(Intent(this, LogActivity::class.java))
R.id.open_fab -> {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)

View File

@ -7,6 +7,7 @@ package emu.skyline
import android.content.Intent
import android.os.Bundle
import android.view.KeyEvent
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.PreferenceFragmentCompat
import kotlinx.android.synthetic.main.titlebar.*
@ -61,4 +62,16 @@ class SettingsActivity : AppCompatActivity() {
setPreferencesFromResource(R.xml.preferences, rootKey)
}
}
/**
* This handles on calling [onBackPressed] when [KeyEvent.KEYCODE_BUTTON_B] is lifted
*/
override fun onKeyUp(keyCode : Int, event : KeyEvent?) : Boolean {
if (keyCode == KeyEvent.KEYCODE_BUTTON_B) {
onBackPressed()
return true
}
return super.onKeyUp(keyCode, event)
}
}

View File

@ -7,10 +7,7 @@ package emu.skyline.preference
import android.graphics.Rect
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.Window
import android.view.*
import androidx.fragment.app.DialogFragment
import emu.skyline.R
import kotlinx.android.synthetic.main.license_dialog.*
@ -26,7 +23,7 @@ class LicenseDialog : DialogFragment() {
val layout = layoutInflater.inflate(R.layout.license_dialog, container)
val displayRectangle = Rect()
val window : Window = activity!!.window
val window : Window = requireActivity().window
window.decorView.getWindowVisibleDisplayFrame(displayRectangle)
layout.minimumWidth = ((displayRectangle.width() * 0.9f).toInt())
@ -43,5 +40,14 @@ class LicenseDialog : DialogFragment() {
license_url.text = arguments?.getString("libraryUrl")!!
license_content.text = context?.getString(arguments?.getInt("libraryLicense")!!)!!
dialog?.setOnKeyListener { _, keyCode, event ->
if (keyCode == KeyEvent.KEYCODE_BUTTON_B && event.action == KeyEvent.ACTION_UP) {
dialog?.onBackPressed()
true
} else {
false
}
}
}
}

View File

@ -6,7 +6,9 @@
package emu.skyline.views
import android.content.Context
import android.graphics.Rect
import android.util.AttributeSet
import android.util.Log
import android.view.View
import android.widget.LinearLayout
import androidx.coordinatorlayout.widget.CoordinatorLayout
@ -16,13 +18,21 @@ import com.google.android.material.snackbar.Snackbar.SnackbarLayout
* Custom linear layout with support for [CoordinatorLayout] to move children, when [com.google.android.material.snackbar.Snackbar] shows up.
*/
class CustomLinearLayout : LinearLayout, CoordinatorLayout.AttachedBehavior {
constructor(context : Context) : this(context, null)
constructor(context : Context, attrs : AttributeSet?) : this(context, attrs, 0)
constructor(context : Context, attrs : AttributeSet?, defStyleAttr : Int) : super(context, attrs, defStyleAttr)
override fun getBehavior() : CoordinatorLayout.Behavior<CustomLinearLayout> = MoveUpwardBehavior()
override fun requestFocus(direction: Int, previouslyFocusedRect: Rect): Boolean = getChildAt(if (direction == View.FOCUS_UP) childCount - 1 else 0 )?.requestFocus() ?: false
/*
override fun onRequestFocusInDescendants(dir : Int, rect : Rect?) : Boolean {
Log.i("DESC", "$dir and $rect")
return getChildAt(0).requestFocus()
}
*/
/**
* Defines behaviour when [com.google.android.material.snackbar.Snackbar] is shown.
* Simply sets an offset to y translation to move children out of the way.

View File

@ -25,14 +25,15 @@
android:id="@+id/game_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.AppCompat.Display1"
android:textAppearance="?android:attr/textAppearanceListItem"
android:textSize="18sp" />
<TextView
android:id="@+id/game_subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.AppCompat.Display2"
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:textColor="@android:color/tertiary_text_light"
android:textSize="14sp" />
<LinearLayout

View File

@ -1,6 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">

View File

@ -13,6 +13,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fastScrollEnabled="true"
android:focusedByDefault="true"
android:transcriptMode="normal"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -23,6 +23,36 @@
android:layout_gravity="bottom|end"
android:orientation="vertical">
<emu.skyline.views.CustomLinearLayout
android:id="@+id/controller_fabs"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="true"
android:orientation="vertical"
android:translationX="72dp"
android:visibility="gone">
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/refresh_fab"
style="@style/Widget.MaterialComponents.FloatingActionButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginBottom="8dp"
app:maxImageSize="26dp"
app:srcCompat="@drawable/ic_refresh" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/settings_fab"
style="@style/Widget.MaterialComponents.FloatingActionButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginBottom="8dp"
app:maxImageSize="26dp"
app:srcCompat="@drawable/ic_settings" />
</emu.skyline.views.CustomLinearLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/open_fab"
style="@style/Widget.MaterialComponents.FloatingActionButton"

View File

@ -19,7 +19,6 @@
<string name="share">Share</string>
<!-- Logger -->
<string name="file_missing">The log file was not found</string>
<string name="io_error">An I/O error has occurred</string>
<string name="upload_logs">The logs are being uploaded</string>
<string name="cleared">The logs have been cleared</string>
<!-- Settings -->