summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Charles Lombardo2023-08-12 16:53:14 -0400
committerGravatar Charles Lombardo2023-08-12 16:53:14 -0400
commit8bd0521b58a94594159030cbfc44198eb59aae18 (patch)
treef99a647779b5bd3631f3bdf254b0339543de6d68
parentMerge pull request #11219 from zeltermann/title-id-search (diff)
downloadyuzu-8bd0521b58a94594159030cbfc44198eb59aae18.tar.gz
yuzu-8bd0521b58a94594159030cbfc44198eb59aae18.tar.xz
yuzu-8bd0521b58a94594159030cbfc44198eb59aae18.zip
android: Show complete indicator during setup
Diffstat (limited to '')
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt19
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt109
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SetupPage.kt14
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt109
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt35
-rw-r--r--src/android/app/src/main/res/layout-w600dp/page_setup.xml69
-rw-r--r--src/android/app/src/main/res/layout/page_setup.xml30
-rw-r--r--src/android/app/src/main/res/values/strings.xml1
8 files changed, 265 insertions, 121 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt
index 481ddd5a5..0e3cec9ac 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt
@@ -5,13 +5,17 @@ package org.yuzu.yuzu_emu.adapters
5 5
6import android.text.Html 6import android.text.Html
7import android.view.LayoutInflater 7import android.view.LayoutInflater
8import android.view.View
8import android.view.ViewGroup 9import android.view.ViewGroup
9import androidx.appcompat.app.AppCompatActivity 10import androidx.appcompat.app.AppCompatActivity
10import androidx.core.content.res.ResourcesCompat 11import androidx.core.content.res.ResourcesCompat
11import androidx.recyclerview.widget.RecyclerView 12import androidx.recyclerview.widget.RecyclerView
12import com.google.android.material.button.MaterialButton 13import com.google.android.material.button.MaterialButton
13import org.yuzu.yuzu_emu.databinding.PageSetupBinding 14import org.yuzu.yuzu_emu.databinding.PageSetupBinding
15import org.yuzu.yuzu_emu.model.SetupCallback
14import org.yuzu.yuzu_emu.model.SetupPage 16import org.yuzu.yuzu_emu.model.SetupPage
17import org.yuzu.yuzu_emu.model.StepState
18import org.yuzu.yuzu_emu.utils.ViewUtils
15 19
16class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>) : 20class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>) :
17 RecyclerView.Adapter<SetupAdapter.SetupPageViewHolder>() { 21 RecyclerView.Adapter<SetupAdapter.SetupPageViewHolder>() {
@@ -26,7 +30,7 @@ class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>)
26 holder.bind(pages[position]) 30 holder.bind(pages[position])
27 31
28 inner class SetupPageViewHolder(val binding: PageSetupBinding) : 32 inner class SetupPageViewHolder(val binding: PageSetupBinding) :
29 RecyclerView.ViewHolder(binding.root) { 33 RecyclerView.ViewHolder(binding.root), SetupCallback {
30 lateinit var page: SetupPage 34 lateinit var page: SetupPage
31 35
32 init { 36 init {
@@ -35,6 +39,12 @@ class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>)
35 39
36 fun bind(page: SetupPage) { 40 fun bind(page: SetupPage) {
37 this.page = page 41 this.page = page
42
43 if (page.stepCompleted.invoke() == StepState.COMPLETE) {
44 binding.buttonAction.visibility = View.INVISIBLE
45 binding.textConfirmation.visibility = View.VISIBLE
46 }
47
38 binding.icon.setImageDrawable( 48 binding.icon.setImageDrawable(
39 ResourcesCompat.getDrawable( 49 ResourcesCompat.getDrawable(
40 activity.resources, 50 activity.resources,
@@ -62,9 +72,14 @@ class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>)
62 MaterialButton.ICON_GRAVITY_END 72 MaterialButton.ICON_GRAVITY_END
63 } 73 }
64 setOnClickListener { 74 setOnClickListener {
65 page.buttonAction.invoke() 75 page.buttonAction.invoke(this@SetupPageViewHolder)
66 } 76 }
67 } 77 }
68 } 78 }
79
80 override fun onStepCompleted() {
81 ViewUtils.hideView(binding.buttonAction, 200)
82 ViewUtils.showView(binding.textConfirmation, 200)
83 }
69 } 84 }
70} 85}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
index 6c4ddaf6b..8ca768dcf 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
@@ -32,10 +32,13 @@ import org.yuzu.yuzu_emu.adapters.SetupAdapter
32import org.yuzu.yuzu_emu.databinding.FragmentSetupBinding 32import org.yuzu.yuzu_emu.databinding.FragmentSetupBinding
33import org.yuzu.yuzu_emu.features.settings.model.Settings 33import org.yuzu.yuzu_emu.features.settings.model.Settings
34import org.yuzu.yuzu_emu.model.HomeViewModel 34import org.yuzu.yuzu_emu.model.HomeViewModel
35import org.yuzu.yuzu_emu.model.SetupCallback
35import org.yuzu.yuzu_emu.model.SetupPage 36import org.yuzu.yuzu_emu.model.SetupPage
37import org.yuzu.yuzu_emu.model.StepState
36import org.yuzu.yuzu_emu.ui.main.MainActivity 38import org.yuzu.yuzu_emu.ui.main.MainActivity
37import org.yuzu.yuzu_emu.utils.DirectoryInitialization 39import org.yuzu.yuzu_emu.utils.DirectoryInitialization
38import org.yuzu.yuzu_emu.utils.GameHelper 40import org.yuzu.yuzu_emu.utils.GameHelper
41import org.yuzu.yuzu_emu.utils.ViewUtils
39 42
40class SetupFragment : Fragment() { 43class SetupFragment : Fragment() {
41 private var _binding: FragmentSetupBinding? = null 44 private var _binding: FragmentSetupBinding? = null
@@ -112,14 +115,22 @@ class SetupFragment : Fragment() {
112 0, 115 0,
113 false, 116 false,
114 R.string.give_permission, 117 R.string.give_permission,
115 { permissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS) }, 118 {
119 notificationCallback = it
120 permissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
121 },
116 true, 122 true,
117 R.string.notification_warning, 123 R.string.notification_warning,
118 R.string.notification_warning_description, 124 R.string.notification_warning_description,
119 0, 125 0,
120 { 126 {
121 NotificationManagerCompat.from(requireContext()) 127 if (NotificationManagerCompat.from(requireContext())
122 .areNotificationsEnabled() 128 .areNotificationsEnabled()
129 ) {
130 StepState.COMPLETE
131 } else {
132 StepState.INCOMPLETE
133 }
123 } 134 }
124 ) 135 )
125 ) 136 )
@@ -133,12 +144,22 @@ class SetupFragment : Fragment() {
133 R.drawable.ic_add, 144 R.drawable.ic_add,
134 true, 145 true,
135 R.string.select_keys, 146 R.string.select_keys,
136 { mainActivity.getProdKey.launch(arrayOf("*/*")) }, 147 {
148 keyCallback = it
149 getProdKey.launch(arrayOf("*/*"))
150 },
137 true, 151 true,
138 R.string.install_prod_keys_warning, 152 R.string.install_prod_keys_warning,
139 R.string.install_prod_keys_warning_description, 153 R.string.install_prod_keys_warning_description,
140 R.string.install_prod_keys_warning_help, 154 R.string.install_prod_keys_warning_help,
141 { File(DirectoryInitialization.userDirectory + "/keys/prod.keys").exists() } 155 {
156 val file = File(DirectoryInitialization.userDirectory + "/keys/prod.keys")
157 if (file.exists()) {
158 StepState.COMPLETE
159 } else {
160 StepState.INCOMPLETE
161 }
162 }
142 ) 163 )
143 ) 164 )
144 add( 165 add(
@@ -150,9 +171,8 @@ class SetupFragment : Fragment() {
150 true, 171 true,
151 R.string.add_games, 172 R.string.add_games,
152 { 173 {
153 mainActivity.getGamesDirectory.launch( 174 gamesDirCallback = it
154 Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data 175 getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data)
155 )
156 }, 176 },
157 true, 177 true,
158 R.string.add_games_warning, 178 R.string.add_games_warning,
@@ -163,7 +183,11 @@ class SetupFragment : Fragment() {
163 PreferenceManager.getDefaultSharedPreferences( 183 PreferenceManager.getDefaultSharedPreferences(
164 YuzuApplication.appContext 184 YuzuApplication.appContext
165 ) 185 )
166 preferences.getString(GameHelper.KEY_GAME_PATH, "")!!.isNotEmpty() 186 if (preferences.getString(GameHelper.KEY_GAME_PATH, "")!!.isNotEmpty()) {
187 StepState.COMPLETE
188 } else {
189 StepState.INCOMPLETE
190 }
167 } 191 }
168 ) 192 )
169 ) 193 )
@@ -194,15 +218,15 @@ class SetupFragment : Fragment() {
194 super.onPageSelected(position) 218 super.onPageSelected(position)
195 219
196 if (position == 1 && previousPosition == 0) { 220 if (position == 1 && previousPosition == 0) {
197 showView(binding.buttonNext) 221 ViewUtils.showView(binding.buttonNext)
198 showView(binding.buttonBack) 222 ViewUtils.showView(binding.buttonBack)
199 } else if (position == 0 && previousPosition == 1) { 223 } else if (position == 0 && previousPosition == 1) {
200 hideView(binding.buttonBack) 224 ViewUtils.hideView(binding.buttonBack)
201 hideView(binding.buttonNext) 225 ViewUtils.hideView(binding.buttonNext)
202 } else if (position == pages.size - 1 && previousPosition == pages.size - 2) { 226 } else if (position == pages.size - 1 && previousPosition == pages.size - 2) {
203 hideView(binding.buttonNext) 227 ViewUtils.hideView(binding.buttonNext)
204 } else if (position == pages.size - 2 && previousPosition == pages.size - 1) { 228 } else if (position == pages.size - 2 && previousPosition == pages.size - 1) {
205 showView(binding.buttonNext) 229 ViewUtils.showView(binding.buttonNext)
206 } 230 }
207 231
208 previousPosition = position 232 previousPosition = position
@@ -215,7 +239,8 @@ class SetupFragment : Fragment() {
215 239
216 // Checks if the user has completed the task on the current page 240 // Checks if the user has completed the task on the current page
217 if (currentPage.hasWarning) { 241 if (currentPage.hasWarning) {
218 if (currentPage.taskCompleted.invoke()) { 242 val stepState = currentPage.stepCompleted.invoke()
243 if (stepState != StepState.INCOMPLETE) {
219 pageForward() 244 pageForward()
220 return@setOnClickListener 245 return@setOnClickListener
221 } 246 }
@@ -264,9 +289,15 @@ class SetupFragment : Fragment() {
264 _binding = null 289 _binding = null
265 } 290 }
266 291
292 private lateinit var notificationCallback: SetupCallback
293
267 @RequiresApi(Build.VERSION_CODES.TIRAMISU) 294 @RequiresApi(Build.VERSION_CODES.TIRAMISU)
268 private val permissionLauncher = 295 private val permissionLauncher =
269 registerForActivityResult(ActivityResultContracts.RequestPermission()) { 296 registerForActivityResult(ActivityResultContracts.RequestPermission()) {
297 if (it) {
298 notificationCallback.onStepCompleted()
299 }
300
270 if (!it && 301 if (!it &&
271 !shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS) 302 !shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)
272 ) { 303 ) {
@@ -277,6 +308,27 @@ class SetupFragment : Fragment() {
277 } 308 }
278 } 309 }
279 310
311 private lateinit var keyCallback: SetupCallback
312
313 val getProdKey =
314 registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
315 if (result != null) {
316 if (mainActivity.processKey(result)) {
317 keyCallback.onStepCompleted()
318 }
319 }
320 }
321
322 private lateinit var gamesDirCallback: SetupCallback
323
324 val getGamesDirectory =
325 registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
326 if (result != null) {
327 mainActivity.processGamesDir(result)
328 gamesDirCallback.onStepCompleted()
329 }
330 }
331
280 private fun finishSetup() { 332 private fun finishSetup() {
281 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit() 333 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit()
282 .putBoolean(Settings.PREF_FIRST_APP_LAUNCH, false) 334 .putBoolean(Settings.PREF_FIRST_APP_LAUNCH, false)
@@ -284,33 +336,6 @@ class SetupFragment : Fragment() {
284 mainActivity.finishSetup(binding.root.findNavController()) 336 mainActivity.finishSetup(binding.root.findNavController())
285 } 337 }
286 338
287 private fun showView(view: View) {
288 view.apply {
289 alpha = 0f
290 visibility = View.VISIBLE
291 isClickable = true
292 }.animate().apply {
293 duration = 300
294 alpha(1f)
295 }.start()
296 }
297
298 private fun hideView(view: View) {
299 if (view.visibility == View.INVISIBLE) {
300 return
301 }
302
303 view.apply {
304 alpha = 1f
305 isClickable = false
306 }.animate().apply {
307 duration = 300
308 alpha(0f)
309 }.withEndAction {
310 view.visibility = View.INVISIBLE
311 }
312 }
313
314 fun pageForward() { 339 fun pageForward() {
315 binding.viewPager2.currentItem = binding.viewPager2.currentItem + 1 340 binding.viewPager2.currentItem = binding.viewPager2.currentItem + 1
316 } 341 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SetupPage.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SetupPage.kt
index a0c878e1c..09a128ae6 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SetupPage.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SetupPage.kt
@@ -10,10 +10,20 @@ data class SetupPage(
10 val buttonIconId: Int, 10 val buttonIconId: Int,
11 val leftAlignedIcon: Boolean, 11 val leftAlignedIcon: Boolean,
12 val buttonTextId: Int, 12 val buttonTextId: Int,
13 val buttonAction: () -> Unit, 13 val buttonAction: (callback: SetupCallback) -> Unit,
14 val hasWarning: Boolean, 14 val hasWarning: Boolean,
15 val warningTitleId: Int = 0, 15 val warningTitleId: Int = 0,
16 val warningDescriptionId: Int = 0, 16 val warningDescriptionId: Int = 0,
17 val warningHelpLinkId: Int = 0, 17 val warningHelpLinkId: Int = 0,
18 val taskCompleted: () -> Boolean = { true } 18 val stepCompleted: () -> StepState = { StepState.UNDEFINED }
19) 19)
20
21interface SetupCallback {
22 fun onStepCompleted()
23}
24
25enum class StepState {
26 COMPLETE,
27 INCOMPLETE,
28 UNDEFINED
29}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
index f7d7aed1e..f77d06262 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
@@ -266,73 +266,80 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
266 266
267 val getGamesDirectory = 267 val getGamesDirectory =
268 registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result -> 268 registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
269 if (result == null) { 269 if (result != null) {
270 return@registerForActivityResult 270 processGamesDir(result)
271 } 271 }
272 }
272 273
273 contentResolver.takePersistableUriPermission( 274 fun processGamesDir(result: Uri) {
274 result, 275 contentResolver.takePersistableUriPermission(
275 Intent.FLAG_GRANT_READ_URI_PERMISSION 276 result,
276 ) 277 Intent.FLAG_GRANT_READ_URI_PERMISSION
278 )
277 279
278 // When a new directory is picked, we currently will reset the existing games 280 // When a new directory is picked, we currently will reset the existing games
279 // database. This effectively means that only one game directory is supported. 281 // database. This effectively means that only one game directory is supported.
280 PreferenceManager.getDefaultSharedPreferences(applicationContext).edit() 282 PreferenceManager.getDefaultSharedPreferences(applicationContext).edit()
281 .putString(GameHelper.KEY_GAME_PATH, result.toString()) 283 .putString(GameHelper.KEY_GAME_PATH, result.toString())
282 .apply() 284 .apply()
283 285
284 Toast.makeText( 286 Toast.makeText(
285 applicationContext, 287 applicationContext,
286 R.string.games_dir_selected, 288 R.string.games_dir_selected,
287 Toast.LENGTH_LONG 289 Toast.LENGTH_LONG
288 ).show() 290 ).show()
289 291
290 gamesViewModel.reloadGames(true) 292 gamesViewModel.reloadGames(true)
291 } 293 }
292 294
293 val getProdKey = 295 val getProdKey =
294 registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> 296 registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
295 if (result == null) { 297 if (result != null) {
296 return@registerForActivityResult 298 processKey(result)
297 } 299 }
300 }
298 301
299 if (FileUtil.getExtension(result) != "keys") { 302 fun processKey(result: Uri): Boolean {
300 MessageDialogFragment.newInstance( 303 if (FileUtil.getExtension(result) != "keys") {
301 R.string.reading_keys_failure, 304 MessageDialogFragment.newInstance(
302 R.string.install_prod_keys_failure_extension_description 305 R.string.reading_keys_failure,
303 ).show(supportFragmentManager, MessageDialogFragment.TAG) 306 R.string.install_prod_keys_failure_extension_description
304 return@registerForActivityResult 307 ).show(supportFragmentManager, MessageDialogFragment.TAG)
305 } 308 return false
309 }
306 310
307 contentResolver.takePersistableUriPermission( 311 contentResolver.takePersistableUriPermission(
312 result,
313 Intent.FLAG_GRANT_READ_URI_PERMISSION
314 )
315
316 val dstPath = DirectoryInitialization.userDirectory + "/keys/"
317 if (FileUtil.copyUriToInternalStorage(
318 applicationContext,
308 result, 319 result,
309 Intent.FLAG_GRANT_READ_URI_PERMISSION 320 dstPath,
321 "prod.keys"
310 ) 322 )
311 323 ) {
312 val dstPath = DirectoryInitialization.userDirectory + "/keys/" 324 if (NativeLibrary.reloadKeys()) {
313 if (FileUtil.copyUriToInternalStorage( 325 Toast.makeText(
314 applicationContext, 326 applicationContext,
315 result, 327 R.string.install_keys_success,
316 dstPath, 328 Toast.LENGTH_SHORT
317 "prod.keys" 329 ).show()
318 ) 330 gamesViewModel.reloadGames(true)
319 ) { 331 return true
320 if (NativeLibrary.reloadKeys()) { 332 } else {
321 Toast.makeText( 333 MessageDialogFragment.newInstance(
322 applicationContext, 334 R.string.invalid_keys_error,
323 R.string.install_keys_success, 335 R.string.install_keys_failure_description,
324 Toast.LENGTH_SHORT 336 R.string.dumping_keys_quickstart_link
325 ).show() 337 ).show(supportFragmentManager, MessageDialogFragment.TAG)
326 gamesViewModel.reloadGames(true) 338 return false
327 } else {
328 MessageDialogFragment.newInstance(
329 R.string.invalid_keys_error,
330 R.string.install_keys_failure_description,
331 R.string.dumping_keys_quickstart_link
332 ).show(supportFragmentManager, MessageDialogFragment.TAG)
333 }
334 } 339 }
335 } 340 }
341 return false
342 }
336 343
337 val getFirmware = 344 val getFirmware =
338 registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> 345 registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt
new file mode 100644
index 000000000..f9a3e4126
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt
@@ -0,0 +1,35 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.utils
5
6import android.view.View
7
8object ViewUtils {
9 fun showView(view: View, length: Long = 300) {
10 view.apply {
11 alpha = 0f
12 visibility = View.VISIBLE
13 isClickable = true
14 }.animate().apply {
15 duration = length
16 alpha(1f)
17 }.start()
18 }
19
20 fun hideView(view: View, length: Long = 300) {
21 if (view.visibility == View.INVISIBLE) {
22 return
23 }
24
25 view.apply {
26 alpha = 1f
27 isClickable = false
28 }.animate().apply {
29 duration = length
30 alpha(0f)
31 }.withEndAction {
32 view.visibility = View.INVISIBLE
33 }.start()
34 }
35}
diff --git a/src/android/app/src/main/res/layout-w600dp/page_setup.xml b/src/android/app/src/main/res/layout-w600dp/page_setup.xml
index e1c26b2f8..9e0ab8ecb 100644
--- a/src/android/app/src/main/res/layout-w600dp/page_setup.xml
+++ b/src/android/app/src/main/res/layout-w600dp/page_setup.xml
@@ -21,45 +21,76 @@
21 21
22 </LinearLayout> 22 </LinearLayout>
23 23
24 <LinearLayout 24 <androidx.constraintlayout.widget.ConstraintLayout
25 android:layout_width="match_parent" 25 android:layout_width="match_parent"
26 android:layout_height="match_parent" 26 android:layout_height="match_parent"
27 android:layout_weight="1" 27 android:layout_weight="1">
28 android:orientation="vertical"
29 android:gravity="center">
30 28
31 <com.google.android.material.textview.MaterialTextView 29 <com.google.android.material.textview.MaterialTextView
32 style="@style/TextAppearance.Material3.DisplaySmall"
33 android:id="@+id/text_title" 30 android:id="@+id/text_title"
34 android:layout_width="match_parent" 31 style="@style/TextAppearance.Material3.DisplaySmall"
35 android:layout_height="wrap_content" 32 android:layout_width="0dp"
36 android:textAlignment="center" 33 android:layout_height="0dp"
34 android:gravity="center"
37 android:textColor="?attr/colorOnSurface" 35 android:textColor="?attr/colorOnSurface"
38 android:textStyle="bold" 36 android:textStyle="bold"
37 app:layout_constraintBottom_toTopOf="@+id/text_description"
38 app:layout_constraintEnd_toEndOf="parent"
39 app:layout_constraintStart_toStartOf="parent"
40 app:layout_constraintTop_toTopOf="parent"
41 app:layout_constraintVertical_weight="2"
39 tools:text="@string/welcome" /> 42 tools:text="@string/welcome" />
40 43
41 <com.google.android.material.textview.MaterialTextView 44 <com.google.android.material.textview.MaterialTextView
42 style="@style/TextAppearance.Material3.TitleLarge"
43 android:id="@+id/text_description" 45 android:id="@+id/text_description"
44 android:layout_width="match_parent" 46 style="@style/TextAppearance.Material3.TitleLarge"
45 android:layout_height="wrap_content" 47 android:layout_width="0dp"
46 android:layout_marginTop="16dp" 48 android:layout_height="0dp"
47 android:paddingHorizontal="32dp" 49 android:gravity="center"
48 android:textAlignment="center" 50 android:textSize="20sp"
49 android:textSize="26sp" 51 android:paddingHorizontal="16dp"
50 app:lineHeight="40sp" 52 app:layout_constraintBottom_toTopOf="@+id/button_action"
53 app:layout_constraintEnd_toEndOf="parent"
54 app:layout_constraintStart_toStartOf="parent"
55 app:layout_constraintTop_toBottomOf="@+id/text_title"
56 app:layout_constraintVertical_weight="2"
57 app:lineHeight="30sp"
51 tools:text="@string/welcome_description" /> 58 tools:text="@string/welcome_description" />
52 59
60 <com.google.android.material.textview.MaterialTextView
61 android:id="@+id/text_confirmation"
62 style="@style/TextAppearance.Material3.TitleLarge"
63 android:layout_width="0dp"
64 android:layout_height="0dp"
65 android:paddingHorizontal="16dp"
66 android:paddingBottom="20dp"
67 android:gravity="center"
68 android:textSize="30sp"
69 android:visibility="invisible"
70 android:text="@string/step_complete"
71 android:textStyle="bold"
72 app:layout_constraintBottom_toBottomOf="parent"
73 app:layout_constraintEnd_toEndOf="parent"
74 app:layout_constraintStart_toStartOf="parent"
75 app:layout_constraintTop_toBottomOf="@+id/text_description"
76 app:layout_constraintVertical_weight="1"
77 app:lineHeight="30sp" />
78
53 <com.google.android.material.button.MaterialButton 79 <com.google.android.material.button.MaterialButton
54 android:id="@+id/button_action" 80 android:id="@+id/button_action"
55 android:layout_width="wrap_content" 81 android:layout_width="wrap_content"
56 android:layout_height="56dp" 82 android:layout_height="56dp"
57 android:layout_marginTop="32dp" 83 android:layout_marginTop="16dp"
84 android:layout_marginBottom="48dp"
58 android:textSize="20sp" 85 android:textSize="20sp"
59 app:iconSize="24sp"
60 app:iconGravity="end" 86 app:iconGravity="end"
87 app:iconSize="24sp"
88 app:layout_constraintBottom_toBottomOf="parent"
89 app:layout_constraintEnd_toEndOf="parent"
90 app:layout_constraintStart_toStartOf="parent"
91 app:layout_constraintTop_toBottomOf="@+id/text_description"
61 tools:text="Get started" /> 92 tools:text="Get started" />
62 93
63 </LinearLayout> 94 </androidx.constraintlayout.widget.ConstraintLayout>
64 95
65</LinearLayout> 96</LinearLayout>
diff --git a/src/android/app/src/main/res/layout/page_setup.xml b/src/android/app/src/main/res/layout/page_setup.xml
index 1436ef308..535abcf02 100644
--- a/src/android/app/src/main/res/layout/page_setup.xml
+++ b/src/android/app/src/main/res/layout/page_setup.xml
@@ -21,11 +21,12 @@
21 app:layout_constraintVertical_chainStyle="spread" 21 app:layout_constraintVertical_chainStyle="spread"
22 app:layout_constraintWidth_max="220dp" 22 app:layout_constraintWidth_max="220dp"
23 app:layout_constraintWidth_min="110dp" 23 app:layout_constraintWidth_min="110dp"
24 app:layout_constraintVertical_weight="3" /> 24 app:layout_constraintVertical_weight="3"
25 tools:src="@drawable/ic_notification" />
25 26
26 <com.google.android.material.textview.MaterialTextView 27 <com.google.android.material.textview.MaterialTextView
27 android:id="@+id/text_title" 28 android:id="@+id/text_title"
28 style="@style/TextAppearance.Material3.DisplayMedium" 29 style="@style/TextAppearance.Material3.DisplaySmall"
29 android:layout_width="0dp" 30 android:layout_width="0dp"
30 android:layout_height="0dp" 31 android:layout_height="0dp"
31 android:textAlignment="center" 32 android:textAlignment="center"
@@ -44,23 +45,42 @@
44 android:layout_width="0dp" 45 android:layout_width="0dp"
45 android:layout_height="0dp" 46 android:layout_height="0dp"
46 android:textAlignment="center" 47 android:textAlignment="center"
47 android:textSize="26sp" 48 android:textSize="20sp"
48 android:paddingHorizontal="16dp" 49 android:paddingHorizontal="16dp"
49 app:layout_constraintBottom_toTopOf="@+id/button_action" 50 app:layout_constraintBottom_toTopOf="@+id/button_action"
50 app:layout_constraintEnd_toEndOf="parent" 51 app:layout_constraintEnd_toEndOf="parent"
51 app:layout_constraintStart_toStartOf="parent" 52 app:layout_constraintStart_toStartOf="parent"
52 app:layout_constraintTop_toBottomOf="@+id/text_title" 53 app:layout_constraintTop_toBottomOf="@+id/text_title"
53 app:layout_constraintVertical_weight="2" 54 app:layout_constraintVertical_weight="2"
54 app:lineHeight="40sp" 55 app:lineHeight="30sp"
55 tools:text="@string/welcome_description" /> 56 tools:text="@string/welcome_description" />
56 57
58 <com.google.android.material.textview.MaterialTextView
59 android:id="@+id/text_confirmation"
60 style="@style/TextAppearance.Material3.TitleLarge"
61 android:layout_width="wrap_content"
62 android:layout_height="0dp"
63 android:paddingHorizontal="16dp"
64 android:paddingTop="24dp"
65 android:textAlignment="center"
66 android:textSize="30sp"
67 android:visibility="invisible"
68 android:text="@string/step_complete"
69 android:textStyle="bold"
70 app:layout_constraintBottom_toBottomOf="parent"
71 app:layout_constraintEnd_toEndOf="parent"
72 app:layout_constraintStart_toStartOf="parent"
73 app:layout_constraintTop_toBottomOf="@+id/text_description"
74 app:layout_constraintVertical_weight="1"
75 app:lineHeight="30sp" />
76
57 <com.google.android.material.button.MaterialButton 77 <com.google.android.material.button.MaterialButton
58 android:id="@+id/button_action" 78 android:id="@+id/button_action"
59 android:layout_width="wrap_content" 79 android:layout_width="wrap_content"
60 android:layout_height="56dp" 80 android:layout_height="56dp"
61 android:textSize="20sp"
62 android:layout_marginTop="16dp" 81 android:layout_marginTop="16dp"
63 android:layout_marginBottom="48dp" 82 android:layout_marginBottom="48dp"
83 android:textSize="20sp"
64 app:iconGravity="end" 84 app:iconGravity="end"
65 app:iconSize="24sp" 85 app:iconSize="24sp"
66 app:layout_constraintBottom_toBottomOf="parent" 86 app:layout_constraintBottom_toBottomOf="parent"
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index 02e25504d..540ea5ef4 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -29,6 +29,7 @@
29 <string name="back">Back</string> 29 <string name="back">Back</string>
30 <string name="add_games">Add Games</string> 30 <string name="add_games">Add Games</string>
31 <string name="add_games_description">Select your games folder</string> 31 <string name="add_games_description">Select your games folder</string>
32 <string name="step_complete">Complete!</string>
32 33
33 <!-- Home strings --> 34 <!-- Home strings -->
34 <string name="home_games">Games</string> 35 <string name="home_games">Games</string>