From 5840d607243b04bd403041ae9047a569f3c5252e Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Mon, 13 Mar 2023 23:15:52 -0400 Subject: android: Use Material 3 components --- .../main/java/org/yuzu/yuzu_emu/NativeLibrary.java | 12 ++-- .../yuzu/yuzu_emu/activities/EmulationActivity.kt | 3 + .../java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt | 8 +-- .../yuzu/yuzu_emu/applets/SoftwareKeyboard.java | 6 +- .../features/settings/ui/SettingsActivity.kt | 22 +++++-- .../features/settings/ui/SettingsAdapter.kt | 3 +- .../ui/viewholder/CheckBoxSettingViewHolder.kt | 15 +++-- .../java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt | 2 + .../java/org/yuzu/yuzu_emu/utils/InsetsHelper.kt | 18 ++++++ .../java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt | 67 ++++++++++++++++++++++ .../app/src/main/res/drawable/gamelist_divider.xml | 11 ---- .../app/src/main/res/layout/activity_main.xml | 20 +++---- .../app/src/main/res/layout/activity_settings.xml | 28 ++++++++- src/android/app/src/main/res/layout/card_game.xml | 23 ++++---- .../app/src/main/res/layout/dialog_checkbox.xml | 16 ------ .../src/main/res/layout/dialog_progress_bar.xml | 25 +++----- .../app/src/main/res/layout/fragment_emulation.xml | 39 ++++++------- .../app/src/main/res/layout/fragment_settings.xml | 7 ++- .../app/src/main/res/layout/list_item_setting.xml | 24 ++++---- .../main/res/layout/list_item_setting_checkbox.xml | 52 ----------------- .../main/res/layout/list_item_setting_switch.xml | 48 ++++++++++++++++ .../main/res/layout/list_item_settings_header.xml | 11 ++-- .../src/main/res/layout/premium_item_setting.xml | 25 ++++---- .../app/src/main/res/menu/menu_game_grid.xml | 11 +++- 24 files changed, 295 insertions(+), 201 deletions(-) create mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InsetsHelper.kt create mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt delete mode 100644 src/android/app/src/main/res/drawable/gamelist_divider.xml delete mode 100644 src/android/app/src/main/res/layout/dialog_checkbox.xml delete mode 100644 src/android/app/src/main/res/layout/list_item_setting_checkbox.xml create mode 100644 src/android/app/src/main/res/layout/list_item_setting_switch.xml diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.java index 067e9938d..c7c616a50 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.java +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.java @@ -24,6 +24,8 @@ import androidx.appcompat.app.AlertDialog; import androidx.core.content.ContextCompat; import androidx.fragment.app.DialogFragment; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + import org.yuzu.yuzu_emu.activities.EmulationActivity; import org.yuzu.yuzu_emu.utils.DocumentsTree; import org.yuzu.yuzu_emu.utils.EmulationMenuSettings; @@ -268,7 +270,7 @@ public final class NativeLibrary { final String title = Objects.requireNonNull(Objects.requireNonNull(getArguments()).getString("title")); final String message = Objects.requireNonNull(Objects.requireNonNull(getArguments()).getString("message")); - return new AlertDialog.Builder(emulationActivity) + return new MaterialAlertDialogBuilder(emulationActivity) .setTitle(title) .setMessage(message) .setPositiveButton(R.string.continue_button, (dialog, which) -> { @@ -369,7 +371,7 @@ public final class NativeLibrary { } else { // Create object used for waiting. final Object lock = new Object(); - AlertDialog.Builder builder = new AlertDialog.Builder(emulationActivity) + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(emulationActivity) .setTitle(caption) .setMessage(text); @@ -451,7 +453,7 @@ public final class NativeLibrary { return alertPromptResult; } - public static AlertDialog.Builder displayAlertPromptImpl(String caption, String text, int buttonConfig) { + public static MaterialAlertDialogBuilder displayAlertPromptImpl(String caption, String text, int buttonConfig) { final EmulationActivity emulationActivity = sEmulationActivity.get(); alertPromptResult = ""; alertPromptButton = 0; @@ -468,7 +470,7 @@ public final class NativeLibrary { FrameLayout container = new FrameLayout(emulationActivity); container.addView(alertPromptEditText); - AlertDialog.Builder builder = new AlertDialog.Builder(emulationActivity) + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(emulationActivity) .setTitle(caption) .setView(container) .setPositiveButton(android.R.string.ok, (dialogInterface, i) -> @@ -536,7 +538,7 @@ public final class NativeLibrary { return; } - AlertDialog.Builder builder = new AlertDialog.Builder(emulationActivity) + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(emulationActivity) .setTitle(captionId) .setMessage(Html.fromHtml(emulationActivity.getString(descriptionId), Html.FROM_HTML_MODE_LEGACY)) .setPositiveButton(android.R.string.ok, (dialog, whichButton) -> emulationActivity.finish()) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt index 9b567cc4d..cbec4836b 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt @@ -29,6 +29,7 @@ import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.fragments.EmulationFragment import org.yuzu.yuzu_emu.fragments.MenuFragment import org.yuzu.yuzu_emu.utils.ControllerMappingHelper +import org.yuzu.yuzu_emu.utils.ThemeHelper import kotlin.math.roundToInt open class EmulationActivity : AppCompatActivity() { @@ -50,6 +51,8 @@ open class EmulationActivity : AppCompatActivity() { } override fun onCreate(savedInstanceState: Bundle?) { + ThemeHelper.setTheme(this) + super.onCreate(savedInstanceState) if (savedInstanceState == null) { // Get params we were passed diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt index aa50cecfa..af4ec63f2 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt @@ -8,9 +8,9 @@ import android.database.DataSetObserver import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.core.content.ContextCompat import androidx.fragment.app.FragmentActivity import androidx.recyclerview.widget.RecyclerView +import com.google.android.material.color.MaterialColors import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.activities.EmulationActivity.Companion.launch import org.yuzu.yuzu_emu.model.GameDatabase @@ -68,11 +68,11 @@ class GameAdapter : RecyclerView.Adapter(), View.OnClickListener holder.regions = cursor!!.getString(GameDatabase.GAME_COLUMN_REGIONS) holder.company = cursor!!.getString(GameDatabase.GAME_COLUMN_CAPTION) val backgroundColorId = - if (isValidGame(holder.path!!)) R.color.view_background else R.color.view_disabled + if (isValidGame(holder.path!!)) R.attr.colorSurface else R.attr.colorErrorContainer val itemView = holder.itemView itemView.setBackgroundColor( - ContextCompat.getColor( - itemView.context, + MaterialColors.getColor( + itemView, backgroundColorId ) ) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/applets/SoftwareKeyboard.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/applets/SoftwareKeyboard.java index 4aeb41472..894da8801 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/applets/SoftwareKeyboard.java +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/applets/SoftwareKeyboard.java @@ -19,6 +19,8 @@ import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.DialogFragment; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + import org.yuzu.yuzu_emu.YuzuApplication; import org.yuzu.yuzu_emu.NativeLibrary; import org.yuzu.yuzu_emu.R; @@ -124,7 +126,7 @@ public final class SoftwareKeyboard { FrameLayout container = new FrameLayout(emulationActivity); container.addView(editText); - AlertDialog.Builder builder = new AlertDialog.Builder(emulationActivity) + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(emulationActivity) .setTitle(R.string.software_keyboard) .setView(container); setCancelable(false); @@ -227,7 +229,7 @@ public final class SoftwareKeyboard { break; } - new AlertDialog.Builder(emulationActivity) + new MaterialAlertDialogBuilder(emulationActivity) .setTitle(R.string.software_keyboard) .setMessage(message) .setPositiveButton(android.R.string.ok, null) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt index e0f067448..1705a72e8 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt @@ -3,15 +3,17 @@ package org.yuzu.yuzu_emu.features.settings.ui -import android.app.ProgressDialog import android.content.Context import android.content.Intent import android.content.IntentFilter import android.os.Bundle import android.view.Menu import android.widget.Toast +import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.localbroadcastmanager.content.LocalBroadcastManager +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.google.android.material.progressindicator.LinearProgressIndicator import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.features.settings.model.Settings @@ -19,12 +21,15 @@ import org.yuzu.yuzu_emu.features.settings.ui.SettingsFragment.Companion.newInst import org.yuzu.yuzu_emu.utils.DirectoryInitialization import org.yuzu.yuzu_emu.utils.DirectoryStateReceiver import org.yuzu.yuzu_emu.utils.EmulationMenuSettings +import org.yuzu.yuzu_emu.utils.ThemeHelper class SettingsActivity : AppCompatActivity(), SettingsActivityView { private val presenter = SettingsActivityPresenter(this) - private var dialog: ProgressDialog? = null + private var dialog: AlertDialog? = null override fun onCreate(savedInstanceState: Bundle?) { + ThemeHelper.setTheme(this) + super.onCreate(savedInstanceState) setContentView(R.layout.activity_settings) val launcher = intent @@ -33,6 +38,7 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView { presenter.onCreate(savedInstanceState, menuTag!!, gameID!!) // Show "Back" button in the action bar for navigation + setSupportActionBar(findViewById(R.id.toolbar_settings)) supportActionBar!!.setDisplayHomeAsUpEnabled(true) } @@ -123,9 +129,15 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView { override fun showLoading() { if (dialog == null) { - dialog = ProgressDialog(this) - dialog!!.setMessage(getString(R.string.load_settings)) - dialog!!.isIndeterminate = true + val root = layoutInflater.inflate(R.layout.dialog_progress_bar, null) + val progressBar = root.findViewById(R.id.progress_bar) + progressBar.isIndeterminate = true + + dialog = MaterialAlertDialogBuilder(this) + .setTitle(R.string.load_settings) + .setView(root) + .setCancelable(false) + .create() } dialog!!.show() } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt index 03e0adf56..4eac386cc 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt @@ -51,7 +51,7 @@ class SettingsAdapter( HeaderViewHolder(view, this) } SettingsItem.TYPE_CHECKBOX -> { - view = inflater.inflate(R.layout.list_item_setting_checkbox, parent, false) + view = inflater.inflate(R.layout.list_item_setting_switch, parent, false) CheckBoxSettingViewHolder(view, this) } SettingsItem.TYPE_SINGLE_CHOICE, SettingsItem.TYPE_STRING_SINGLE_CHOICE -> { @@ -105,7 +105,6 @@ class SettingsAdapter( fun onBooleanClick(item: CheckBoxSetting, position: Int, checked: Boolean) { val setting = item.setChecked(checked) - notifyItemChanged(position) if (setting != null) { fragmentView.putSetting(setting) } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/CheckBoxSettingViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/CheckBoxSettingViewHolder.kt index 35d0586db..3296aed68 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/CheckBoxSettingViewHolder.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/CheckBoxSettingViewHolder.kt @@ -4,8 +4,9 @@ package org.yuzu.yuzu_emu.features.settings.ui.viewholder import android.view.View -import android.widget.CheckBox +import android.widget.CompoundButton import android.widget.TextView +import com.google.android.material.materialswitch.MaterialSwitch import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.features.settings.model.view.CheckBoxSetting import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem @@ -16,12 +17,12 @@ class CheckBoxSettingViewHolder(itemView: View, adapter: SettingsAdapter) : private lateinit var item: CheckBoxSetting private lateinit var textSettingName: TextView private lateinit var textSettingDescription: TextView - private lateinit var checkbox: CheckBox + private lateinit var switch: MaterialSwitch override fun findViews(root: View) { textSettingName = root.findViewById(R.id.text_setting_name) textSettingDescription = root.findViewById(R.id.text_setting_description) - checkbox = root.findViewById(R.id.checkbox) + switch = root.findViewById(R.id.switch_widget) } override fun bind(item: SettingsItem) { @@ -34,11 +35,13 @@ class CheckBoxSettingViewHolder(itemView: View, adapter: SettingsAdapter) : textSettingDescription.text = "" textSettingDescription.visibility = View.GONE } - checkbox.isChecked = this.item.isChecked + switch.isChecked = this.item.isChecked + switch.setOnCheckedChangeListener { _: CompoundButton, _: Boolean -> + adapter.onBooleanClick(item, bindingAdapterPosition, switch.isChecked) + } } override fun onClick(clicked: View) { - checkbox.toggle() - adapter.onBooleanClick(item, bindingAdapterPosition, checkbox.isChecked) + switch.toggle() } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt index af0d9f4ff..266403885 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt @@ -27,6 +27,8 @@ class MainActivity : AppCompatActivity(), MainView { private val presenter = MainPresenter(this) override fun onCreate(savedInstanceState: Bundle?) { + ThemeHelper.setTheme(this) + super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) findViews() diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InsetsHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InsetsHelper.kt new file mode 100644 index 000000000..3f9acc5b0 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InsetsHelper.kt @@ -0,0 +1,18 @@ +package org.yuzu.yuzu_emu.utils + +import android.content.Context + +object InsetsHelper { + const val THREE_BUTTON_NAVIGATION = 0 + const val TWO_BUTTON_NAVIGATION = 1 + const val GESTURE_NAVIGATION = 2 + + fun getSystemGestureType(context: Context): Int { + val resources = context.resources + val resourceId = + resources.getIdentifier("config_navBarInteractionMode", "integer", "android") + return if (resourceId != 0) { + resources.getInteger(resourceId) + } else 0 + } +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt new file mode 100644 index 000000000..ce6396e91 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt @@ -0,0 +1,67 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.utils + +import android.app.Activity +import android.content.res.Configuration +import android.graphics.Color +import androidx.annotation.ColorInt +import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContextCompat +import androidx.core.view.WindowCompat +import com.google.android.material.color.MaterialColors +import org.yuzu.yuzu_emu.R +import kotlin.math.roundToInt + +object ThemeHelper { + private const val NAV_BAR_ALPHA = 0.9f + + @JvmStatic + fun setTheme(activity: AppCompatActivity) { + val windowController = WindowCompat.getInsetsController( + activity.window, + activity.window.decorView + ) + val isLightMode = + (activity.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_NO + windowController.isAppearanceLightStatusBars = isLightMode + windowController.isAppearanceLightNavigationBars = isLightMode + + activity.window.statusBarColor = ContextCompat.getColor(activity, android.R.color.transparent) + + val navigationBarColor = + MaterialColors.getColor(activity.window.decorView, R.attr.colorSurface) + setNavigationBarColor(activity, navigationBarColor) + } + + @JvmStatic + fun setNavigationBarColor(activity: Activity, @ColorInt color: Int) { + val gestureType = InsetsHelper.getSystemGestureType(activity.applicationContext) + val orientation = activity.resources.configuration.orientation + + if ((gestureType == InsetsHelper.THREE_BUTTON_NAVIGATION || + gestureType == InsetsHelper.TWO_BUTTON_NAVIGATION) && + orientation == Configuration.ORIENTATION_LANDSCAPE + ) { + activity.window.navigationBarColor = color + } else if (gestureType == InsetsHelper.THREE_BUTTON_NAVIGATION || + gestureType == InsetsHelper.TWO_BUTTON_NAVIGATION + ) { + activity.window.navigationBarColor = getColorWithOpacity(color, NAV_BAR_ALPHA) + } else { + activity.window.navigationBarColor = ContextCompat.getColor( + activity.applicationContext, + android.R.color.transparent + ) + } + } + + @ColorInt + private fun getColorWithOpacity(@ColorInt color: Int, alphaFactor: Float): Int { + return Color.argb( + (alphaFactor * Color.alpha(color)).roundToInt(), Color.red(color), + Color.green(color), Color.blue(color) + ) + } +} diff --git a/src/android/app/src/main/res/drawable/gamelist_divider.xml b/src/android/app/src/main/res/drawable/gamelist_divider.xml deleted file mode 100644 index 7da9dccce..000000000 --- a/src/android/app/src/main/res/drawable/gamelist_divider.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - diff --git a/src/android/app/src/main/res/layout/activity_main.xml b/src/android/app/src/main/res/layout/activity_main.xml index cea0922a7..34abf955d 100644 --- a/src/android/app/src/main/res/layout/activity_main.xml +++ b/src/android/app/src/main/res/layout/activity_main.xml @@ -5,23 +5,23 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - - + android:layout_height="wrap_content" + app:liftOnScrollTargetViewId="@id/grid_games"> + android:layout_height="?attr/actionBarSize" /> + + diff --git a/src/android/app/src/main/res/layout/activity_settings.xml b/src/android/app/src/main/res/layout/activity_settings.xml index 11b91c45f..449deab08 100644 --- a/src/android/app/src/main/res/layout/activity_settings.xml +++ b/src/android/app/src/main/res/layout/activity_settings.xml @@ -1,5 +1,27 @@ - + android:layout_height="match_parent"> + + + + + + + + + + diff --git a/src/android/app/src/main/res/layout/card_game.xml b/src/android/app/src/main/res/layout/card_game.xml index a0d453719..a107fec0f 100644 --- a/src/android/app/src/main/res/layout/card_game.xml +++ b/src/android/app/src/main/res/layout/card_game.xml @@ -5,7 +5,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="?attr/selectableItemBackground" + android:foreground="?attr/selectableItemBackground" android:clickable="true" android:clipToPadding="false" android:focusable="true" @@ -15,7 +15,8 @@ android:paddingBottom="8dp" android:transitionName="card_game"> - + tools:text="Super Mario Odyssey" /> - + + tools:text="Super Mario Odyssey" /> - - - - diff --git a/src/android/app/src/main/res/layout/dialog_progress_bar.xml b/src/android/app/src/main/res/layout/dialog_progress_bar.xml index a81157a29..1dbfd4f7b 100644 --- a/src/android/app/src/main/res/layout/dialog_progress_bar.xml +++ b/src/android/app/src/main/res/layout/dialog_progress_bar.xml @@ -1,26 +1,15 @@ - - + android:layout_margin="24dp" + app:trackCornerRadius="4dp" /> - - \ No newline at end of file + diff --git a/src/android/app/src/main/res/layout/fragment_emulation.xml b/src/android/app/src/main/res/layout/fragment_emulation.xml index 729a986db..c2eb097f9 100644 --- a/src/android/app/src/main/res/layout/fragment_emulation.xml +++ b/src/android/app/src/main/res/layout/fragment_emulation.xml @@ -6,6 +6,17 @@ tools:context="org.yuzu.yuzu_emu.fragments.EmulationFragment"> +