From 8e1f8ae7e9a8ffdabcdd500a1f4c83e920575aa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=97=B1=20PixelyIon?= Date: Thu, 28 May 2020 05:39:36 +0000 Subject: [PATCH] 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. --- app/build.gradle | 11 ++++--- app/src/main/java/emu/skyline/AppDialog.kt | 4 +-- app/src/main/java/emu/skyline/LogActivity.kt | 13 ++++++++ app/src/main/java/emu/skyline/MainActivity.kt | 29 +++++++++++++++++- .../main/java/emu/skyline/SettingsActivity.kt | 13 ++++++++ .../emu/skyline/preference/LicenseDialog.kt | 16 ++++++---- .../emu/skyline/views/CustomLinearLayout.kt | 12 +++++++- app/src/main/res/layout/app_dialog.xml | 5 ++-- app/src/main/res/layout/license_dialog.xml | 2 -- app/src/main/res/layout/log_activity.xml | 1 + app/src/main/res/layout/main_activity.xml | 30 +++++++++++++++++++ app/src/main/res/values/strings.xml | 1 - 12 files changed, 119 insertions(+), 18 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index a189dc1e..77600f93 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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' diff --git a/app/src/main/java/emu/skyline/AppDialog.kt b/app/src/main/java/emu/skyline/AppDialog.kt index a8e56e20..2de7ade3 100644 --- a/app/src/main/java/emu/skyline/AppDialog.kt +++ b/app/src/main/java/emu/skyline/AppDialog.kt @@ -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) diff --git a/app/src/main/java/emu/skyline/LogActivity.kt b/app/src/main/java/emu/skyline/LogActivity.kt index 48f4bdd4..4be36cdb 100644 --- a/app/src/main/java/emu/skyline/LogActivity.kt +++ b/app/src/main/java/emu/skyline/LogActivity.kt @@ -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) + } } diff --git a/app/src/main/java/emu/skyline/MainActivity.kt b/app/src/main/java/emu/skyline/MainActivity.kt index cf7dc347..b6209d8b 100644 --- a/app/src/main/java/emu/skyline/MainActivity.kt +++ b/app/src/main/java/emu/skyline/MainActivity.kt @@ -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(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) diff --git a/app/src/main/java/emu/skyline/SettingsActivity.kt b/app/src/main/java/emu/skyline/SettingsActivity.kt index ad694d67..a74f1081 100644 --- a/app/src/main/java/emu/skyline/SettingsActivity.kt +++ b/app/src/main/java/emu/skyline/SettingsActivity.kt @@ -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) + } } diff --git a/app/src/main/java/emu/skyline/preference/LicenseDialog.kt b/app/src/main/java/emu/skyline/preference/LicenseDialog.kt index bf24dca3..2c5fe66f 100644 --- a/app/src/main/java/emu/skyline/preference/LicenseDialog.kt +++ b/app/src/main/java/emu/skyline/preference/LicenseDialog.kt @@ -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 + } + } } } diff --git a/app/src/main/java/emu/skyline/views/CustomLinearLayout.kt b/app/src/main/java/emu/skyline/views/CustomLinearLayout.kt index e6ad3fd6..1f935028 100644 --- a/app/src/main/java/emu/skyline/views/CustomLinearLayout.kt +++ b/app/src/main/java/emu/skyline/views/CustomLinearLayout.kt @@ -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 = 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. diff --git a/app/src/main/res/layout/app_dialog.xml b/app/src/main/res/layout/app_dialog.xml index a7e743b5..1980090b 100644 --- a/app/src/main/res/layout/app_dialog.xml +++ b/app/src/main/res/layout/app_dialog.xml @@ -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" /> - - diff --git a/app/src/main/res/layout/log_activity.xml b/app/src/main/res/layout/log_activity.xml index 5ee4d06d..0fb3f68a 100644 --- a/app/src/main/res/layout/log_activity.xml +++ b/app/src/main/res/layout/log_activity.xml @@ -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" /> diff --git a/app/src/main/res/layout/main_activity.xml b/app/src/main/res/layout/main_activity.xml index 280adf62..39f86e1c 100644 --- a/app/src/main/res/layout/main_activity.xml +++ b/app/src/main/res/layout/main_activity.xml @@ -23,6 +23,36 @@ android:layout_gravity="bottom|end" android:orientation="vertical"> + + + + + + + Share The log file was not found - An I/O error has occurred The logs are being uploaded The logs have been cleared