diff options
8 files changed, 372 insertions, 1 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt index ff1e64e0a..f5eba1222 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt | |||
| @@ -12,6 +12,7 @@ import android.view.LayoutInflater | |||
| 12 | import android.view.ViewGroup | 12 | import android.view.ViewGroup |
| 13 | import android.widget.TextView | 13 | import android.widget.TextView |
| 14 | import androidx.appcompat.app.AlertDialog | 14 | import androidx.appcompat.app.AlertDialog |
| 15 | import androidx.fragment.app.Fragment | ||
| 15 | import androidx.lifecycle.ViewModelProvider | 16 | import androidx.lifecycle.ViewModelProvider |
| 16 | import androidx.navigation.findNavController | 17 | import androidx.navigation.findNavController |
| 17 | import androidx.recyclerview.widget.AsyncDifferConfig | 18 | import androidx.recyclerview.widget.AsyncDifferConfig |
| @@ -37,7 +38,7 @@ import org.yuzu.yuzu_emu.features.settings.ui.viewholder.* | |||
| 37 | import org.yuzu.yuzu_emu.model.SettingsViewModel | 38 | import org.yuzu.yuzu_emu.model.SettingsViewModel |
| 38 | 39 | ||
| 39 | class SettingsAdapter( | 40 | class SettingsAdapter( |
| 40 | private val fragment: SettingsFragment, | 41 | private val fragment: Fragment, |
| 41 | private val context: Context | 42 | private val context: Context |
| 42 | ) : ListAdapter<SettingsItem, SettingViewHolder>(AsyncDifferConfig.Builder(DiffCallback()).build()), | 43 | ) : ListAdapter<SettingsItem, SettingViewHolder>(AsyncDifferConfig.Builder(DiffCallback()).build()), |
| 43 | DialogInterface.OnClickListener { | 44 | DialogInterface.OnClickListener { |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt index 5890b0642..0ea587a88 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt | |||
| @@ -13,12 +13,14 @@ import androidx.core.view.WindowInsetsCompat | |||
| 13 | import androidx.core.view.updatePadding | 13 | import androidx.core.view.updatePadding |
| 14 | import androidx.fragment.app.Fragment | 14 | import androidx.fragment.app.Fragment |
| 15 | import androidx.fragment.app.activityViewModels | 15 | import androidx.fragment.app.activityViewModels |
| 16 | import androidx.navigation.findNavController | ||
| 16 | import androidx.navigation.fragment.navArgs | 17 | import androidx.navigation.fragment.navArgs |
| 17 | import androidx.recyclerview.widget.LinearLayoutManager | 18 | import androidx.recyclerview.widget.LinearLayoutManager |
| 18 | import com.google.android.material.divider.MaterialDividerItemDecoration | 19 | import com.google.android.material.divider.MaterialDividerItemDecoration |
| 19 | import com.google.android.material.transition.MaterialSharedAxis | 20 | import com.google.android.material.transition.MaterialSharedAxis |
| 20 | import org.yuzu.yuzu_emu.R | 21 | import org.yuzu.yuzu_emu.R |
| 21 | import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding | 22 | import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding |
| 23 | import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile | ||
| 22 | import org.yuzu.yuzu_emu.model.SettingsViewModel | 24 | import org.yuzu.yuzu_emu.model.SettingsViewModel |
| 23 | 25 | ||
| 24 | class SettingsFragment : Fragment() { | 26 | class SettingsFragment : Fragment() { |
| @@ -84,11 +86,43 @@ class SettingsFragment : Fragment() { | |||
| 84 | } | 86 | } |
| 85 | } | 87 | } |
| 86 | 88 | ||
| 89 | settingsViewModel.isUsingSearch.observe(viewLifecycleOwner) { | ||
| 90 | if (it) { | ||
| 91 | reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true) | ||
| 92 | exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false) | ||
| 93 | } else { | ||
| 94 | reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false) | ||
| 95 | exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | if (args.menuTag == SettingsFile.FILE_NAME_CONFIG) { | ||
| 100 | binding.toolbarSettings.inflateMenu(R.menu.menu_settings) | ||
| 101 | binding.toolbarSettings.setOnMenuItemClickListener { | ||
| 102 | when (it.itemId) { | ||
| 103 | R.id.action_search -> { | ||
| 104 | reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true) | ||
| 105 | exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false) | ||
| 106 | view.findNavController() | ||
| 107 | .navigate(R.id.action_settingsFragment_to_settingsSearchFragment) | ||
| 108 | true | ||
| 109 | } | ||
| 110 | |||
| 111 | else -> false | ||
| 112 | } | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 87 | presenter.onViewCreated() | 116 | presenter.onViewCreated() |
| 88 | 117 | ||
| 89 | setInsets() | 118 | setInsets() |
| 90 | } | 119 | } |
| 91 | 120 | ||
| 121 | override fun onResume() { | ||
| 122 | super.onResume() | ||
| 123 | settingsViewModel.setIsUsingSearch(false) | ||
| 124 | } | ||
| 125 | |||
| 92 | override fun onDetach() { | 126 | override fun onDetach() { |
| 93 | super.onDetach() | 127 | super.onDetach() |
| 94 | settingsAdapter?.closeDialog() | 128 | settingsAdapter?.closeDialog() |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt new file mode 100644 index 000000000..4f93db4ad --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt | |||
| @@ -0,0 +1,189 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | package org.yuzu.yuzu_emu.fragments | ||
| 5 | |||
| 6 | import android.content.Context | ||
| 7 | import android.os.Bundle | ||
| 8 | import android.view.LayoutInflater | ||
| 9 | import android.view.View | ||
| 10 | import android.view.ViewGroup | ||
| 11 | import android.view.inputmethod.InputMethodManager | ||
| 12 | import androidx.core.view.ViewCompat | ||
| 13 | import androidx.core.view.WindowInsetsCompat | ||
| 14 | import androidx.core.view.updatePadding | ||
| 15 | import androidx.core.widget.doOnTextChanged | ||
| 16 | import androidx.fragment.app.Fragment | ||
| 17 | import androidx.fragment.app.activityViewModels | ||
| 18 | import androidx.recyclerview.widget.LinearLayoutManager | ||
| 19 | import com.google.android.material.divider.MaterialDividerItemDecoration | ||
| 20 | import com.google.android.material.transition.MaterialSharedAxis | ||
| 21 | import info.debatty.java.stringsimilarity.Cosine | ||
| 22 | import org.yuzu.yuzu_emu.R | ||
| 23 | import org.yuzu.yuzu_emu.databinding.FragmentSettingsSearchBinding | ||
| 24 | import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem | ||
| 25 | import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter | ||
| 26 | import org.yuzu.yuzu_emu.model.SettingsViewModel | ||
| 27 | import org.yuzu.yuzu_emu.utils.NativeConfig | ||
| 28 | |||
| 29 | class SettingsSearchFragment : Fragment() { | ||
| 30 | private var _binding: FragmentSettingsSearchBinding? = null | ||
| 31 | private val binding get() = _binding!! | ||
| 32 | |||
| 33 | private var settingsAdapter: SettingsAdapter? = null | ||
| 34 | |||
| 35 | private val settingsViewModel: SettingsViewModel by activityViewModels() | ||
| 36 | |||
| 37 | override fun onCreate(savedInstanceState: Bundle?) { | ||
| 38 | super.onCreate(savedInstanceState) | ||
| 39 | enterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false) | ||
| 40 | returnTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true) | ||
| 41 | } | ||
| 42 | |||
| 43 | override fun onCreateView( | ||
| 44 | inflater: LayoutInflater, | ||
| 45 | container: ViewGroup?, | ||
| 46 | savedInstanceState: Bundle? | ||
| 47 | ): View { | ||
| 48 | _binding = FragmentSettingsSearchBinding.inflate(layoutInflater) | ||
| 49 | return binding.root | ||
| 50 | } | ||
| 51 | |||
| 52 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||
| 53 | super.onViewCreated(view, savedInstanceState) | ||
| 54 | settingsViewModel.setIsUsingSearch(true) | ||
| 55 | |||
| 56 | if (savedInstanceState != null) { | ||
| 57 | binding.searchText.setText(savedInstanceState.getString(SEARCH_TEXT)) | ||
| 58 | } | ||
| 59 | |||
| 60 | settingsAdapter = SettingsAdapter(this, requireContext()) | ||
| 61 | |||
| 62 | val dividerDecoration = MaterialDividerItemDecoration( | ||
| 63 | requireContext(), | ||
| 64 | LinearLayoutManager.VERTICAL | ||
| 65 | ) | ||
| 66 | dividerDecoration.isLastItemDecorated = false | ||
| 67 | binding.settingsList.apply { | ||
| 68 | adapter = settingsAdapter | ||
| 69 | layoutManager = LinearLayoutManager(requireContext()) | ||
| 70 | addItemDecoration(dividerDecoration) | ||
| 71 | } | ||
| 72 | |||
| 73 | focusSearch() | ||
| 74 | |||
| 75 | binding.backButton.setOnClickListener { settingsViewModel.setShouldNavigateBack(true) } | ||
| 76 | binding.searchBackground.setOnClickListener { focusSearch() } | ||
| 77 | binding.clearButton.setOnClickListener { binding.searchText.setText("") } | ||
| 78 | binding.searchText.doOnTextChanged { _, _, _, _ -> | ||
| 79 | search() | ||
| 80 | binding.settingsList.smoothScrollToPosition(0) | ||
| 81 | } | ||
| 82 | settingsViewModel.shouldReloadSettingsList.observe(viewLifecycleOwner) { | ||
| 83 | if (it) { | ||
| 84 | settingsViewModel.setShouldReloadSettingsList(false) | ||
| 85 | search() | ||
| 86 | } | ||
| 87 | } | ||
| 88 | |||
| 89 | search() | ||
| 90 | |||
| 91 | setInsets() | ||
| 92 | } | ||
| 93 | |||
| 94 | override fun onDetach() { | ||
| 95 | super.onDetach() | ||
| 96 | settingsAdapter?.closeDialog() | ||
| 97 | } | ||
| 98 | |||
| 99 | override fun onSaveInstanceState(outState: Bundle) { | ||
| 100 | super.onSaveInstanceState(outState) | ||
| 101 | outState.putString(SEARCH_TEXT, binding.searchText.text.toString()) | ||
| 102 | } | ||
| 103 | |||
| 104 | private fun search() { | ||
| 105 | val searchTerm = binding.searchText.text.toString().lowercase() | ||
| 106 | binding.clearButton.visibility = | ||
| 107 | if (searchTerm.isEmpty()) View.INVISIBLE else View.VISIBLE | ||
| 108 | if (searchTerm.isEmpty()) { | ||
| 109 | binding.noResultsView.visibility = View.VISIBLE | ||
| 110 | settingsAdapter?.submitList(emptyList()) | ||
| 111 | return | ||
| 112 | } | ||
| 113 | |||
| 114 | val baseList = SettingsItem.settingsItems | ||
| 115 | val similarityAlgorithm = if (searchTerm.length > 2) Cosine() else Cosine(1) | ||
| 116 | val sortedList: List<SettingsItem> = baseList.mapNotNull { item -> | ||
| 117 | val title = getString(item.value.nameId).lowercase() | ||
| 118 | val similarity = similarityAlgorithm.similarity(searchTerm, title) | ||
| 119 | if (similarity > 0.08) { | ||
| 120 | Pair(similarity, item) | ||
| 121 | } else { | ||
| 122 | null | ||
| 123 | } | ||
| 124 | }.sortedByDescending { it.first }.mapNotNull { | ||
| 125 | val item = it.second.value | ||
| 126 | val pairedSettingKey = item.setting.pairedSettingKey | ||
| 127 | val optionalSetting: SettingsItem? = if (pairedSettingKey.isNotEmpty()) { | ||
| 128 | val pairedSettingValue = NativeConfig.getBoolean(pairedSettingKey, false) | ||
| 129 | if (pairedSettingValue) it.second.value else null | ||
| 130 | } else { | ||
| 131 | it.second.value | ||
| 132 | } | ||
| 133 | optionalSetting | ||
| 134 | } | ||
| 135 | settingsAdapter?.submitList(sortedList) | ||
| 136 | binding.noResultsView.visibility = | ||
| 137 | if (sortedList.isEmpty()) View.VISIBLE else View.INVISIBLE | ||
| 138 | } | ||
| 139 | |||
| 140 | private fun focusSearch() { | ||
| 141 | binding.searchText.requestFocus() | ||
| 142 | val imm = requireActivity() | ||
| 143 | .getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager? | ||
| 144 | imm?.showSoftInput(binding.searchText, InputMethodManager.SHOW_IMPLICIT) | ||
| 145 | } | ||
| 146 | |||
| 147 | private fun setInsets() = | ||
| 148 | ViewCompat.setOnApplyWindowInsetsListener( | ||
| 149 | binding.root | ||
| 150 | ) { _: View, windowInsets: WindowInsetsCompat -> | ||
| 151 | val extraListSpacing = resources.getDimensionPixelSize(R.dimen.spacing_med) | ||
| 152 | val sideMargin = resources.getDimensionPixelSize(R.dimen.spacing_medlarge) | ||
| 153 | val topMargin = resources.getDimensionPixelSize(R.dimen.spacing_chip) | ||
| 154 | |||
| 155 | val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) | ||
| 156 | val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout()) | ||
| 157 | |||
| 158 | val leftInsets = barInsets.left + cutoutInsets.left | ||
| 159 | val rightInsets = barInsets.right + cutoutInsets.right | ||
| 160 | |||
| 161 | binding.settingsList.updatePadding(bottom = barInsets.bottom + extraListSpacing) | ||
| 162 | binding.frameSearch.updatePadding( | ||
| 163 | left = leftInsets + sideMargin, | ||
| 164 | top = barInsets.top + topMargin, | ||
| 165 | right = rightInsets + sideMargin | ||
| 166 | ) | ||
| 167 | binding.noResultsView.updatePadding( | ||
| 168 | left = leftInsets, | ||
| 169 | right = rightInsets, | ||
| 170 | bottom = barInsets.bottom | ||
| 171 | ) | ||
| 172 | |||
| 173 | val mlpSettingsList = binding.settingsList.layoutParams as ViewGroup.MarginLayoutParams | ||
| 174 | mlpSettingsList.leftMargin = leftInsets + sideMargin | ||
| 175 | mlpSettingsList.rightMargin = rightInsets + sideMargin | ||
| 176 | binding.settingsList.layoutParams = mlpSettingsList | ||
| 177 | |||
| 178 | val mlpDivider = binding.divider.layoutParams as ViewGroup.MarginLayoutParams | ||
| 179 | mlpDivider.leftMargin = leftInsets + sideMargin | ||
| 180 | mlpDivider.rightMargin = rightInsets + sideMargin | ||
| 181 | binding.divider.layoutParams = mlpDivider | ||
| 182 | |||
| 183 | windowInsets | ||
| 184 | } | ||
| 185 | |||
| 186 | companion object { | ||
| 187 | const val SEARCH_TEXT = "SearchText" | ||
| 188 | } | ||
| 189 | } | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt index 6f2276293..a0cb7225f 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt | |||
| @@ -27,6 +27,9 @@ class SettingsViewModel : ViewModel() { | |||
| 27 | private val _shouldReloadSettingsList = MutableLiveData(false) | 27 | private val _shouldReloadSettingsList = MutableLiveData(false) |
| 28 | val shouldReloadSettingsList: LiveData<Boolean> get() = _shouldReloadSettingsList | 28 | val shouldReloadSettingsList: LiveData<Boolean> get() = _shouldReloadSettingsList |
| 29 | 29 | ||
| 30 | private val _isUsingSearch = MutableLiveData(false) | ||
| 31 | val isUsingSearch: LiveData<Boolean> get() = _isUsingSearch | ||
| 32 | |||
| 30 | fun setToolbarTitle(value: String) { | 33 | fun setToolbarTitle(value: String) { |
| 31 | _toolbarTitle.value = value | 34 | _toolbarTitle.value = value |
| 32 | } | 35 | } |
| @@ -47,6 +50,10 @@ class SettingsViewModel : ViewModel() { | |||
| 47 | _shouldReloadSettingsList.value = value | 50 | _shouldReloadSettingsList.value = value |
| 48 | } | 51 | } |
| 49 | 52 | ||
| 53 | fun setIsUsingSearch(value: Boolean) { | ||
| 54 | _isUsingSearch.value = value | ||
| 55 | } | ||
| 56 | |||
| 50 | fun clear() { | 57 | fun clear() { |
| 51 | game = null | 58 | game = null |
| 52 | shouldSave = false | 59 | shouldSave = false |
diff --git a/src/android/app/src/main/res/layout/fragment_settings_search.xml b/src/android/app/src/main/res/layout/fragment_settings_search.xml new file mode 100644 index 000000000..c779ed2fc --- /dev/null +++ b/src/android/app/src/main/res/layout/fragment_settings_search.xml | |||
| @@ -0,0 +1,120 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 2 | <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 3 | xmlns:app="http://schemas.android.com/apk/res-auto" | ||
| 4 | xmlns:tools="http://schemas.android.com/tools" | ||
| 5 | android:layout_width="match_parent" | ||
| 6 | android:layout_height="match_parent"> | ||
| 7 | |||
| 8 | <RelativeLayout | ||
| 9 | android:id="@+id/relativeLayout" | ||
| 10 | android:layout_width="0dp" | ||
| 11 | android:layout_height="0dp" | ||
| 12 | app:layout_constraintBottom_toBottomOf="parent" | ||
| 13 | app:layout_constraintEnd_toEndOf="parent" | ||
| 14 | app:layout_constraintStart_toStartOf="parent" | ||
| 15 | app:layout_constraintTop_toBottomOf="@+id/divider"> | ||
| 16 | |||
| 17 | <LinearLayout | ||
| 18 | android:id="@+id/no_results_view" | ||
| 19 | android:layout_width="match_parent" | ||
| 20 | android:layout_height="match_parent" | ||
| 21 | android:gravity="center" | ||
| 22 | android:orientation="vertical"> | ||
| 23 | |||
| 24 | <ImageView | ||
| 25 | android:id="@+id/icon_no_results" | ||
| 26 | android:layout_width="match_parent" | ||
| 27 | android:layout_height="80dp" | ||
| 28 | android:src="@drawable/ic_search" /> | ||
| 29 | |||
| 30 | <com.google.android.material.textview.MaterialTextView | ||
| 31 | android:id="@+id/notice_text" | ||
| 32 | style="@style/TextAppearance.Material3.TitleLarge" | ||
| 33 | android:layout_width="match_parent" | ||
| 34 | android:layout_height="wrap_content" | ||
| 35 | android:gravity="center" | ||
| 36 | android:paddingTop="8dp" | ||
| 37 | android:text="@string/search_settings" | ||
| 38 | tools:visibility="visible" /> | ||
| 39 | |||
| 40 | </LinearLayout> | ||
| 41 | |||
| 42 | <androidx.recyclerview.widget.RecyclerView | ||
| 43 | android:id="@+id/settings_list" | ||
| 44 | android:layout_width="match_parent" | ||
| 45 | android:layout_height="match_parent" | ||
| 46 | android:clipToPadding="false" /> | ||
| 47 | |||
| 48 | </RelativeLayout> | ||
| 49 | |||
| 50 | <FrameLayout | ||
| 51 | android:id="@+id/frame_search" | ||
| 52 | android:layout_width="match_parent" | ||
| 53 | android:layout_height="wrap_content" | ||
| 54 | android:clipToPadding="false" | ||
| 55 | app:layout_constraintEnd_toEndOf="parent" | ||
| 56 | app:layout_constraintStart_toStartOf="parent" | ||
| 57 | app:layout_constraintTop_toTopOf="parent"> | ||
| 58 | |||
| 59 | <com.google.android.material.card.MaterialCardView | ||
| 60 | android:id="@+id/search_background" | ||
| 61 | style="?attr/materialCardViewFilledStyle" | ||
| 62 | android:layout_width="match_parent" | ||
| 63 | android:layout_height="56dp" | ||
| 64 | app:cardCornerRadius="28dp"> | ||
| 65 | |||
| 66 | <LinearLayout | ||
| 67 | android:id="@+id/search_container" | ||
| 68 | android:layout_width="match_parent" | ||
| 69 | android:layout_height="match_parent" | ||
| 70 | android:layout_marginEnd="56dp" | ||
| 71 | android:orientation="horizontal"> | ||
| 72 | |||
| 73 | <Button | ||
| 74 | android:id="@+id/back_button" | ||
| 75 | style="?attr/materialIconButtonFilledTonalStyle" | ||
| 76 | android:layout_width="wrap_content" | ||
| 77 | android:layout_height="wrap_content" | ||
| 78 | android:layout_gravity="center_vertical" | ||
| 79 | android:layout_marginStart="8dp" | ||
| 80 | app:backgroundTint="@android:color/transparent" | ||
| 81 | app:icon="@drawable/ic_back" /> | ||
| 82 | |||
| 83 | <EditText | ||
| 84 | android:id="@+id/search_text" | ||
| 85 | android:layout_width="match_parent" | ||
| 86 | android:layout_height="match_parent" | ||
| 87 | android:background="@android:color/transparent" | ||
| 88 | android:hint="@string/search_settings" | ||
| 89 | android:imeOptions="flagNoFullscreen" | ||
| 90 | android:inputType="text" | ||
| 91 | android:maxLines="1" /> | ||
| 92 | |||
| 93 | </LinearLayout> | ||
| 94 | |||
| 95 | <Button | ||
| 96 | android:id="@+id/clear_button" | ||
| 97 | style="?attr/materialIconButtonFilledTonalStyle" | ||
| 98 | android:layout_width="wrap_content" | ||
| 99 | android:layout_height="wrap_content" | ||
| 100 | android:layout_gravity="center_vertical|end" | ||
| 101 | android:layout_marginEnd="8dp" | ||
| 102 | android:visibility="invisible" | ||
| 103 | app:backgroundTint="@android:color/transparent" | ||
| 104 | app:icon="@drawable/ic_clear" | ||
| 105 | tools:visibility="visible" /> | ||
| 106 | |||
| 107 | </com.google.android.material.card.MaterialCardView> | ||
| 108 | |||
| 109 | </FrameLayout> | ||
| 110 | |||
| 111 | <com.google.android.material.divider.MaterialDivider | ||
| 112 | android:id="@+id/divider" | ||
| 113 | android:layout_width="match_parent" | ||
| 114 | android:layout_height="wrap_content" | ||
| 115 | android:layout_marginTop="20dp" | ||
| 116 | app:layout_constraintEnd_toEndOf="parent" | ||
| 117 | app:layout_constraintStart_toStartOf="parent" | ||
| 118 | app:layout_constraintTop_toBottomOf="@+id/frame_search" /> | ||
| 119 | |||
| 120 | </androidx.constraintlayout.widget.ConstraintLayout> | ||
diff --git a/src/android/app/src/main/res/menu/menu_settings.xml b/src/android/app/src/main/res/menu/menu_settings.xml new file mode 100644 index 000000000..21501a471 --- /dev/null +++ b/src/android/app/src/main/res/menu/menu_settings.xml | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 2 | <menu xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 3 | xmlns:app="http://schemas.android.com/apk/res-auto"> | ||
| 4 | |||
| 5 | <item | ||
| 6 | android:id="@+id/action_search" | ||
| 7 | android:icon="@drawable/ic_search" | ||
| 8 | android:title="@string/home_search" | ||
| 9 | app:showAsAction="always" /> | ||
| 10 | |||
| 11 | </menu> | ||
diff --git a/src/android/app/src/main/res/navigation/settings_navigation.xml b/src/android/app/src/main/res/navigation/settings_navigation.xml index b36200c65..88e1b4587 100644 --- a/src/android/app/src/main/res/navigation/settings_navigation.xml +++ b/src/android/app/src/main/res/navigation/settings_navigation.xml | |||
| @@ -15,10 +15,18 @@ | |||
| 15 | android:name="game" | 15 | android:name="game" |
| 16 | app:argType="org.yuzu.yuzu_emu.model.Game" | 16 | app:argType="org.yuzu.yuzu_emu.model.Game" |
| 17 | app:nullable="true" /> | 17 | app:nullable="true" /> |
| 18 | <action | ||
| 19 | android:id="@+id/action_settingsFragment_to_settingsSearchFragment" | ||
| 20 | app:destination="@id/settingsSearchFragment" /> | ||
| 18 | </fragment> | 21 | </fragment> |
| 19 | 22 | ||
| 20 | <action | 23 | <action |
| 21 | android:id="@+id/action_global_settingsFragment" | 24 | android:id="@+id/action_global_settingsFragment" |
| 22 | app:destination="@id/settingsFragment" /> | 25 | app:destination="@id/settingsFragment" /> |
| 23 | 26 | ||
| 27 | <fragment | ||
| 28 | android:id="@+id/settingsSearchFragment" | ||
| 29 | android:name="org.yuzu.yuzu_emu.fragments.SettingsSearchFragment" | ||
| 30 | android:label="SettingsSearchFragment" /> | ||
| 31 | |||
| 24 | </navigation> | 32 | </navigation> |
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 6b782780a..d43891cec 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml | |||
| @@ -43,6 +43,7 @@ | |||
| 43 | <string name="add_games_warning_description">Games won\'t be displayed in the Games list if a folder isn\'t selected.</string> | 43 | <string name="add_games_warning_description">Games won\'t be displayed in the Games list if a folder isn\'t selected.</string> |
| 44 | <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> | 44 | <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> |
| 45 | <string name="home_search_games">Search games</string> | 45 | <string name="home_search_games">Search games</string> |
| 46 | <string name="search_settings">Search settings</string> | ||
| 46 | <string name="games_dir_selected">Games directory selected</string> | 47 | <string name="games_dir_selected">Games directory selected</string> |
| 47 | <string name="install_prod_keys">Install prod.keys</string> | 48 | <string name="install_prod_keys">Install prod.keys</string> |
| 48 | <string name="install_prod_keys_description">Required to decrypt retail games</string> | 49 | <string name="install_prod_keys_description">Required to decrypt retail games</string> |