mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2026-09-18 02:40:11 +00:00
Merge pull request #14742 from Simonx22/android/settings-search-next
Android: Add global settings search
This commit is contained in:
+1
@@ -86,5 +86,6 @@ abstract class SettingsItem {
|
|||||||
const val TYPE_STRING = 12
|
const val TYPE_STRING = 12
|
||||||
const val TYPE_HYPERLINK_HEADER = 13
|
const val TYPE_HYPERLINK_HEADER = 13
|
||||||
const val TYPE_DATETIME_CHOICE = 14
|
const val TYPE_DATETIME_CHOICE = 14
|
||||||
|
const val TYPE_SEARCH_RESULT = 15
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
package org.dolphinemu.dolphinemu.features.settings.model.view
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag
|
||||||
|
|
||||||
|
class SettingsSearchResult(
|
||||||
|
name: CharSequence,
|
||||||
|
description: CharSequence,
|
||||||
|
val menuKey: MenuTag,
|
||||||
|
val settingPosition: Int,
|
||||||
|
val navigationExtras: Bundle?
|
||||||
|
) : SettingsItem(name, description) {
|
||||||
|
override val type: Int = TYPE_SEARCH_RESULT
|
||||||
|
|
||||||
|
override val setting: AbstractSetting? = null
|
||||||
|
}
|
||||||
+191
-34
@@ -7,14 +7,17 @@ import android.content.DialogInterface
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
import android.view.Menu
|
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import android.view.animation.PathInterpolator
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.activity.OnBackPressedCallback
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.appcompat.widget.SearchView
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
|
import androidx.core.view.WindowCompat
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.core.view.WindowInsetsCompat
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
@@ -39,6 +42,17 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView, ThemeProvide
|
|||||||
private var dialog: AlertDialog? = null
|
private var dialog: AlertDialog? = null
|
||||||
private var toolbarLayout: CollapsingToolbarLayout? = null
|
private var toolbarLayout: CollapsingToolbarLayout? = null
|
||||||
private var binding: ActivitySettingsBinding? = null
|
private var binding: ActivitySettingsBinding? = null
|
||||||
|
private lateinit var searchView: SearchView
|
||||||
|
private var expandedToolbarHeight = 0
|
||||||
|
private var toolbarStateGeneration = 0
|
||||||
|
private var currentToolbarTitle: String? = null
|
||||||
|
private var currentToolbarShowsHeadline = false
|
||||||
|
private var currentToolbarShowsSearch = false
|
||||||
|
private var currentToolbarShowsSearchMode = false
|
||||||
|
override val settingsSearchQuery: String
|
||||||
|
get() = presenter!!.settingsSearchQuery
|
||||||
|
override val isSettingsSearchActive: Boolean
|
||||||
|
get() = presenter!!.isSettingsSearchActive
|
||||||
|
|
||||||
override var themeId: Int = 0
|
override var themeId: Int = 0
|
||||||
override var isMappingAllDevices = false
|
override var isMappingAllDevices = false
|
||||||
@@ -76,8 +90,11 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView, ThemeProvide
|
|||||||
presenter = SettingsActivityPresenter(this, settings)
|
presenter = SettingsActivityPresenter(this, settings)
|
||||||
presenter!!.onCreate(savedInstanceState, menuTag, gameID, revision, isWii, this)
|
presenter!!.onCreate(savedInstanceState, menuTag, gameID, revision, isWii, this)
|
||||||
toolbarLayout = binding!!.toolbarSettingsLayout
|
toolbarLayout = binding!!.toolbarSettingsLayout
|
||||||
|
expandedToolbarHeight = toolbarLayout!!.layoutParams.height
|
||||||
setSupportActionBar(binding!!.toolbarSettings)
|
setSupportActionBar(binding!!.toolbarSettings)
|
||||||
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
|
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
|
||||||
|
setUpSettingsSearch()
|
||||||
|
setUpBackNavigation()
|
||||||
|
|
||||||
// TODO: Remove this when CollapsingToolbarLayouts are fixed by Google
|
// TODO: Remove this when CollapsingToolbarLayouts are fixed by Google
|
||||||
// https://github.com/material-components/material-components-android/issues/1310
|
// https://github.com/material-components/material-components-android/issues/1310
|
||||||
@@ -86,16 +103,78 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView, ThemeProvide
|
|||||||
enableScrollTint(this, binding!!.toolbarSettings, binding!!.appbarSettings)
|
enableScrollTint(this, binding!!.toolbarSettings, binding!!.appbarSettings)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
private fun setUpSettingsSearch() {
|
||||||
val inflater = menuInflater
|
searchView = binding!!.settingsSearch
|
||||||
inflater.inflate(R.menu.menu_settings, menu)
|
searchView.setQuery(settingsSearchQuery, false)
|
||||||
return true
|
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
|
||||||
|
override fun onQueryTextSubmit(query: String?): Boolean {
|
||||||
|
searchView.clearFocus()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onQueryTextChange(newText: String?): Boolean {
|
||||||
|
presenter!!.onSettingsSearchQueryChanged(newText.orEmpty())
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
binding!!.settingsSearchPreview.setOnClickListener { enterSettingsSearch() }
|
||||||
|
binding!!.settingsSearchToolbar.setNavigationOnClickListener { exitSettingsSearch() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun enterSettingsSearch() {
|
||||||
|
if (!presenter!!.enterSettingsSearch()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshToolbarState()
|
||||||
|
val focusDelay = if (areSystemAnimationsEnabled()) SEARCH_FOCUS_DELAY_MS else 0L
|
||||||
|
searchView.postDelayed({
|
||||||
|
if (!isSettingsSearchActive) {
|
||||||
|
return@postDelayed
|
||||||
|
}
|
||||||
|
searchView.requestFocus()
|
||||||
|
WindowCompat.getInsetsController(window, searchView).show(WindowInsetsCompat.Type.ime())
|
||||||
|
}, focusDelay)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun exitSettingsSearch() {
|
||||||
|
if (!presenter!!.exitSettingsSearch()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
searchView.setQuery("", false)
|
||||||
|
searchView.clearFocus()
|
||||||
|
WindowCompat.getInsetsController(window, searchView).hide(WindowInsetsCompat.Type.ime())
|
||||||
|
refreshToolbarState()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun refreshToolbarState() {
|
||||||
|
val title = currentToolbarTitle ?: getString(R.string.settings)
|
||||||
|
setToolbarState(
|
||||||
|
title, currentToolbarShowsHeadline, currentToolbarShowsSearch
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setUpBackNavigation() {
|
||||||
|
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
|
||||||
|
override fun handleOnBackPressed() {
|
||||||
|
if (supportFragmentManager.backStackEntryCount == 0 && isSettingsSearchActive) {
|
||||||
|
exitSettingsSearch()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
isEnabled = false
|
||||||
|
onBackPressedDispatcher.onBackPressed()
|
||||||
|
isEnabled = true
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSaveInstanceState(outState: Bundle) {
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
// Critical: If super method is not called, rotations will be busted.
|
// Critical: If super method is not called, rotations will be busted.
|
||||||
super.onSaveInstanceState(outState)
|
super.onSaveInstanceState(outState)
|
||||||
outState.putBoolean(KEY_MAPPING_ALL_DEVICES, isMappingAllDevices)
|
outState.putBoolean(KEY_MAPPING_ALL_DEVICES, isMappingAllDevices)
|
||||||
|
presenter!!.onSaveInstanceState(outState)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
@@ -128,10 +207,17 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView, ThemeProvide
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun showSettingsFragment(
|
override fun showSettingsFragment(
|
||||||
|
menuTag: MenuTag, extras: Bundle?, addToStack: Boolean, gameId: String
|
||||||
|
) {
|
||||||
|
replaceSettingsFragment(menuTag, extras, addToStack, gameId, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun replaceSettingsFragment(
|
||||||
menuTag: MenuTag,
|
menuTag: MenuTag,
|
||||||
extras: Bundle?,
|
extras: Bundle?,
|
||||||
addToStack: Boolean,
|
addToStack: Boolean,
|
||||||
gameId: String
|
gameId: String,
|
||||||
|
isSearchResult: Boolean
|
||||||
) {
|
) {
|
||||||
if (!addToStack && fragment != null) return
|
if (!addToStack && fragment != null) return
|
||||||
val transaction = supportFragmentManager.beginTransaction()
|
val transaction = supportFragmentManager.beginTransaction()
|
||||||
@@ -140,15 +226,18 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView, ThemeProvide
|
|||||||
transaction.setCustomAnimations(
|
transaction.setCustomAnimations(
|
||||||
R.anim.anim_settings_fragment_in,
|
R.anim.anim_settings_fragment_in,
|
||||||
R.anim.anim_settings_fragment_out,
|
R.anim.anim_settings_fragment_out,
|
||||||
0,
|
if (isSearchResult) R.anim.anim_settings_search_pop_in else 0,
|
||||||
R.anim.anim_pop_settings_fragment_out
|
if (isSearchResult) {
|
||||||
|
R.anim.anim_settings_search_pop_out
|
||||||
|
} else {
|
||||||
|
R.anim.anim_pop_settings_fragment_out
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
transaction.addToBackStack(null)
|
transaction.addToBackStack(null)
|
||||||
}
|
}
|
||||||
transaction.replace(
|
transaction.replace(
|
||||||
R.id.frame_content_settings,
|
R.id.frame_content_settings, newInstance(menuTag, gameId, extras), FRAGMENT_TAG
|
||||||
newInstance(menuTag, gameId, extras), FRAGMENT_TAG
|
|
||||||
)
|
)
|
||||||
transaction.commit()
|
transaction.commit()
|
||||||
}
|
}
|
||||||
@@ -157,16 +246,22 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView, ThemeProvide
|
|||||||
fragment.show(supportFragmentManager, FRAGMENT_DIALOG_TAG)
|
fragment.show(supportFragmentManager, FRAGMENT_DIALOG_TAG)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun showSearchResult(
|
||||||
|
menuTag: MenuTag, settingPosition: Int, gameId: String, extras: Bundle?
|
||||||
|
) {
|
||||||
|
val navigationExtras = extras?.let(::Bundle) ?: Bundle()
|
||||||
|
navigationExtras.putInt(
|
||||||
|
SettingsFragment.ARGUMENT_SCROLL_TO_SETTING_POSITION, settingPosition
|
||||||
|
)
|
||||||
|
replaceSettingsFragment(menuTag, navigationExtras, true, gameId, true)
|
||||||
|
}
|
||||||
|
|
||||||
private fun areSystemAnimationsEnabled(): Boolean {
|
private fun areSystemAnimationsEnabled(): Boolean {
|
||||||
val duration = android.provider.Settings.Global.getFloat(
|
val duration = android.provider.Settings.Global.getFloat(
|
||||||
contentResolver,
|
contentResolver, android.provider.Settings.Global.ANIMATOR_DURATION_SCALE, 1f
|
||||||
android.provider.Settings.Global.ANIMATOR_DURATION_SCALE,
|
|
||||||
1f
|
|
||||||
)
|
)
|
||||||
val transition = android.provider.Settings.Global.getFloat(
|
val transition = android.provider.Settings.Global.getFloat(
|
||||||
contentResolver,
|
contentResolver, android.provider.Settings.Global.TRANSITION_ANIMATION_SCALE, 1f
|
||||||
android.provider.Settings.Global.TRANSITION_ANIMATION_SCALE,
|
|
||||||
1f
|
|
||||||
)
|
)
|
||||||
return duration != 0f && transition != 0f
|
return duration != 0f && transition != 0f
|
||||||
}
|
}
|
||||||
@@ -183,10 +278,8 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView, ThemeProvide
|
|||||||
|
|
||||||
override fun showLoading() {
|
override fun showLoading() {
|
||||||
if (dialog == null) {
|
if (dialog == null) {
|
||||||
dialog = MaterialAlertDialogBuilder(this)
|
dialog = MaterialAlertDialogBuilder(this).setTitle(getString(R.string.load_settings))
|
||||||
.setTitle(getString(R.string.load_settings))
|
.setView(R.layout.dialog_indeterminate_progress).create()
|
||||||
.setView(R.layout.dialog_indeterminate_progress)
|
|
||||||
.create()
|
|
||||||
}
|
}
|
||||||
dialog!!.show()
|
dialog!!.show()
|
||||||
}
|
}
|
||||||
@@ -196,12 +289,10 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView, ThemeProvide
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun showGameIniJunkDeletionQuestion() {
|
override fun showGameIniJunkDeletionQuestion() {
|
||||||
MaterialAlertDialogBuilder(this)
|
MaterialAlertDialogBuilder(this).setTitle(getString(R.string.game_ini_junk_title))
|
||||||
.setTitle(getString(R.string.game_ini_junk_title))
|
|
||||||
.setMessage(getString(R.string.game_ini_junk_question))
|
.setMessage(getString(R.string.game_ini_junk_question))
|
||||||
.setPositiveButton(R.string.yes) { _: DialogInterface?, _: Int -> presenter!!.clearGameSettings() }
|
.setPositiveButton(R.string.yes) { _: DialogInterface?, _: Int -> presenter!!.clearGameSettings() }
|
||||||
.setNegativeButton(R.string.no, null)
|
.setNegativeButton(R.string.no, null).show()
|
||||||
.show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSettingsFileLoaded(settings: Settings) {
|
override fun onSettingsFileLoaded(settings: Settings) {
|
||||||
@@ -229,13 +320,78 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView, ThemeProvide
|
|||||||
return presenter!!.hasMenuTagActionForValue(menuTag, value)
|
return presenter!!.hasMenuTagActionForValue(menuTag, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getMenuTagActionExtras(menuTag: MenuTag, value: Int): Bundle? {
|
||||||
|
return presenter!!.getMenuTagActionExtras(menuTag, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun filterSettings(query: String) {
|
||||||
|
fragment?.filterSettings(query)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onSupportNavigateUp(): Boolean {
|
override fun onSupportNavigateUp(): Boolean {
|
||||||
onBackPressed()
|
onBackPressedDispatcher.onBackPressed()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setToolbarTitle(title: String) {
|
override fun setToolbarState(title: String, showHeadline: Boolean, showSearch: Boolean) {
|
||||||
binding!!.toolbarSettingsLayout.title = title
|
val appBar = binding!!.appbarSettings
|
||||||
|
val generation = ++toolbarStateGeneration
|
||||||
|
val showSearchMode = showSearch && isSettingsSearchActive
|
||||||
|
val stateChanged =
|
||||||
|
currentToolbarTitle != title || currentToolbarShowsHeadline != showHeadline || currentToolbarShowsSearch != showSearch || currentToolbarShowsSearchMode != showSearchMode
|
||||||
|
appBar.animate().cancel()
|
||||||
|
|
||||||
|
if (!appBar.isLaidOut || !stateChanged) {
|
||||||
|
applyToolbarState(title, showHeadline, showSearch)
|
||||||
|
appBar.alpha = 1f
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!showSearch) {
|
||||||
|
searchView.clearFocus()
|
||||||
|
}
|
||||||
|
|
||||||
|
appBar.animate().alpha(0f).setDuration(APP_BAR_FADE_OUT_DURATION_MS)
|
||||||
|
.setInterpolator(APP_BAR_FADE_OUT_INTERPOLATOR).withEndAction {
|
||||||
|
if (generation != toolbarStateGeneration) {
|
||||||
|
return@withEndAction
|
||||||
|
}
|
||||||
|
|
||||||
|
applyToolbarState(title, showHeadline, showSearch)
|
||||||
|
appBar.post {
|
||||||
|
if (generation != toolbarStateGeneration) {
|
||||||
|
return@post
|
||||||
|
}
|
||||||
|
|
||||||
|
appBar.animate().alpha(1f).setDuration(APP_BAR_FADE_IN_DURATION_MS)
|
||||||
|
.setInterpolator(APP_BAR_FADE_IN_INTERPOLATOR).start()
|
||||||
|
}
|
||||||
|
}.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun applyToolbarState(title: String, showHeadline: Boolean, showSearch: Boolean) {
|
||||||
|
val showSearchMode = showSearch && isSettingsSearchActive
|
||||||
|
toolbarLayout!!.isTitleEnabled = showHeadline
|
||||||
|
supportActionBar!!.title = title
|
||||||
|
if (showHeadline) {
|
||||||
|
toolbarLayout!!.title = title
|
||||||
|
}
|
||||||
|
toolbarLayout!!.layoutParams = toolbarLayout!!.layoutParams.apply {
|
||||||
|
height = if (showHeadline) {
|
||||||
|
expandedToolbarHeight
|
||||||
|
} else {
|
||||||
|
binding!!.toolbarSettings.layoutParams.height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
toolbarLayout!!.visibility = if (showSearchMode) View.GONE else View.VISIBLE
|
||||||
|
binding!!.settingsSearchContainer.visibility =
|
||||||
|
if (showSearch && !showSearchMode) View.VISIBLE else View.GONE
|
||||||
|
binding!!.settingsSearchModeContainer.visibility =
|
||||||
|
if (showSearchMode) View.VISIBLE else View.GONE
|
||||||
|
currentToolbarTitle = title
|
||||||
|
currentToolbarShowsHeadline = showHeadline
|
||||||
|
currentToolbarShowsSearch = showSearch
|
||||||
|
currentToolbarShowsSearchMode = showSearchMode
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setOldControllerSettingsWarningVisibility(visible: Boolean): Int {
|
override fun setOldControllerSettingsWarningVisibility(visible: Boolean): Int {
|
||||||
@@ -274,14 +430,16 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView, ThemeProvide
|
|||||||
private const val KEY_MAPPING_ALL_DEVICES = "all_devices"
|
private const val KEY_MAPPING_ALL_DEVICES = "all_devices"
|
||||||
private const val FRAGMENT_TAG = "settings"
|
private const val FRAGMENT_TAG = "settings"
|
||||||
private const val FRAGMENT_DIALOG_TAG = "settings_dialog"
|
private const val FRAGMENT_DIALOG_TAG = "settings_dialog"
|
||||||
|
private const val APP_BAR_FADE_OUT_DURATION_MS = 90L
|
||||||
|
private const val APP_BAR_FADE_IN_DURATION_MS = 180L
|
||||||
|
private const val SEARCH_FOCUS_DELAY_MS =
|
||||||
|
APP_BAR_FADE_OUT_DURATION_MS + APP_BAR_FADE_IN_DURATION_MS
|
||||||
|
private val APP_BAR_FADE_OUT_INTERPOLATOR = PathInterpolator(0.4f, 0f, 1f, 1f)
|
||||||
|
private val APP_BAR_FADE_IN_INTERPOLATOR = PathInterpolator(0f, 0f, 0.2f, 1f)
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun launch(
|
fun launch(
|
||||||
context: Context,
|
context: Context, menuTag: MenuTag?, gameId: String?, revision: Int, isWii: Boolean
|
||||||
menuTag: MenuTag?,
|
|
||||||
gameId: String?,
|
|
||||||
revision: Int,
|
|
||||||
isWii: Boolean
|
|
||||||
) {
|
) {
|
||||||
val settings = Intent(context, SettingsActivity::class.java)
|
val settings = Intent(context, SettingsActivity::class.java)
|
||||||
settings.putExtra(ARG_MENU_TAG, menuTag)
|
settings.putExtra(ARG_MENU_TAG, menuTag)
|
||||||
@@ -296,8 +454,7 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView, ThemeProvide
|
|||||||
val settings = Intent(context, SettingsActivity::class.java)
|
val settings = Intent(context, SettingsActivity::class.java)
|
||||||
settings.putExtra(ARG_MENU_TAG, menuTag)
|
settings.putExtra(ARG_MENU_TAG, menuTag)
|
||||||
settings.putExtra(
|
settings.putExtra(
|
||||||
ARG_IS_WII,
|
ARG_IS_WII, !NativeLibrary.IsRunning() || NativeLibrary.IsEmulatingWii()
|
||||||
!NativeLibrary.IsRunning() || NativeLibrary.IsEmulatingWii()
|
|
||||||
)
|
)
|
||||||
context.startActivity(settings)
|
context.startActivity(settings)
|
||||||
}
|
}
|
||||||
|
|||||||
+79
-45
@@ -10,14 +10,17 @@ import org.dolphinemu.dolphinemu.utils.AfterDirectoryInitializationRunner
|
|||||||
import org.dolphinemu.dolphinemu.utils.Log
|
import org.dolphinemu.dolphinemu.utils.Log
|
||||||
|
|
||||||
class SettingsActivityPresenter(
|
class SettingsActivityPresenter(
|
||||||
private val activityView: SettingsActivityView,
|
private val activityView: SettingsActivityView, var settings: Settings?
|
||||||
var settings: Settings?
|
|
||||||
) {
|
) {
|
||||||
private var menuTag: MenuTag? = null
|
private var menuTag: MenuTag? = null
|
||||||
private var gameId: String? = null
|
private var gameId: String? = null
|
||||||
private var revision = 0
|
private var revision = 0
|
||||||
private var isWii = false
|
private var isWii = false
|
||||||
private lateinit var activity: AppCompatActivity
|
private lateinit var activity: AppCompatActivity
|
||||||
|
var settingsSearchQuery = ""
|
||||||
|
private set
|
||||||
|
var isSettingsSearchActive = false
|
||||||
|
private set
|
||||||
|
|
||||||
fun onCreate(
|
fun onCreate(
|
||||||
savedInstanceState: Bundle?,
|
savedInstanceState: Bundle?,
|
||||||
@@ -32,6 +35,43 @@ class SettingsActivityPresenter(
|
|||||||
this.revision = revision
|
this.revision = revision
|
||||||
this.isWii = isWii
|
this.isWii = isWii
|
||||||
this.activity = activity
|
this.activity = activity
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
isSettingsSearchActive =
|
||||||
|
savedInstanceState.getBoolean(KEY_SETTINGS_SEARCH_ACTIVE)
|
||||||
|
settingsSearchQuery =
|
||||||
|
savedInstanceState.getString(KEY_SETTINGS_SEARCH_QUERY).orEmpty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onSaveInstanceState(outState: Bundle) {
|
||||||
|
outState.putBoolean(KEY_SETTINGS_SEARCH_ACTIVE, isSettingsSearchActive)
|
||||||
|
outState.putString(KEY_SETTINGS_SEARCH_QUERY, settingsSearchQuery)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onSettingsSearchQueryChanged(query: String) {
|
||||||
|
settingsSearchQuery = query
|
||||||
|
activityView.filterSettings(query)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun enterSettingsSearch(): Boolean {
|
||||||
|
if (isSettingsSearchActive) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
isSettingsSearchActive = true
|
||||||
|
activityView.filterSettings(settingsSearchQuery)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun exitSettingsSearch(): Boolean {
|
||||||
|
if (!isSettingsSearchActive) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
isSettingsSearchActive = false
|
||||||
|
settingsSearchQuery = ""
|
||||||
|
activityView.filterSettings("")
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onDestroy() {
|
fun onDestroy() {
|
||||||
@@ -85,55 +125,49 @@ class SettingsActivityPresenter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun onMenuTagAction(menuTag: MenuTag, value: Int) {
|
fun onMenuTagAction(menuTag: MenuTag, value: Int) {
|
||||||
if (menuTag.isSerialPort1Menu) {
|
val action = getMenuTagAction(menuTag, value) ?: return
|
||||||
// Not disabled or dummy
|
activityView.showSettingsFragment(action.menuTag, action.extras, true, gameId!!)
|
||||||
if (value != 0 && value != 255) {
|
|
||||||
val bundle = Bundle()
|
|
||||||
bundle.putInt(SettingsFragmentPresenter.ARG_SERIALPORT1_TYPE, value)
|
|
||||||
activityView.showSettingsFragment(menuTag, bundle, true, gameId!!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (menuTag.isGCPadMenu) {
|
|
||||||
// Not disabled
|
|
||||||
if (value != 0)
|
|
||||||
{
|
|
||||||
val bundle = Bundle()
|
|
||||||
bundle.putInt(SettingsFragmentPresenter.ARG_CONTROLLER_TYPE, value)
|
|
||||||
activityView.showSettingsFragment(menuTag, bundle, true, gameId!!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (menuTag.isWiimoteMenu) {
|
|
||||||
// Emulated Wii Remote
|
|
||||||
if (value == 1) {
|
|
||||||
activityView.showSettingsFragment(menuTag, null, true, gameId!!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (menuTag.isWiimoteExtensionMenu) {
|
|
||||||
// Not disabled
|
|
||||||
if (value != 0) {
|
|
||||||
val bundle = Bundle()
|
|
||||||
bundle.putInt(SettingsFragmentPresenter.ARG_CONTROLLER_TYPE, value)
|
|
||||||
activityView.showSettingsFragment(menuTag, bundle, true, gameId!!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hasMenuTagActionForValue(menuTag: MenuTag, value: Int): Boolean {
|
fun hasMenuTagActionForValue(menuTag: MenuTag, value: Int): Boolean {
|
||||||
if (menuTag.isSerialPort1Menu) {
|
return getMenuTagAction(menuTag, value) != null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getMenuTagActionExtras(menuTag: MenuTag, value: Int): Bundle? {
|
||||||
|
return getMenuTagAction(menuTag, value)?.extras
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getMenuTagAction(menuTag: MenuTag, value: Int): MenuTagAction? {
|
||||||
|
return when {
|
||||||
// Not disabled or dummy
|
// Not disabled or dummy
|
||||||
return value != 0 && value != 255
|
menuTag.isSerialPort1Menu && value != 0 && value != 255 -> MenuTagAction(
|
||||||
}
|
menuTag, Bundle().apply {
|
||||||
if (menuTag.isGCPadMenu) {
|
putInt(SettingsFragmentPresenter.ARG_SERIALPORT1_TYPE, value)
|
||||||
|
})
|
||||||
|
|
||||||
// Not disabled
|
// Not disabled
|
||||||
return value != 0
|
menuTag.isGCPadMenu && value != 0 -> MenuTagAction(
|
||||||
}
|
menuTag, Bundle().apply {
|
||||||
if (menuTag.isWiimoteMenu) {
|
putInt(SettingsFragmentPresenter.ARG_CONTROLLER_TYPE, value)
|
||||||
|
})
|
||||||
|
|
||||||
// Emulated Wii Remote
|
// Emulated Wii Remote
|
||||||
return value == 1
|
menuTag.isWiimoteMenu && value == 1 -> MenuTagAction(menuTag, null)
|
||||||
}
|
|
||||||
return if (menuTag.isWiimoteExtensionMenu) {
|
|
||||||
// Not disabled
|
// Not disabled
|
||||||
value != 0
|
menuTag.isWiimoteExtensionMenu && value != 0 -> MenuTagAction(
|
||||||
} else false
|
menuTag, Bundle().apply {
|
||||||
|
putInt(SettingsFragmentPresenter.ARG_CONTROLLER_TYPE, value)
|
||||||
|
})
|
||||||
|
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class MenuTagAction(val menuTag: MenuTag, val extras: Bundle?)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val KEY_SETTINGS_SEARCH_ACTIVE = "settings_search_active"
|
||||||
|
private const val KEY_SETTINGS_SEARCH_QUERY = "settings_search_query"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+28
-6
@@ -10,6 +10,16 @@ import org.dolphinemu.dolphinemu.features.settings.model.Settings
|
|||||||
* Abstraction for the Activity that manages SettingsFragments.
|
* Abstraction for the Activity that manages SettingsFragments.
|
||||||
*/
|
*/
|
||||||
interface SettingsActivityView {
|
interface SettingsActivityView {
|
||||||
|
/**
|
||||||
|
* The query currently displayed in the settings search view.
|
||||||
|
*/
|
||||||
|
val settingsSearchQuery: String
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the dedicated settings search screen is active.
|
||||||
|
*/
|
||||||
|
val isSettingsSearchActive: Boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show a new SettingsFragment.
|
* Show a new SettingsFragment.
|
||||||
*
|
*
|
||||||
@@ -17,12 +27,19 @@ interface SettingsActivityView {
|
|||||||
* @param addToStack Whether or not this fragment should replace a previous one.
|
* @param addToStack Whether or not this fragment should replace a previous one.
|
||||||
*/
|
*/
|
||||||
fun showSettingsFragment(
|
fun showSettingsFragment(
|
||||||
menuTag: MenuTag,
|
menuTag: MenuTag, extras: Bundle?, addToStack: Boolean, gameId: String
|
||||||
extras: Bundle?,
|
|
||||||
addToStack: Boolean,
|
|
||||||
gameId: String
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the settings screen containing a search result and scrolls to the result.
|
||||||
|
*/
|
||||||
|
fun showSearchResult(menuTag: MenuTag, settingPosition: Int, gameId: String, extras: Bundle?)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters the root settings screen using the current search query.
|
||||||
|
*/
|
||||||
|
fun filterSettings(query: String)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows a DialogFragment.
|
* Shows a DialogFragment.
|
||||||
*
|
*
|
||||||
@@ -86,6 +103,11 @@ interface SettingsActivityView {
|
|||||||
*/
|
*/
|
||||||
fun hasMenuTagActionForValue(menuTag: MenuTag, value: Int): Boolean
|
fun hasMenuTagActionForValue(menuTag: MenuTag, value: Int): Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the arguments used when opening a navigable setting's associated screen.
|
||||||
|
*/
|
||||||
|
fun getMenuTagActionExtras(menuTag: MenuTag, value: Int): Bundle?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show loading dialog while loading the settings
|
* Show loading dialog while loading the settings
|
||||||
*/
|
*/
|
||||||
@@ -102,9 +124,9 @@ interface SettingsActivityView {
|
|||||||
fun showGameIniJunkDeletionQuestion()
|
fun showGameIniJunkDeletionQuestion()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Accesses the material toolbar layout and changes the title
|
* Updates the settings app bar as a single state change.
|
||||||
*/
|
*/
|
||||||
fun setToolbarTitle(title: String)
|
fun setToolbarState(title: String, showHeadline: Boolean, showSearch: Boolean)
|
||||||
/**
|
/**
|
||||||
* Returns whether the input mapping dialog should detect inputs from all devices,
|
* Returns whether the input mapping dialog should detect inputs from all devices,
|
||||||
* not just the device configured for the controller.
|
* not just the device configured for the controller.
|
||||||
|
|||||||
+121
-111
@@ -27,14 +27,46 @@ import com.google.android.material.slider.Slider
|
|||||||
import com.google.android.material.timepicker.MaterialTimePicker
|
import com.google.android.material.timepicker.MaterialTimePicker
|
||||||
import com.google.android.material.timepicker.TimeFormat
|
import com.google.android.material.timepicker.TimeFormat
|
||||||
import org.dolphinemu.dolphinemu.R
|
import org.dolphinemu.dolphinemu.R
|
||||||
import org.dolphinemu.dolphinemu.databinding.*
|
import org.dolphinemu.dolphinemu.databinding.DialogAdvancedMappingBinding
|
||||||
|
import org.dolphinemu.dolphinemu.databinding.DialogInputStringBinding
|
||||||
|
import org.dolphinemu.dolphinemu.databinding.DialogSliderBinding
|
||||||
|
import org.dolphinemu.dolphinemu.databinding.ListItemHeaderBinding
|
||||||
|
import org.dolphinemu.dolphinemu.databinding.ListItemMappingBinding
|
||||||
|
import org.dolphinemu.dolphinemu.databinding.ListItemSearchResultBinding
|
||||||
|
import org.dolphinemu.dolphinemu.databinding.ListItemSettingBinding
|
||||||
|
import org.dolphinemu.dolphinemu.databinding.ListItemSettingSwitchBinding
|
||||||
|
import org.dolphinemu.dolphinemu.databinding.ListItemSubmenuBinding
|
||||||
import org.dolphinemu.dolphinemu.features.input.model.view.InputMappingControlSetting
|
import org.dolphinemu.dolphinemu.features.input.model.view.InputMappingControlSetting
|
||||||
import org.dolphinemu.dolphinemu.features.input.ui.AdvancedMappingDialog
|
import org.dolphinemu.dolphinemu.features.input.ui.AdvancedMappingDialog
|
||||||
import org.dolphinemu.dolphinemu.features.input.ui.MotionAlertDialog
|
import org.dolphinemu.dolphinemu.features.input.ui.MotionAlertDialog
|
||||||
import org.dolphinemu.dolphinemu.features.input.ui.viewholder.InputMappingControlSettingViewHolder
|
import org.dolphinemu.dolphinemu.features.input.ui.viewholder.InputMappingControlSettingViewHolder
|
||||||
import org.dolphinemu.dolphinemu.features.settings.model.Settings
|
import org.dolphinemu.dolphinemu.features.settings.model.Settings
|
||||||
import org.dolphinemu.dolphinemu.features.settings.model.view.*
|
import org.dolphinemu.dolphinemu.features.settings.model.view.DateTimeChoiceSetting
|
||||||
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.*
|
import org.dolphinemu.dolphinemu.features.settings.model.view.DirectoryPicker
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.model.view.FilePicker
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.model.view.FloatSliderSetting
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.model.view.InputStringSetting
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.model.view.IntSliderSetting
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsSearchResult
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.model.view.SingleChoiceSetting
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.model.view.SingleChoiceSettingDynamicDescriptions
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.model.view.SliderSetting
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.model.view.StringSingleChoiceSetting
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.model.view.SubmenuSetting
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.model.view.SwitchSetting
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.DateTimeSettingViewHolder
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.FilePickerViewHolder
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.HeaderHyperLinkViewHolder
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.HeaderViewHolder
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.InputStringSettingViewHolder
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.RunRunnableViewHolder
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SettingViewHolder
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SettingsSearchResultViewHolder
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SingleChoiceViewHolder
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SliderViewHolder
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SubmenuViewHolder
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SwitchSettingViewHolder
|
||||||
import org.dolphinemu.dolphinemu.utils.DirectoryInitialization
|
import org.dolphinemu.dolphinemu.utils.DirectoryInitialization
|
||||||
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper
|
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper
|
||||||
import org.dolphinemu.dolphinemu.utils.Log
|
import org.dolphinemu.dolphinemu.utils.Log
|
||||||
@@ -42,14 +74,13 @@ import org.dolphinemu.dolphinemu.utils.PermissionsHandler
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.RandomAccessFile
|
import java.io.RandomAccessFile
|
||||||
import java.util.*
|
import java.util.Calendar
|
||||||
|
import java.util.TimeZone
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
class SettingsAdapter(
|
class SettingsAdapter(
|
||||||
private val fragmentView: SettingsFragmentView,
|
private val fragmentView: SettingsFragmentView, private val context: Context
|
||||||
private val context: Context
|
) : RecyclerView.Adapter<SettingViewHolder>(), DialogInterface.OnClickListener,
|
||||||
) :
|
|
||||||
RecyclerView.Adapter<SettingViewHolder>(), DialogInterface.OnClickListener,
|
|
||||||
Slider.OnChangeListener {
|
Slider.OnChangeListener {
|
||||||
private var settingsList: ArrayList<SettingsItem>? = null
|
private var settingsList: ArrayList<SettingsItem>? = null
|
||||||
private var clickedItem: SettingsItem? = null
|
private var clickedItem: SettingsItem? = null
|
||||||
@@ -68,55 +99,59 @@ class SettingsAdapter(
|
|||||||
val inflater = LayoutInflater.from(parent.context)
|
val inflater = LayoutInflater.from(parent.context)
|
||||||
return when (viewType) {
|
return when (viewType) {
|
||||||
SettingsItem.TYPE_HEADER -> HeaderViewHolder(
|
SettingsItem.TYPE_HEADER -> HeaderViewHolder(
|
||||||
ListItemHeaderBinding.inflate(inflater, parent, false),
|
ListItemHeaderBinding.inflate(inflater, parent, false), this
|
||||||
this
|
|
||||||
)
|
)
|
||||||
|
|
||||||
SettingsItem.TYPE_SWITCH -> SwitchSettingViewHolder(
|
SettingsItem.TYPE_SWITCH -> SwitchSettingViewHolder(
|
||||||
ListItemSettingSwitchBinding.inflate(inflater, parent, false),
|
ListItemSettingSwitchBinding.inflate(inflater, parent, false), this
|
||||||
this
|
|
||||||
)
|
)
|
||||||
SettingsItem.TYPE_STRING_SINGLE_CHOICE,
|
|
||||||
SettingsItem.TYPE_SINGLE_CHOICE_DYNAMIC_DESCRIPTIONS,
|
SettingsItem.TYPE_STRING_SINGLE_CHOICE, SettingsItem.TYPE_SINGLE_CHOICE_DYNAMIC_DESCRIPTIONS, SettingsItem.TYPE_SINGLE_CHOICE -> SingleChoiceViewHolder(
|
||||||
SettingsItem.TYPE_SINGLE_CHOICE -> SingleChoiceViewHolder(
|
ListItemSettingBinding.inflate(inflater, parent, false), this
|
||||||
ListItemSettingBinding.inflate(inflater, parent, false),
|
|
||||||
this
|
|
||||||
)
|
)
|
||||||
|
|
||||||
SettingsItem.TYPE_SLIDER -> SliderViewHolder(
|
SettingsItem.TYPE_SLIDER -> SliderViewHolder(
|
||||||
ListItemSettingBinding.inflate(inflater, parent, false),
|
ListItemSettingBinding.inflate(inflater, parent, false), this, context
|
||||||
this,
|
|
||||||
context
|
|
||||||
)
|
)
|
||||||
|
|
||||||
SettingsItem.TYPE_SUBMENU -> SubmenuViewHolder(
|
SettingsItem.TYPE_SUBMENU -> SubmenuViewHolder(
|
||||||
ListItemSubmenuBinding.inflate(inflater, parent, false),
|
ListItemSubmenuBinding.inflate(inflater, parent, false), this
|
||||||
this
|
|
||||||
)
|
)
|
||||||
|
|
||||||
SettingsItem.TYPE_INPUT_MAPPING_CONTROL -> InputMappingControlSettingViewHolder(
|
SettingsItem.TYPE_INPUT_MAPPING_CONTROL -> InputMappingControlSettingViewHolder(
|
||||||
ListItemMappingBinding.inflate(inflater, parent, false),
|
ListItemMappingBinding.inflate(inflater, parent, false), this
|
||||||
this
|
|
||||||
)
|
)
|
||||||
SettingsItem.TYPE_FILE_PICKER,
|
|
||||||
SettingsItem.TYPE_DIRECTORY_PICKER -> FilePickerViewHolder(
|
SettingsItem.TYPE_FILE_PICKER, SettingsItem.TYPE_DIRECTORY_PICKER -> FilePickerViewHolder(
|
||||||
ListItemSettingBinding.inflate(inflater, parent, false),
|
ListItemSettingBinding.inflate(inflater, parent, false), this
|
||||||
this
|
|
||||||
)
|
)
|
||||||
|
|
||||||
SettingsItem.TYPE_RUN_RUNNABLE -> RunRunnableViewHolder(
|
SettingsItem.TYPE_RUN_RUNNABLE -> RunRunnableViewHolder(
|
||||||
ListItemSettingBinding.inflate(inflater, parent, false),
|
ListItemSettingBinding.inflate(inflater, parent, false), this, context
|
||||||
this, context
|
|
||||||
)
|
)
|
||||||
|
|
||||||
SettingsItem.TYPE_STRING -> InputStringSettingViewHolder(
|
SettingsItem.TYPE_STRING -> InputStringSettingViewHolder(
|
||||||
ListItemSettingBinding.inflate(inflater, parent, false), this
|
ListItemSettingBinding.inflate(inflater, parent, false), this
|
||||||
)
|
)
|
||||||
|
|
||||||
SettingsItem.TYPE_HYPERLINK_HEADER -> HeaderHyperLinkViewHolder(
|
SettingsItem.TYPE_HYPERLINK_HEADER -> HeaderHyperLinkViewHolder(
|
||||||
ListItemHeaderBinding.inflate(inflater, parent, false), this
|
ListItemHeaderBinding.inflate(inflater, parent, false), this
|
||||||
)
|
)
|
||||||
|
|
||||||
SettingsItem.TYPE_DATETIME_CHOICE -> DateTimeSettingViewHolder(
|
SettingsItem.TYPE_DATETIME_CHOICE -> DateTimeSettingViewHolder(
|
||||||
ListItemSettingBinding.inflate(inflater, parent, false), this
|
ListItemSettingBinding.inflate(inflater, parent, false), this
|
||||||
)
|
)
|
||||||
|
|
||||||
|
SettingsItem.TYPE_SEARCH_RESULT -> SettingsSearchResultViewHolder(
|
||||||
|
ListItemSearchResultBinding.inflate(inflater, parent, false), this
|
||||||
|
)
|
||||||
|
|
||||||
else -> throw IllegalArgumentException("Invalid view type: $viewType")
|
else -> throw IllegalArgumentException("Invalid view type: $viewType")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: SettingViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: SettingViewHolder, position: Int) {
|
||||||
|
holder.clearSearchResultHighlight()
|
||||||
holder.bind(getItem(position))
|
holder.bind(getItem(position))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,7 +178,7 @@ class SettingsAdapter(
|
|||||||
|
|
||||||
fun clearSetting(item: SettingsItem) {
|
fun clearSetting(item: SettingsItem) {
|
||||||
item.clear(settings!!)
|
item.clear(settings!!)
|
||||||
fragmentView.onSettingChanged()
|
fragmentView.onSettingChanged(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun notifyAllSettingsChanged() {
|
fun notifyAllSettingsChanged() {
|
||||||
@@ -153,7 +188,7 @@ class SettingsAdapter(
|
|||||||
|
|
||||||
fun onBooleanClick(item: SwitchSetting, checked: Boolean) {
|
fun onBooleanClick(item: SwitchSetting, checked: Boolean) {
|
||||||
item.setChecked(settings!!, checked)
|
item.setChecked(settings!!, checked)
|
||||||
fragmentView.onSettingChanged()
|
fragmentView.onSettingChanged(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onInputStringClick(item: InputStringSetting, position: Int) {
|
fun onInputStringClick(item: InputStringSetting, position: Int) {
|
||||||
@@ -161,29 +196,24 @@ class SettingsAdapter(
|
|||||||
val binding = DialogInputStringBinding.inflate(inflater)
|
val binding = DialogInputStringBinding.inflate(inflater)
|
||||||
val input = binding.input
|
val input = binding.input
|
||||||
input.setText(item.selectedValue)
|
input.setText(item.selectedValue)
|
||||||
dialog = MaterialAlertDialogBuilder(fragmentView.fragmentActivity)
|
dialog = MaterialAlertDialogBuilder(fragmentView.fragmentActivity).setView(binding.root)
|
||||||
.setView(binding.root)
|
|
||||||
.setMessage(item.description)
|
.setMessage(item.description)
|
||||||
.setPositiveButton(R.string.ok) { _: DialogInterface?, _: Int ->
|
.setPositiveButton(R.string.ok) { _: DialogInterface?, _: Int ->
|
||||||
val editTextInput = input.text.toString()
|
val editTextInput = input.text.toString()
|
||||||
if (item.selectedValue != editTextInput) {
|
if (item.selectedValue != editTextInput) {
|
||||||
notifyItemChanged(position)
|
notifyItemChanged(position)
|
||||||
fragmentView.onSettingChanged()
|
fragmentView.onSettingChanged(item)
|
||||||
}
|
}
|
||||||
item.setSelectedValue(fragmentView.settings!!, editTextInput)
|
item.setSelectedValue(fragmentView.settings!!, editTextInput)
|
||||||
}
|
}.setNegativeButton(R.string.cancel, null).show()
|
||||||
.setNegativeButton(R.string.cancel, null)
|
|
||||||
.show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onSingleChoiceClick(item: SingleChoiceSetting, position: Int) {
|
fun onSingleChoiceClick(item: SingleChoiceSetting, position: Int) {
|
||||||
clickedItem = item
|
clickedItem = item
|
||||||
clickedPosition = position
|
clickedPosition = position
|
||||||
val value = getSelectionForSingleChoiceValue(item)
|
val value = getSelectionForSingleChoiceValue(item)
|
||||||
dialog = MaterialAlertDialogBuilder(fragmentView.fragmentActivity)
|
dialog = MaterialAlertDialogBuilder(fragmentView.fragmentActivity).setTitle(item.name)
|
||||||
.setTitle(item.name)
|
.setSingleChoiceItems(item.choicesId, value, this).show()
|
||||||
.setSingleChoiceItems(item.choicesId, value, this)
|
|
||||||
.show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onStringSingleChoiceClick(item: StringSingleChoiceSetting, position: Int) {
|
fun onStringSingleChoiceClick(item: StringSingleChoiceSetting, position: Int) {
|
||||||
@@ -193,35 +223,26 @@ class SettingsAdapter(
|
|||||||
val choices = item.choices
|
val choices = item.choices
|
||||||
val noChoicesAvailableString = item.noChoicesAvailableString
|
val noChoicesAvailableString = item.noChoicesAvailableString
|
||||||
dialog = if (noChoicesAvailableString != 0 && choices.isEmpty()) {
|
dialog = if (noChoicesAvailableString != 0 && choices.isEmpty()) {
|
||||||
MaterialAlertDialogBuilder(fragmentView.fragmentActivity)
|
MaterialAlertDialogBuilder(fragmentView.fragmentActivity).setTitle(item.name)
|
||||||
.setTitle(item.name)
|
.setMessage(noChoicesAvailableString).setPositiveButton(R.string.ok, null).show()
|
||||||
.setMessage(noChoicesAvailableString)
|
|
||||||
.setPositiveButton(R.string.ok, null)
|
|
||||||
.show()
|
|
||||||
} else {
|
} else {
|
||||||
MaterialAlertDialogBuilder(fragmentView.fragmentActivity)
|
MaterialAlertDialogBuilder(fragmentView.fragmentActivity).setTitle(item.name)
|
||||||
.setTitle(item.name)
|
|
||||||
.setSingleChoiceItems(
|
.setSingleChoiceItems(
|
||||||
item.choices, item.selectedValueIndex,
|
item.choices, item.selectedValueIndex, this
|
||||||
this
|
).show()
|
||||||
)
|
|
||||||
.show()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onSingleChoiceDynamicDescriptionsClick(
|
fun onSingleChoiceDynamicDescriptionsClick(
|
||||||
item: SingleChoiceSettingDynamicDescriptions,
|
item: SingleChoiceSettingDynamicDescriptions, position: Int
|
||||||
position: Int
|
|
||||||
) {
|
) {
|
||||||
clickedItem = item
|
clickedItem = item
|
||||||
clickedPosition = position
|
clickedPosition = position
|
||||||
|
|
||||||
val value = getSelectionForSingleChoiceDynamicDescriptionsValue(item)
|
val value = getSelectionForSingleChoiceDynamicDescriptionsValue(item)
|
||||||
|
|
||||||
dialog = MaterialAlertDialogBuilder(fragmentView.fragmentActivity)
|
dialog = MaterialAlertDialogBuilder(fragmentView.fragmentActivity).setTitle(item.name)
|
||||||
.setTitle(item.name)
|
.setSingleChoiceItems(item.choicesId, value, this).show()
|
||||||
.setSingleChoiceItems(item.choicesId, value, this)
|
|
||||||
.show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onSliderClick(item: SliderSetting, position: Int) {
|
fun onSliderClick(item: SliderSetting, position: Int) {
|
||||||
@@ -251,6 +272,7 @@ class SettingsAdapter(
|
|||||||
slider.valueTo = item.max
|
slider.valueTo = item.max
|
||||||
slider.stepSize = item.stepSize
|
slider.stepSize = item.stepSize
|
||||||
}
|
}
|
||||||
|
|
||||||
is IntSliderSetting -> {
|
is IntSliderSetting -> {
|
||||||
slider.valueFrom = item.min.toFloat()
|
slider.valueFrom = item.min.toFloat()
|
||||||
slider.valueTo = item.max.toFloat()
|
slider.valueTo = item.max.toFloat()
|
||||||
@@ -260,29 +282,27 @@ class SettingsAdapter(
|
|||||||
slider.value = (seekbarProgress / slider.stepSize).roundToInt() * slider.stepSize
|
slider.value = (seekbarProgress / slider.stepSize).roundToInt() * slider.stepSize
|
||||||
slider.addOnChangeListener(this)
|
slider.addOnChangeListener(this)
|
||||||
|
|
||||||
dialog = MaterialAlertDialogBuilder(fragmentView.fragmentActivity)
|
dialog = MaterialAlertDialogBuilder(fragmentView.fragmentActivity).setTitle(item.name)
|
||||||
.setTitle(item.name)
|
.setView(binding.root).setPositiveButton(R.string.ok, this).show()
|
||||||
.setView(binding.root)
|
|
||||||
.setPositiveButton(R.string.ok, this)
|
|
||||||
.show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onSubmenuClick(item: SubmenuSetting) {
|
fun onSubmenuClick(item: SubmenuSetting) {
|
||||||
fragmentView.loadSubMenu(item.menuKey)
|
fragmentView.loadSubMenu(item.menuKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onSearchResultClick(item: SettingsSearchResult) {
|
||||||
|
fragmentView.loadSearchResult(item.menuKey, item.settingPosition, item.navigationExtras)
|
||||||
|
}
|
||||||
|
|
||||||
fun onInputMappingClick(item: InputMappingControlSetting, position: Int) {
|
fun onInputMappingClick(item: InputMappingControlSetting, position: Int) {
|
||||||
if (item.controller.getDefaultDevice().isEmpty() && !fragmentView.isMappingAllDevices) {
|
if (item.controller.getDefaultDevice().isEmpty() && !fragmentView.isMappingAllDevices) {
|
||||||
MaterialAlertDialogBuilder(fragmentView.fragmentActivity)
|
MaterialAlertDialogBuilder(fragmentView.fragmentActivity).setMessage(R.string.input_binding_no_device)
|
||||||
.setMessage(R.string.input_binding_no_device)
|
.setPositiveButton(R.string.ok, this).show()
|
||||||
.setPositiveButton(R.string.ok, this)
|
|
||||||
.show()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val dialog = MotionAlertDialog(
|
val dialog = MotionAlertDialog(
|
||||||
fragmentView.fragmentActivity, item,
|
fragmentView.fragmentActivity, item, fragmentView.isMappingAllDevices
|
||||||
fragmentView.isMappingAllDevices
|
|
||||||
)
|
)
|
||||||
|
|
||||||
val background = ContextCompat.getDrawable(context, R.drawable.dialog_round)
|
val background = ContextCompat.getDrawable(context, R.drawable.dialog_round)
|
||||||
@@ -296,18 +316,16 @@ class SettingsAdapter(
|
|||||||
dialog.setTitle(R.string.input_binding)
|
dialog.setTitle(R.string.input_binding)
|
||||||
dialog.setMessage(
|
dialog.setMessage(
|
||||||
String.format(
|
String.format(
|
||||||
context.getString(R.string.input_binding_description),
|
context.getString(R.string.input_binding_description), item.name
|
||||||
item.name
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
dialog.setButton(AlertDialog.BUTTON_NEGATIVE, context.getString(R.string.cancel), this)
|
dialog.setButton(AlertDialog.BUTTON_NEGATIVE, context.getString(R.string.cancel), this)
|
||||||
dialog.setButton(
|
dialog.setButton(
|
||||||
AlertDialog.BUTTON_NEUTRAL,
|
AlertDialog.BUTTON_NEUTRAL, context.getString(R.string.clear)
|
||||||
context.getString(R.string.clear)
|
|
||||||
) { _: DialogInterface?, _: Int -> item.clearValue() }
|
) { _: DialogInterface?, _: Int -> item.clearValue() }
|
||||||
dialog.setOnDismissListener {
|
dialog.setOnDismissListener {
|
||||||
notifyItemChanged(position)
|
notifyItemChanged(position)
|
||||||
fragmentView.onSettingChanged()
|
fragmentView.onSettingChanged(item)
|
||||||
}
|
}
|
||||||
dialog.setCanceledOnTouchOutside(false)
|
dialog.setCanceledOnTouchOutside(false)
|
||||||
dialog.show()
|
dialog.show()
|
||||||
@@ -317,10 +335,7 @@ class SettingsAdapter(
|
|||||||
val inflater = LayoutInflater.from(context)
|
val inflater = LayoutInflater.from(context)
|
||||||
val binding = DialogAdvancedMappingBinding.inflate(inflater)
|
val binding = DialogAdvancedMappingBinding.inflate(inflater)
|
||||||
val dialog = AdvancedMappingDialog(
|
val dialog = AdvancedMappingDialog(
|
||||||
context,
|
context, binding, item.controlReference, item.controller
|
||||||
binding,
|
|
||||||
item.controlReference,
|
|
||||||
item.controller
|
|
||||||
)
|
)
|
||||||
|
|
||||||
val background = ContextCompat.getDrawable(context, R.drawable.dialog_round)
|
val background = ContextCompat.getDrawable(context, R.drawable.dialog_round)
|
||||||
@@ -338,12 +353,11 @@ class SettingsAdapter(
|
|||||||
) { _: DialogInterface?, _: Int ->
|
) { _: DialogInterface?, _: Int ->
|
||||||
item.value = dialog.expression
|
item.value = dialog.expression
|
||||||
notifyItemChanged(position)
|
notifyItemChanged(position)
|
||||||
fragmentView.onSettingChanged()
|
fragmentView.onSettingChanged(item)
|
||||||
}
|
}
|
||||||
dialog.setButton(AlertDialog.BUTTON_NEGATIVE, context.getString(R.string.cancel), this)
|
dialog.setButton(AlertDialog.BUTTON_NEGATIVE, context.getString(R.string.cancel), this)
|
||||||
dialog.setButton(
|
dialog.setButton(
|
||||||
AlertDialog.BUTTON_NEUTRAL,
|
AlertDialog.BUTTON_NEUTRAL, context.getString(R.string.clear)
|
||||||
context.getString(R.string.clear)
|
|
||||||
) { _: DialogInterface?, _: Int -> }
|
) { _: DialogInterface?, _: Int -> }
|
||||||
dialog.setCanceledOnTouchOutside(false)
|
dialog.setCanceledOnTouchOutside(false)
|
||||||
dialog.show()
|
dialog.show()
|
||||||
@@ -361,14 +375,12 @@ class SettingsAdapter(
|
|||||||
val directoryPicker = item as DirectoryPicker
|
val directoryPicker = item as DirectoryPicker
|
||||||
|
|
||||||
if (!PermissionsHandler.isExternalStorageLegacy()) {
|
if (!PermissionsHandler.isExternalStorageLegacy()) {
|
||||||
MaterialAlertDialogBuilder(context)
|
MaterialAlertDialogBuilder(context).setMessage(R.string.path_not_changeable_scoped_storage)
|
||||||
.setMessage(R.string.path_not_changeable_scoped_storage)
|
|
||||||
.setPositiveButton(R.string.ok) { dialog: DialogInterface, _: Int -> dialog.dismiss() }
|
.setPositiveButton(R.string.ok) { dialog: DialogInterface, _: Int -> dialog.dismiss() }
|
||||||
.show()
|
.show()
|
||||||
} else {
|
} else {
|
||||||
val intent = FileBrowserHelper.createDirectoryPickerIntent(
|
val intent = FileBrowserHelper.createDirectoryPickerIntent(
|
||||||
fragmentView.fragmentActivity,
|
fragmentView.fragmentActivity, FileBrowserHelper.GAME_EXTENSIONS
|
||||||
FileBrowserHelper.GAME_EXTENSIONS
|
|
||||||
)
|
)
|
||||||
directoryPicker.launcher.launch(intent)
|
directoryPicker.launcher.launch(intent)
|
||||||
}
|
}
|
||||||
@@ -400,32 +412,24 @@ class SettingsAdapter(
|
|||||||
calendar.timeZone = TimeZone.getTimeZone("UTC")
|
calendar.timeZone = TimeZone.getTimeZone("UTC")
|
||||||
|
|
||||||
// Start and end epoch times available for the Wii's date picker
|
// Start and end epoch times available for the Wii's date picker
|
||||||
val calendarConstraints = CalendarConstraints.Builder()
|
val calendarConstraints =
|
||||||
.setStart(946684800000L)
|
CalendarConstraints.Builder().setStart(946684800000L).setEnd(2082672000000L).build()
|
||||||
.setEnd(2082672000000L)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
var timeFormat = TimeFormat.CLOCK_12H
|
var timeFormat = TimeFormat.CLOCK_12H
|
||||||
if (DateFormat.is24HourFormat(fragmentView.fragmentActivity)) {
|
if (DateFormat.is24HourFormat(fragmentView.fragmentActivity)) {
|
||||||
timeFormat = TimeFormat.CLOCK_24H
|
timeFormat = TimeFormat.CLOCK_24H
|
||||||
}
|
}
|
||||||
|
|
||||||
val datePicker = MaterialDatePicker.Builder.datePicker()
|
val datePicker = MaterialDatePicker.Builder.datePicker().setSelection(storedTime)
|
||||||
.setSelection(storedTime)
|
.setTitleText(R.string.select_rtc_date).setCalendarConstraints(calendarConstraints)
|
||||||
.setTitleText(R.string.select_rtc_date)
|
|
||||||
.setCalendarConstraints(calendarConstraints)
|
|
||||||
.build()
|
|
||||||
val timePicker = MaterialTimePicker.Builder()
|
|
||||||
.setTimeFormat(timeFormat)
|
|
||||||
.setHour(calendar[Calendar.HOUR_OF_DAY])
|
|
||||||
.setMinute(calendar[Calendar.MINUTE])
|
|
||||||
.setTitleText(R.string.select_rtc_time)
|
|
||||||
.build()
|
.build()
|
||||||
|
val timePicker = MaterialTimePicker.Builder().setTimeFormat(timeFormat)
|
||||||
|
.setHour(calendar[Calendar.HOUR_OF_DAY]).setMinute(calendar[Calendar.MINUTE])
|
||||||
|
.setTitleText(R.string.select_rtc_time).build()
|
||||||
|
|
||||||
datePicker.addOnPositiveButtonClickListener {
|
datePicker.addOnPositiveButtonClickListener {
|
||||||
timePicker.show(
|
timePicker.show(
|
||||||
fragmentView.fragmentActivity.supportFragmentManager,
|
fragmentView.fragmentActivity.supportFragmentManager, "TimePicker"
|
||||||
"TimePicker"
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
timePicker.addOnPositiveButtonClickListener {
|
timePicker.addOnPositiveButtonClickListener {
|
||||||
@@ -435,7 +439,7 @@ class SettingsAdapter(
|
|||||||
val rtcString = "0x" + java.lang.Long.toHexString(epochTime)
|
val rtcString = "0x" + java.lang.Long.toHexString(epochTime)
|
||||||
if (item.getSelectedValue() != rtcString) {
|
if (item.getSelectedValue() != rtcString) {
|
||||||
notifyItemChanged(clickedPosition)
|
notifyItemChanged(clickedPosition)
|
||||||
fragmentView.onSettingChanged()
|
fragmentView.onSettingChanged(item)
|
||||||
}
|
}
|
||||||
item.setSelectedValue(fragmentView.settings!!, rtcString)
|
item.setSelectedValue(fragmentView.settings!!, rtcString)
|
||||||
clickedItem = null
|
clickedItem = null
|
||||||
@@ -448,7 +452,7 @@ class SettingsAdapter(
|
|||||||
|
|
||||||
if (filePicker.getSelectedValue() != selectedFile) {
|
if (filePicker.getSelectedValue() != selectedFile) {
|
||||||
notifyItemChanged(clickedPosition)
|
notifyItemChanged(clickedPosition)
|
||||||
fragmentView.onSettingChanged()
|
fragmentView.onSettingChanged(filePicker)
|
||||||
}
|
}
|
||||||
|
|
||||||
filePicker.setSelectedValue(fragmentView.settings!!, selectedFile)
|
filePicker.setSelectedValue(fragmentView.settings!!, selectedFile)
|
||||||
@@ -470,44 +474,50 @@ class SettingsAdapter(
|
|||||||
val scSetting = clickedItem as SingleChoiceSetting
|
val scSetting = clickedItem as SingleChoiceSetting
|
||||||
|
|
||||||
val value = getValueForSingleChoiceSelection(scSetting, which)
|
val value = getValueForSingleChoiceSelection(scSetting, which)
|
||||||
if (scSetting.selectedValue != value) fragmentView.onSettingChanged()
|
if (scSetting.selectedValue != value) fragmentView.onSettingChanged(scSetting)
|
||||||
|
|
||||||
scSetting.setSelectedValue(settings!!, value)
|
scSetting.setSelectedValue(settings!!, value)
|
||||||
|
|
||||||
closeDialog()
|
closeDialog()
|
||||||
}
|
}
|
||||||
|
|
||||||
is SingleChoiceSettingDynamicDescriptions -> {
|
is SingleChoiceSettingDynamicDescriptions -> {
|
||||||
val scSetting = clickedItem as SingleChoiceSettingDynamicDescriptions
|
val scSetting = clickedItem as SingleChoiceSettingDynamicDescriptions
|
||||||
|
|
||||||
val value = getValueForSingleChoiceDynamicDescriptionsSelection(scSetting, which)
|
val value = getValueForSingleChoiceDynamicDescriptionsSelection(scSetting, which)
|
||||||
if (scSetting.selectedValue != value) fragmentView.onSettingChanged()
|
if (scSetting.selectedValue != value) fragmentView.onSettingChanged(scSetting)
|
||||||
|
|
||||||
scSetting.setSelectedValue(settings!!, value)
|
scSetting.setSelectedValue(settings!!, value)
|
||||||
|
|
||||||
closeDialog()
|
closeDialog()
|
||||||
}
|
}
|
||||||
|
|
||||||
is StringSingleChoiceSetting -> {
|
is StringSingleChoiceSetting -> {
|
||||||
val scSetting = clickedItem as StringSingleChoiceSetting
|
val scSetting = clickedItem as StringSingleChoiceSetting
|
||||||
|
|
||||||
val value = scSetting.getValueAt(which)
|
val value = scSetting.getValueAt(which)
|
||||||
if (scSetting.selectedValue != value) fragmentView.onSettingChanged()
|
if (scSetting.selectedValue != value) fragmentView.onSettingChanged(scSetting)
|
||||||
|
|
||||||
scSetting.setSelectedValue(settings!!, value)
|
scSetting.setSelectedValue(settings!!, value)
|
||||||
|
|
||||||
closeDialog()
|
closeDialog()
|
||||||
}
|
}
|
||||||
|
|
||||||
is IntSliderSetting -> {
|
is IntSliderSetting -> {
|
||||||
val sliderSetting = clickedItem as IntSliderSetting
|
val sliderSetting = clickedItem as IntSliderSetting
|
||||||
if (sliderSetting.selectedValue != seekbarProgress.toInt()) {
|
if (sliderSetting.selectedValue != seekbarProgress.toInt()) {
|
||||||
fragmentView.onSettingChanged()
|
fragmentView.onSettingChanged(sliderSetting)
|
||||||
}
|
}
|
||||||
sliderSetting.setSelectedValue(settings!!, seekbarProgress.toInt())
|
sliderSetting.setSelectedValue(settings!!, seekbarProgress.toInt())
|
||||||
closeDialog()
|
closeDialog()
|
||||||
}
|
}
|
||||||
|
|
||||||
is FloatSliderSetting -> {
|
is FloatSliderSetting -> {
|
||||||
val sliderSetting = clickedItem as FloatSliderSetting
|
val sliderSetting = clickedItem as FloatSliderSetting
|
||||||
|
|
||||||
if (sliderSetting.selectedValue != seekbarProgress) fragmentView.onSettingChanged()
|
if (sliderSetting.selectedValue != seekbarProgress) {
|
||||||
|
fragmentView.onSettingChanged(sliderSetting)
|
||||||
|
}
|
||||||
|
|
||||||
sliderSetting.setSelectedValue(settings!!, seekbarProgress)
|
sliderSetting.setSelectedValue(settings!!, seekbarProgress)
|
||||||
|
|
||||||
@@ -540,6 +550,7 @@ class SettingsAdapter(
|
|||||||
|
|
||||||
override fun onViewRecycled(holder: SettingViewHolder) {
|
override fun onViewRecycled(holder: SettingViewHolder) {
|
||||||
super.onViewRecycled(holder)
|
super.onViewRecycled(holder)
|
||||||
|
holder.clearSearchResultHighlight()
|
||||||
holder.onViewRecycled()
|
holder.onViewRecycled()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -587,8 +598,7 @@ class SettingsAdapter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getValueForSingleChoiceDynamicDescriptionsSelection(
|
private fun getValueForSingleChoiceDynamicDescriptionsSelection(
|
||||||
item: SingleChoiceSettingDynamicDescriptions,
|
item: SingleChoiceSettingDynamicDescriptions, which: Int
|
||||||
which: Int
|
|
||||||
): Int {
|
): Int {
|
||||||
val valuesId = item.valuesId
|
val valuesId = item.valuesId
|
||||||
return if (valuesId > 0) {
|
return if (valuesId > 0) {
|
||||||
|
|||||||
+139
-26
@@ -24,17 +24,22 @@ import androidx.fragment.app.DialogFragment
|
|||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import org.dolphinemu.dolphinemu.R
|
import org.dolphinemu.dolphinemu.R
|
||||||
import org.dolphinemu.dolphinemu.databinding.FragmentSettingsBinding
|
import org.dolphinemu.dolphinemu.databinding.FragmentSettingsBinding
|
||||||
import org.dolphinemu.dolphinemu.features.settings.model.Settings
|
import org.dolphinemu.dolphinemu.features.settings.model.Settings
|
||||||
import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem
|
import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SettingViewHolder
|
||||||
import org.dolphinemu.dolphinemu.utils.GpuDriverInstallResult
|
import org.dolphinemu.dolphinemu.utils.GpuDriverInstallResult
|
||||||
import org.dolphinemu.dolphinemu.utils.SerializableHelper.serializable
|
import org.dolphinemu.dolphinemu.utils.SerializableHelper.serializable
|
||||||
import java.util.*
|
import java.util.EnumMap
|
||||||
import kotlin.collections.ArrayList
|
|
||||||
|
|
||||||
class SettingsFragment : Fragment(), SettingsFragmentView {
|
class SettingsFragment : Fragment(), SettingsFragmentView {
|
||||||
private lateinit var presenter: SettingsFragmentPresenter
|
private lateinit var presenter: SettingsFragmentPresenter
|
||||||
@@ -51,6 +56,11 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
|
|||||||
SettingsActivityResultLaunchers(this) { adapter }
|
SettingsActivityResultLaunchers(this) { adapter }
|
||||||
|
|
||||||
private var oldControllerSettingsWarningHeight = 0
|
private var oldControllerSettingsWarningHeight = 0
|
||||||
|
private var hasScrolledToSearchResult = false
|
||||||
|
private var highlightedSearchResult: SettingsItem? = null
|
||||||
|
private var highlightedSearchResultPosition = RecyclerView.NO_POSITION
|
||||||
|
private var searchIndexWarmupJob: Job? = null
|
||||||
|
private var searchJob: Job? = null
|
||||||
|
|
||||||
private var binding: FragmentSettingsBinding? = null
|
private var binding: FragmentSettingsBinding? = null
|
||||||
|
|
||||||
@@ -82,9 +92,7 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
|
||||||
container: ViewGroup?,
|
|
||||||
savedInstanceState: Bundle?
|
|
||||||
): View {
|
): View {
|
||||||
binding = FragmentSettingsBinding.inflate(inflater, container, false)
|
binding = FragmentSettingsBinding.inflate(inflater, container, false)
|
||||||
return binding!!.root
|
return binding!!.root
|
||||||
@@ -92,7 +100,11 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
|
|||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
if (titles.containsKey(menuTag)) {
|
if (titles.containsKey(menuTag)) {
|
||||||
activityView!!.setToolbarTitle(getString(titles[menuTag]!!))
|
activityView!!.setToolbarState(
|
||||||
|
getString(titles[menuTag]!!),
|
||||||
|
menuTag != MenuTag.SETTINGS,
|
||||||
|
menuTag == MenuTag.SETTINGS
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val manager = LinearLayoutManager(activity)
|
val manager = LinearLayoutManager(activity)
|
||||||
@@ -107,10 +119,13 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
|
|||||||
setInsets()
|
setInsets()
|
||||||
|
|
||||||
val activity = requireActivity() as SettingsActivityView
|
val activity = requireActivity() as SettingsActivityView
|
||||||
|
presenter.invalidateSearchIndex()
|
||||||
presenter.onViewCreated(menuTag, activity.settings)
|
presenter.onViewCreated(menuTag, activity.settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
|
clearSearchResultHighlight()
|
||||||
|
searchJob?.cancel()
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
binding = null
|
binding = null
|
||||||
}
|
}
|
||||||
@@ -129,7 +144,81 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun showSettingsList(settingsList: ArrayList<SettingsItem>) {
|
override fun showSettingsList(settingsList: ArrayList<SettingsItem>) {
|
||||||
adapter!!.setSettings(settingsList)
|
val query = activityView?.settingsSearchQuery.orEmpty()
|
||||||
|
val isShowingSearch =
|
||||||
|
menuTag == MenuTag.SETTINGS && activityView?.isSettingsSearchActive == true
|
||||||
|
if (!isShowingSearch) {
|
||||||
|
adapter!!.setSettings(settingsList)
|
||||||
|
}
|
||||||
|
if (menuTag == MenuTag.SETTINGS) {
|
||||||
|
warmUpSearchIndex()
|
||||||
|
if (isShowingSearch) {
|
||||||
|
applySettingsFilter(query)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val position = arguments?.getInt(
|
||||||
|
ARGUMENT_SCROLL_TO_SETTING_POSITION, RecyclerView.NO_POSITION
|
||||||
|
) ?: RecyclerView.NO_POSITION
|
||||||
|
if (!hasScrolledToSearchResult && position in settingsList.indices) {
|
||||||
|
hasScrolledToSearchResult = true
|
||||||
|
binding?.listSettings?.post {
|
||||||
|
val recyclerView = binding?.listSettings ?: return@post
|
||||||
|
(recyclerView.layoutManager as? LinearLayoutManager)?.scrollToPositionWithOffset(
|
||||||
|
position, 0
|
||||||
|
)
|
||||||
|
highlightSearchResult(position, settingsList[position])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun filterSettings(query: String) {
|
||||||
|
if (!this::presenter.isInitialized || presenter.settings == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
applySettingsFilter(query)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun applySettingsFilter(query: String) {
|
||||||
|
searchJob?.cancel()
|
||||||
|
if (query.isBlank()) {
|
||||||
|
val results = if (activityView?.isSettingsSearchActive == true) {
|
||||||
|
arrayListOf()
|
||||||
|
} else {
|
||||||
|
presenter.getSettingsList()
|
||||||
|
}
|
||||||
|
showSearchResults(query, results)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
searchJob = viewLifecycleOwner.lifecycleScope.launch {
|
||||||
|
delay(SEARCH_QUERY_DEBOUNCE_MS)
|
||||||
|
val results = presenter.searchSettings(query)
|
||||||
|
if (activityView?.settingsSearchQuery == query) {
|
||||||
|
showSearchResults(query, results)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun warmUpSearchIndex() {
|
||||||
|
if (searchIndexWarmupJob?.isActive == true) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
searchIndexWarmupJob = viewLifecycleOwner.lifecycleScope.launch {
|
||||||
|
presenter.prepareSearchIndex()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showSearchResults(query: String, results: ArrayList<SettingsItem>) {
|
||||||
|
adapter!!.setSettings(results)
|
||||||
|
binding?.textNoSearchResults?.text =
|
||||||
|
getString(R.string.search_settings_no_results, query.trim())
|
||||||
|
binding?.textNoSearchResults?.visibility =
|
||||||
|
if (query.isNotBlank() && results.isEmpty()) View.VISIBLE else View.GONE
|
||||||
|
binding?.listSettings?.visibility =
|
||||||
|
if (query.isNotBlank() && results.isEmpty()) View.GONE else View.VISIBLE
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun loadSubMenu(menuKey: MenuTag) {
|
override fun loadSubMenu(menuKey: MenuTag) {
|
||||||
@@ -139,13 +228,35 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
activityView!!.showSettingsFragment(
|
activityView!!.showSettingsFragment(
|
||||||
menuKey,
|
menuKey, null, true, requireArguments().getString(ARGUMENT_GAME_ID)!!
|
||||||
null,
|
|
||||||
true,
|
|
||||||
requireArguments().getString(ARGUMENT_GAME_ID)!!
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun loadSearchResult(menuKey: MenuTag, settingPosition: Int, extras: Bundle?) {
|
||||||
|
activityView!!.showSearchResult(
|
||||||
|
menuKey, settingPosition, requireArguments().getString(ARGUMENT_GAME_ID)!!, extras
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun highlightSearchResult(position: Int, setting: SettingsItem) {
|
||||||
|
val recyclerView = binding?.listSettings ?: return
|
||||||
|
highlightedSearchResult = setting
|
||||||
|
highlightedSearchResultPosition = position
|
||||||
|
recyclerView.post {
|
||||||
|
(recyclerView.findViewHolderForAdapterPosition(position) as? SettingViewHolder)
|
||||||
|
?.highlightSearchResult()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun clearSearchResultHighlight() {
|
||||||
|
val recyclerView = binding?.listSettings
|
||||||
|
(recyclerView?.findViewHolderForAdapterPosition(
|
||||||
|
highlightedSearchResultPosition
|
||||||
|
) as? SettingViewHolder)?.clearSearchResultHighlight()
|
||||||
|
highlightedSearchResult = null
|
||||||
|
highlightedSearchResultPosition = RecyclerView.NO_POSITION
|
||||||
|
}
|
||||||
|
|
||||||
override fun showDialogFragment(fragment: DialogFragment) {
|
override fun showDialogFragment(fragment: DialogFragment) {
|
||||||
activityView!!.showDialogFragment(fragment)
|
activityView!!.showDialogFragment(fragment)
|
||||||
}
|
}
|
||||||
@@ -157,7 +268,11 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
|
|||||||
override val settings: Settings?
|
override val settings: Settings?
|
||||||
get() = presenter.settings
|
get() = presenter.settings
|
||||||
|
|
||||||
override fun onSettingChanged() {
|
override fun onSettingChanged(setting: SettingsItem?) {
|
||||||
|
if (setting == null || setting === highlightedSearchResult) {
|
||||||
|
clearSearchResultHighlight()
|
||||||
|
}
|
||||||
|
presenter.invalidateSearchIndex()
|
||||||
activityView!!.onSettingChanged()
|
activityView!!.onSettingChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,6 +289,10 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
|
|||||||
return activityView!!.hasMenuTagActionForValue(menuTag, value)
|
return activityView!!.hasMenuTagActionForValue(menuTag, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getMenuTagActionExtras(menuTag: MenuTag, value: Int): Bundle? {
|
||||||
|
return activityView!!.getMenuTagActionExtras(menuTag, value)
|
||||||
|
}
|
||||||
|
|
||||||
override var isMappingAllDevices: Boolean
|
override var isMappingAllDevices: Boolean
|
||||||
get() = activityView!!.isMappingAllDevices
|
get() = activityView!!.isMappingAllDevices
|
||||||
set(allDevices) {
|
set(allDevices) {
|
||||||
@@ -203,17 +322,13 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
|
|||||||
}
|
}
|
||||||
val msg = "${presenter.gpuDriver!!.name} ${presenter.gpuDriver!!.driverVersion}"
|
val msg = "${presenter.gpuDriver!!.name} ${presenter.gpuDriver!!.driverVersion}"
|
||||||
|
|
||||||
MaterialAlertDialogBuilder(requireContext())
|
MaterialAlertDialogBuilder(requireContext()).setTitle(getString(R.string.gpu_driver_dialog_title))
|
||||||
.setTitle(getString(R.string.gpu_driver_dialog_title))
|
.setMessage(msg).setNegativeButton(android.R.string.cancel, null)
|
||||||
.setMessage(msg)
|
|
||||||
.setNegativeButton(android.R.string.cancel, null)
|
|
||||||
.setNeutralButton(R.string.gpu_driver_dialog_system) { _: DialogInterface?, _: Int ->
|
.setNeutralButton(R.string.gpu_driver_dialog_system) { _: DialogInterface?, _: Int ->
|
||||||
presenter.useSystemDriver()
|
presenter.useSystemDriver()
|
||||||
}
|
}.setPositiveButton(R.string.gpu_driver_dialog_install) { _: DialogInterface?, _: Int ->
|
||||||
.setPositiveButton(R.string.gpu_driver_dialog_install) { _: DialogInterface?, _: Int ->
|
|
||||||
askForDriverFile()
|
askForDriverFile()
|
||||||
}
|
}.show()
|
||||||
.show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getFragmentLifecycle(): Lifecycle {
|
override fun getFragmentLifecycle(): Lifecycle {
|
||||||
@@ -230,16 +345,12 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
|
|||||||
|
|
||||||
override fun onDriverInstallDone(result: GpuDriverInstallResult) {
|
override fun onDriverInstallDone(result: GpuDriverInstallResult) {
|
||||||
val view = binding?.root ?: return
|
val view = binding?.root ?: return
|
||||||
Snackbar
|
Snackbar.make(view, resolveInstallResultString(result), Snackbar.LENGTH_LONG).show()
|
||||||
.make(view, resolveInstallResultString(result), Snackbar.LENGTH_LONG)
|
|
||||||
.show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDriverUninstallDone() {
|
override fun onDriverUninstallDone() {
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
requireContext(),
|
requireContext(), R.string.gpu_driver_dialog_uninstall_done, Toast.LENGTH_SHORT
|
||||||
R.string.gpu_driver_dialog_uninstall_done,
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,6 +367,8 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
|
|||||||
companion object {
|
companion object {
|
||||||
private const val ARGUMENT_MENU_TAG = "menu_tag"
|
private const val ARGUMENT_MENU_TAG = "menu_tag"
|
||||||
private const val ARGUMENT_GAME_ID = "game_id"
|
private const val ARGUMENT_GAME_ID = "game_id"
|
||||||
|
const val ARGUMENT_SCROLL_TO_SETTING_POSITION = "scroll_to_setting_position"
|
||||||
|
private const val SEARCH_QUERY_DEBOUNCE_MS = 120L
|
||||||
private val titles: MutableMap<MenuTag, Int> = EnumMap(MenuTag::class.java)
|
private val titles: MutableMap<MenuTag, Int> = EnumMap(MenuTag::class.java)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
|||||||
+410
-359
File diff suppressed because it is too large
Load Diff
+13
-1
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
package org.dolphinemu.dolphinemu.features.settings.ui
|
package org.dolphinemu.dolphinemu.features.settings.ui
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
@@ -50,6 +51,12 @@ interface SettingsFragmentView {
|
|||||||
* @param menuKey Identifier for the settings group that should be shown.
|
* @param menuKey Identifier for the settings group that should be shown.
|
||||||
*/
|
*/
|
||||||
fun loadSubMenu(menuKey: MenuTag)
|
fun loadSubMenu(menuKey: MenuTag)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the settings screen containing a search result and scrolls to the result.
|
||||||
|
*/
|
||||||
|
fun loadSearchResult(menuKey: MenuTag, settingPosition: Int, extras: Bundle?)
|
||||||
|
|
||||||
fun showDialogFragment(fragment: DialogFragment)
|
fun showDialogFragment(fragment: DialogFragment)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -67,7 +74,7 @@ interface SettingsFragmentView {
|
|||||||
/**
|
/**
|
||||||
* Have the fragment tell the containing Activity that a Setting was modified.
|
* Have the fragment tell the containing Activity that a Setting was modified.
|
||||||
*/
|
*/
|
||||||
fun onSettingChanged()
|
fun onSettingChanged(setting: SettingsItem? = null)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refetches the values of all controller settings.
|
* Refetches the values of all controller settings.
|
||||||
@@ -95,6 +102,11 @@ interface SettingsFragmentView {
|
|||||||
*/
|
*/
|
||||||
fun hasMenuTagActionForValue(menuTag: MenuTag, value: Int): Boolean
|
fun hasMenuTagActionForValue(menuTag: MenuTag, value: Int): Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the arguments used when opening a navigable setting's associated screen.
|
||||||
|
*/
|
||||||
|
fun getMenuTagActionExtras(menuTag: MenuTag, value: Int): Bundle?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controls whether the input mapping dialog should detect inputs from all devices,
|
* Controls whether the input mapping dialog should detect inputs from all devices,
|
||||||
* not just the device configured for the controller.
|
* not just the device configured for the controller.
|
||||||
|
|||||||
+43
@@ -2,14 +2,20 @@
|
|||||||
|
|
||||||
package org.dolphinemu.dolphinemu.features.settings.ui.viewholder
|
package org.dolphinemu.dolphinemu.features.settings.ui.viewholder
|
||||||
|
|
||||||
|
import android.animation.ValueAnimator
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
import android.graphics.Paint
|
import android.graphics.Paint
|
||||||
import android.graphics.Typeface
|
import android.graphics.Typeface
|
||||||
|
import android.graphics.drawable.ColorDrawable
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.graphics.drawable.LayerDrawable
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.View.OnLongClickListener
|
import android.view.View.OnLongClickListener
|
||||||
|
import android.view.animation.DecelerateInterpolator
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
|
import com.google.android.material.color.MaterialColors
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import org.dolphinemu.dolphinemu.DolphinApplication
|
import org.dolphinemu.dolphinemu.DolphinApplication
|
||||||
import org.dolphinemu.dolphinemu.R
|
import org.dolphinemu.dolphinemu.R
|
||||||
@@ -21,6 +27,9 @@ abstract class SettingViewHolder(itemView: View, protected val adapter: Settings
|
|||||||
LifecycleViewHolder(itemView, adapter.getFragmentLifecycle()),
|
LifecycleViewHolder(itemView, adapter.getFragmentLifecycle()),
|
||||||
LifecycleOwner, View.OnClickListener, OnLongClickListener {
|
LifecycleOwner, View.OnClickListener, OnLongClickListener {
|
||||||
|
|
||||||
|
private val defaultBackground: Drawable? = itemView.background
|
||||||
|
private var searchResultHighlightAnimator: ValueAnimator? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
itemView.setOnClickListener(this)
|
itemView.setOnClickListener(this)
|
||||||
itemView.setOnLongClickListener(this)
|
itemView.setOnLongClickListener(this)
|
||||||
@@ -39,6 +48,35 @@ abstract class SettingViewHolder(itemView: View, protected val adapter: Settings
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun highlightSearchResult() {
|
||||||
|
clearSearchResultHighlight()
|
||||||
|
|
||||||
|
val highlight = ColorDrawable(
|
||||||
|
MaterialColors.getColor(
|
||||||
|
itemView, com.google.android.material.R.attr.colorSecondaryContainer
|
||||||
|
)
|
||||||
|
).apply { alpha = 0 }
|
||||||
|
itemView.background = if (defaultBackground == null) {
|
||||||
|
highlight
|
||||||
|
} else {
|
||||||
|
LayerDrawable(arrayOf(highlight, defaultBackground))
|
||||||
|
}
|
||||||
|
searchResultHighlightAnimator = ValueAnimator.ofInt(
|
||||||
|
0, SEARCH_RESULT_HIGHLIGHT_MAX_ALPHA
|
||||||
|
).apply {
|
||||||
|
duration = SEARCH_RESULT_HIGHLIGHT_FADE_IN_DURATION_MS
|
||||||
|
interpolator = DecelerateInterpolator()
|
||||||
|
addUpdateListener { highlight.alpha = it.animatedValue as Int }
|
||||||
|
start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clearSearchResultHighlight() {
|
||||||
|
searchResultHighlightAnimator?.cancel()
|
||||||
|
searchResultHighlightAnimator = null
|
||||||
|
itemView.background = defaultBackground
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by the adapter to set this ViewHolder's child views to display the list item
|
* Called by the adapter to set this ViewHolder's child views to display the list item
|
||||||
* it must now represent.
|
* it must now represent.
|
||||||
@@ -102,4 +140,9 @@ abstract class SettingViewHolder(itemView: View, protected val adapter: Settings
|
|||||||
Toast.LENGTH_SHORT
|
Toast.LENGTH_SHORT
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val SEARCH_RESULT_HIGHLIGHT_FADE_IN_DURATION_MS = 180L
|
||||||
|
private const val SEARCH_RESULT_HIGHLIGHT_MAX_ALPHA = 255
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
package org.dolphinemu.dolphinemu.features.settings.ui.viewholder
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import org.dolphinemu.dolphinemu.databinding.ListItemSearchResultBinding
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsSearchResult
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter
|
||||||
|
|
||||||
|
class SettingsSearchResultViewHolder(
|
||||||
|
private val binding: ListItemSearchResultBinding, adapter: SettingsAdapter
|
||||||
|
) : SettingViewHolder(binding.root, adapter) {
|
||||||
|
private lateinit var result: SettingsSearchResult
|
||||||
|
|
||||||
|
override val item: SettingsItem
|
||||||
|
get() = result
|
||||||
|
|
||||||
|
override fun bind(item: SettingsItem) {
|
||||||
|
result = item as SettingsSearchResult
|
||||||
|
binding.textSettingName.text = item.name
|
||||||
|
binding.textSettingDescription.text = item.description
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClick(clicked: View) {
|
||||||
|
adapter.onSearchResultClick(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,20 +1,20 @@
|
|||||||
package org.dolphinemu.dolphinemu.utils
|
package org.dolphinemu.dolphinemu.utils
|
||||||
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import org.dolphinemu.dolphinemu.R
|
|
||||||
import android.os.Build
|
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
|
||||||
import androidx.core.view.WindowInsetsControllerCompat
|
|
||||||
import androidx.core.view.WindowCompat
|
|
||||||
import org.dolphinemu.dolphinemu.ui.main.ThemeProvider
|
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import com.google.android.material.appbar.MaterialToolbar
|
import android.os.Build
|
||||||
import com.google.android.material.appbar.AppBarLayout
|
|
||||||
import com.google.android.material.elevation.ElevationOverlayProvider
|
|
||||||
import com.google.android.material.color.MaterialColors
|
|
||||||
import androidx.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.view.WindowCompat
|
||||||
|
import androidx.core.view.WindowInsetsControllerCompat
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
|
import com.google.android.material.appbar.AppBarLayout
|
||||||
|
import com.google.android.material.appbar.MaterialToolbar
|
||||||
|
import com.google.android.material.color.MaterialColors
|
||||||
|
import com.google.android.material.elevation.ElevationOverlayProvider
|
||||||
|
import org.dolphinemu.dolphinemu.R
|
||||||
|
import org.dolphinemu.dolphinemu.ui.main.ThemeProvider
|
||||||
|
|
||||||
object ThemeHelper {
|
object ThemeHelper {
|
||||||
|
|
||||||
@@ -52,8 +52,7 @@ object ThemeHelper {
|
|||||||
.getInt(CURRENT_THEME_MODE, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
.getInt(CURRENT_THEME_MODE, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
||||||
activity.delegate.localNightMode = themeMode
|
activity.delegate.localNightMode = themeMode
|
||||||
val windowController = WindowCompat.getInsetsController(
|
val windowController = WindowCompat.getInsetsController(
|
||||||
activity.window,
|
activity.window, activity.window.decorView
|
||||||
activity.window.decorView
|
|
||||||
)
|
)
|
||||||
val systemReportedThemeMode =
|
val systemReportedThemeMode =
|
||||||
activity.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
|
activity.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
|
||||||
@@ -62,6 +61,7 @@ object ThemeHelper {
|
|||||||
Configuration.UI_MODE_NIGHT_NO -> setLightModeSystemBars(windowController)
|
Configuration.UI_MODE_NIGHT_NO -> setLightModeSystemBars(windowController)
|
||||||
Configuration.UI_MODE_NIGHT_YES -> setDarkModeSystemBars(windowController)
|
Configuration.UI_MODE_NIGHT_YES -> setDarkModeSystemBars(windowController)
|
||||||
}
|
}
|
||||||
|
|
||||||
AppCompatDelegate.MODE_NIGHT_NO -> setLightModeSystemBars(windowController)
|
AppCompatDelegate.MODE_NIGHT_NO -> setLightModeSystemBars(windowController)
|
||||||
AppCompatDelegate.MODE_NIGHT_YES -> setDarkModeSystemBars(windowController)
|
AppCompatDelegate.MODE_NIGHT_YES -> setDarkModeSystemBars(windowController)
|
||||||
}
|
}
|
||||||
@@ -83,66 +83,50 @@ object ThemeHelper {
|
|||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun saveTheme(activity: AppCompatActivity, themeValue: Int) {
|
fun saveTheme(activity: AppCompatActivity, themeValue: Int) {
|
||||||
PreferenceManager.getDefaultSharedPreferences(activity.applicationContext)
|
PreferenceManager.getDefaultSharedPreferences(activity.applicationContext).edit()
|
||||||
.edit()
|
.putInt(CURRENT_THEME, themeValue).apply()
|
||||||
.putInt(CURRENT_THEME, themeValue)
|
|
||||||
.apply()
|
|
||||||
activity.recreate()
|
activity.recreate()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun deleteThemeKey(activity: AppCompatActivity) {
|
fun deleteThemeKey(activity: AppCompatActivity) {
|
||||||
PreferenceManager.getDefaultSharedPreferences(activity.applicationContext)
|
PreferenceManager.getDefaultSharedPreferences(activity.applicationContext).edit()
|
||||||
.edit()
|
.remove(CURRENT_THEME).apply()
|
||||||
.remove(CURRENT_THEME)
|
|
||||||
.apply()
|
|
||||||
activity.recreate()
|
activity.recreate()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun saveThemeMode(activity: AppCompatActivity, themeModeValue: Int) {
|
fun saveThemeMode(activity: AppCompatActivity, themeModeValue: Int) {
|
||||||
PreferenceManager.getDefaultSharedPreferences(activity.applicationContext)
|
PreferenceManager.getDefaultSharedPreferences(activity.applicationContext).edit()
|
||||||
.edit()
|
.putInt(CURRENT_THEME_MODE, themeModeValue).apply()
|
||||||
.putInt(CURRENT_THEME_MODE, themeModeValue)
|
|
||||||
.apply()
|
|
||||||
setThemeMode(activity)
|
setThemeMode(activity)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun deleteThemeModeKey(activity: AppCompatActivity) {
|
fun deleteThemeModeKey(activity: AppCompatActivity) {
|
||||||
PreferenceManager.getDefaultSharedPreferences(activity.applicationContext)
|
PreferenceManager.getDefaultSharedPreferences(activity.applicationContext).edit()
|
||||||
.edit()
|
.remove(CURRENT_THEME_MODE).apply()
|
||||||
.remove(CURRENT_THEME_MODE)
|
|
||||||
.apply()
|
|
||||||
setThemeMode(activity)
|
setThemeMode(activity)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun saveBackgroundSetting(activity: AppCompatActivity, backgroundValue: Boolean) {
|
fun saveBackgroundSetting(activity: AppCompatActivity, backgroundValue: Boolean) {
|
||||||
PreferenceManager.getDefaultSharedPreferences(activity.applicationContext)
|
PreferenceManager.getDefaultSharedPreferences(activity.applicationContext).edit()
|
||||||
.edit()
|
.putBoolean(USE_BLACK_BACKGROUNDS, backgroundValue).apply()
|
||||||
.putBoolean(USE_BLACK_BACKGROUNDS, backgroundValue)
|
|
||||||
.apply()
|
|
||||||
activity.recreate()
|
activity.recreate()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun deleteBackgroundSetting(activity: AppCompatActivity) {
|
fun deleteBackgroundSetting(activity: AppCompatActivity) {
|
||||||
PreferenceManager.getDefaultSharedPreferences(activity.applicationContext)
|
PreferenceManager.getDefaultSharedPreferences(activity.applicationContext).edit()
|
||||||
.edit()
|
.remove(USE_BLACK_BACKGROUNDS).apply()
|
||||||
.remove(USE_BLACK_BACKGROUNDS)
|
|
||||||
.apply()
|
|
||||||
activity.recreate()
|
activity.recreate()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun resetThemePreferences(activity: AppCompatActivity, applyImmediately: Boolean = false) {
|
fun resetThemePreferences(activity: AppCompatActivity, applyImmediately: Boolean = false) {
|
||||||
PreferenceManager.getDefaultSharedPreferences(activity.applicationContext)
|
PreferenceManager.getDefaultSharedPreferences(activity.applicationContext).edit()
|
||||||
.edit()
|
.remove(CURRENT_THEME).remove(CURRENT_THEME_MODE).remove(USE_BLACK_BACKGROUNDS).apply()
|
||||||
.remove(CURRENT_THEME)
|
|
||||||
.remove(CURRENT_THEME_MODE)
|
|
||||||
.remove(USE_BLACK_BACKGROUNDS)
|
|
||||||
.apply()
|
|
||||||
activity.delegate.localNightMode = AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
activity.delegate.localNightMode = AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
||||||
activity.delegate.applyDayNight()
|
activity.delegate.applyDayNight()
|
||||||
if (applyImmediately) {
|
if (applyImmediately) {
|
||||||
@@ -170,7 +154,7 @@ object ThemeHelper {
|
|||||||
activity: AppCompatActivity, toolbar: MaterialToolbar, appBarLayout: AppBarLayout
|
activity: AppCompatActivity, toolbar: MaterialToolbar, appBarLayout: AppBarLayout
|
||||||
) {
|
) {
|
||||||
appBarLayout.addOnOffsetChangedListener { layout: AppBarLayout, verticalOffset: Int ->
|
appBarLayout.addOnOffsetChangedListener { layout: AppBarLayout, verticalOffset: Int ->
|
||||||
if (-verticalOffset >= layout.totalScrollRange / 2) {
|
if (layout.totalScrollRange > 0 && -verticalOffset >= layout.totalScrollRange / 2) {
|
||||||
@ColorInt val color =
|
@ColorInt val color =
|
||||||
ElevationOverlayProvider(appBarLayout.context).compositeOverlay(
|
ElevationOverlayProvider(appBarLayout.context).compositeOverlay(
|
||||||
MaterialColors.getColor(appBarLayout, R.attr.colorSurface),
|
MaterialColors.getColor(appBarLayout, R.attr.colorSurface),
|
||||||
@@ -180,8 +164,7 @@ object ThemeHelper {
|
|||||||
setStatusBarColor(activity, color)
|
setStatusBarColor(activity, color)
|
||||||
} else {
|
} else {
|
||||||
@ColorInt val statusBarColor = ContextCompat.getColor(
|
@ColorInt val statusBarColor = ContextCompat.getColor(
|
||||||
activity.applicationContext,
|
activity.applicationContext, android.R.color.transparent
|
||||||
android.R.color.transparent
|
|
||||||
)
|
)
|
||||||
@ColorInt val appBarColor = MaterialColors.getColor(toolbar, R.attr.colorSurface)
|
@ColorInt val appBarColor = MaterialColors.getColor(toolbar, R.attr.colorSurface)
|
||||||
toolbar.setBackgroundColor(appBarColor)
|
toolbar.setBackgroundColor(appBarColor)
|
||||||
@@ -198,8 +181,7 @@ object ThemeHelper {
|
|||||||
setStatusBarColor(activity, color)
|
setStatusBarColor(activity, color)
|
||||||
} else {
|
} else {
|
||||||
@ColorInt val statusBarColor = ContextCompat.getColor(
|
@ColorInt val statusBarColor = ContextCompat.getColor(
|
||||||
activity.applicationContext,
|
activity.applicationContext, android.R.color.transparent
|
||||||
android.R.color.transparent
|
|
||||||
)
|
)
|
||||||
setStatusBarColor(activity, statusBarColor)
|
setStatusBarColor(activity, statusBarColor)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:duration="180"
|
||||||
|
android:fromAlpha="0"
|
||||||
|
android:interpolator="@android:anim/decelerate_interpolator"
|
||||||
|
android:startOffset="60"
|
||||||
|
android:toAlpha="1" />
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:duration="90"
|
||||||
|
android:fromAlpha="1"
|
||||||
|
android:interpolator="@android:anim/accelerate_interpolator"
|
||||||
|
android:toAlpha="0" />
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:alpha="0.45" android:color="?attr/colorOutline" />
|
||||||
|
</selector>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M9.5,3a6.5,6.5 0,1 0,0 13a6.5,6.5 0,0 0,0 -13zM9.5,5a4.5,4.5 0,1 1,0 9a4.5,4.5 0,0 1,0 -9zM14.65,13.24l5.56,5.56l-1.41,1.41l-5.56,-5.56z" />
|
||||||
|
</vector>
|
||||||
@@ -1,22 +1,50 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
<androidx.coordinatorlayout.widget.CoordinatorLayout android:id="@+id/coordinator_main"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:id="@+id/coordinator_main"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="?attr/colorSurface">
|
android:background="?attr/colorSurface">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/frame_content_settings"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/old_controller_settings_warning"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="bottom"
|
||||||
|
android:background="?attr/colorErrorContainer"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="false"
|
||||||
|
android:text="@string/old_controller_settings"
|
||||||
|
android:textColor="?attr/colorOnErrorContainer"
|
||||||
|
android:visibility="invisible" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/workaround_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_gravity="bottom"
|
||||||
|
android:background="@android:color/transparent"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="false" />
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:id="@+id/appbar_settings"
|
android:id="@+id/appbar_settings"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentTop="true"
|
android:layout_alignParentTop="true"
|
||||||
|
android:background="@android:color/transparent"
|
||||||
|
app:backgroundTint="@android:color/transparent"
|
||||||
app:elevation="0dp">
|
app:elevation="0dp">
|
||||||
|
|
||||||
<com.google.android.material.appbar.CollapsingToolbarLayout
|
<com.google.android.material.appbar.CollapsingToolbarLayout
|
||||||
style="?attr/collapsingToolbarLayoutMediumStyle"
|
|
||||||
android:id="@+id/toolbar_settings_layout"
|
android:id="@+id/toolbar_settings_layout"
|
||||||
|
style="?attr/collapsingToolbarLayoutMediumStyle"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/collapsingToolbarLayoutMediumSize"
|
android:layout_height="?attr/collapsingToolbarLayoutMediumSize"
|
||||||
app:contentScrim="@android:color/transparent"
|
app:contentScrim="@android:color/transparent"
|
||||||
@@ -31,33 +59,94 @@
|
|||||||
|
|
||||||
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/settings_search_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/colorSurface"
|
||||||
|
android:paddingBottom="@dimen/spacing_medlarge">
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/settings_search_preview"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:foreground="?android:attr/selectableItemBackground"
|
||||||
|
android:layout_marginEnd="@dimen/spacing_large"
|
||||||
|
android:layout_marginStart="@dimen/spacing_large"
|
||||||
|
app:cardBackgroundColor="?attr/colorSurfaceVariant"
|
||||||
|
app:cardCornerRadius="28dp"
|
||||||
|
app:cardElevation="0dp"
|
||||||
|
app:strokeColor="@color/settings_search_outline"
|
||||||
|
app:strokeWidth="1dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingEnd="@dimen/spacing_large"
|
||||||
|
android:paddingStart="20dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:contentDescription="@null"
|
||||||
|
app:srcCompat="@drawable/ic_search"
|
||||||
|
app:tint="?android:attr/textColorSecondary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="@dimen/spacing_large"
|
||||||
|
android:text="@string/search_settings"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
android:textSize="18sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/settings_search_mode_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/colorSurface"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="gone">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
|
android:id="@+id/settings_search_toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:background="?attr/colorSurface"
|
||||||
|
app:navigationContentDescription="@string/search_settings_back"
|
||||||
|
app:navigationIcon="?attr/homeAsUpIndicator">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.SearchView
|
||||||
|
android:id="@+id/settings_search"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@android:color/transparent"
|
||||||
|
android:imeOptions="actionSearch"
|
||||||
|
android:inputType="text"
|
||||||
|
app:iconifiedByDefault="false"
|
||||||
|
app:queryBackground="@android:color/transparent"
|
||||||
|
app:queryHint="@string/search_settings"
|
||||||
|
app:searchIcon="@null" />
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.MaterialToolbar>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="@color/settings_search_outline" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
<FrameLayout
|
|
||||||
android:id="@+id/frame_content_settings"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/old_controller_settings_warning"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="bottom"
|
|
||||||
android:background="?attr/colorErrorContainer"
|
|
||||||
android:text="@string/old_controller_settings"
|
|
||||||
android:textColor="?attr/colorOnErrorContainer"
|
|
||||||
android:visibility="invisible"
|
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="false" />
|
|
||||||
|
|
||||||
<View
|
|
||||||
android:id="@+id/workaround_view"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_gravity="bottom"
|
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="false"
|
|
||||||
android:background="@android:color/transparent" />
|
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<FrameLayout
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
@@ -11,4 +10,16 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:clipToPadding="false" />
|
android:clipToPadding="false" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text_no_search_results"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center"
|
||||||
|
android:padding="@dimen/spacing_xtralarge"
|
||||||
|
android:text="@string/search_settings_no_results"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?android:attr/selectableItemBackground"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:minHeight="64dp"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingBottom="@dimen/spacing_large"
|
||||||
|
android:paddingEnd="@dimen/spacing_large"
|
||||||
|
android:paddingStart="@dimen/spacing_large"
|
||||||
|
android:paddingTop="@dimen/spacing_large">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text_setting_name"
|
||||||
|
style="@style/TextAppearance.MaterialComponents.Headline5"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAlignment="viewStart"
|
||||||
|
android:textSize="16sp"
|
||||||
|
tools:text="Internal Resolution" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text_setting_description"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/spacing_small"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textAlignment="viewStart"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
tools:text="Graphics Settings › Enhancements" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@@ -62,6 +62,10 @@
|
|||||||
|
|
||||||
<!-- Main Preference Fragment -->
|
<!-- Main Preference Fragment -->
|
||||||
<string name="settings">Settings</string>
|
<string name="settings">Settings</string>
|
||||||
|
<string name="search_settings">Search settings</string>
|
||||||
|
<string name="search_settings_back">Back to settings</string>
|
||||||
|
<string name="search_settings_no_results">No settings found for “%1$s”</string>
|
||||||
|
<string name="search_settings_category_path">%1$s › %2$s</string>
|
||||||
<string name="game_settings">Game Settings: %1$s</string>
|
<string name="game_settings">Game Settings: %1$s</string>
|
||||||
<string name="config">Config</string>
|
<string name="config">Config</string>
|
||||||
<string name="graphics_settings">Graphics Settings</string>
|
<string name="graphics_settings">Graphics Settings</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user