Przeglądaj źródła

android: Show complete indicator during setup

Charles Lombardo 2 lat temu
rodzic
commit
8bd0521b58

+ 17 - 2
src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt

@@ -5,13 +5,17 @@ package org.yuzu.yuzu_emu.adapters
 
 
 import android.text.Html
 import android.text.Html
 import android.view.LayoutInflater
 import android.view.LayoutInflater
+import android.view.View
 import android.view.ViewGroup
 import android.view.ViewGroup
 import androidx.appcompat.app.AppCompatActivity
 import androidx.appcompat.app.AppCompatActivity
 import androidx.core.content.res.ResourcesCompat
 import androidx.core.content.res.ResourcesCompat
 import androidx.recyclerview.widget.RecyclerView
 import androidx.recyclerview.widget.RecyclerView
 import com.google.android.material.button.MaterialButton
 import com.google.android.material.button.MaterialButton
 import org.yuzu.yuzu_emu.databinding.PageSetupBinding
 import org.yuzu.yuzu_emu.databinding.PageSetupBinding
+import org.yuzu.yuzu_emu.model.SetupCallback
 import org.yuzu.yuzu_emu.model.SetupPage
 import org.yuzu.yuzu_emu.model.SetupPage
+import org.yuzu.yuzu_emu.model.StepState
+import org.yuzu.yuzu_emu.utils.ViewUtils
 
 
 class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>) :
 class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>) :
     RecyclerView.Adapter<SetupAdapter.SetupPageViewHolder>() {
     RecyclerView.Adapter<SetupAdapter.SetupPageViewHolder>() {
@@ -26,7 +30,7 @@ class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>)
         holder.bind(pages[position])
         holder.bind(pages[position])
 
 
     inner class SetupPageViewHolder(val binding: PageSetupBinding) :
     inner class SetupPageViewHolder(val binding: PageSetupBinding) :
-        RecyclerView.ViewHolder(binding.root) {
+        RecyclerView.ViewHolder(binding.root), SetupCallback {
         lateinit var page: SetupPage
         lateinit var page: SetupPage
 
 
         init {
         init {
@@ -35,6 +39,12 @@ class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>)
 
 
         fun bind(page: SetupPage) {
         fun bind(page: SetupPage) {
             this.page = page
             this.page = page
+
+            if (page.stepCompleted.invoke() == StepState.COMPLETE) {
+                binding.buttonAction.visibility = View.INVISIBLE
+                binding.textConfirmation.visibility = View.VISIBLE
+            }
+
             binding.icon.setImageDrawable(
             binding.icon.setImageDrawable(
                 ResourcesCompat.getDrawable(
                 ResourcesCompat.getDrawable(
                     activity.resources,
                     activity.resources,
@@ -62,9 +72,14 @@ class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>)
                         MaterialButton.ICON_GRAVITY_END
                         MaterialButton.ICON_GRAVITY_END
                     }
                     }
                 setOnClickListener {
                 setOnClickListener {
-                    page.buttonAction.invoke()
+                    page.buttonAction.invoke(this@SetupPageViewHolder)
                 }
                 }
             }
             }
         }
         }
+
+        override fun onStepCompleted() {
+            ViewUtils.hideView(binding.buttonAction, 200)
+            ViewUtils.showView(binding.textConfirmation, 200)
+        }
     }
     }
 }
 }

+ 67 - 42
src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt

@@ -32,10 +32,13 @@ import org.yuzu.yuzu_emu.adapters.SetupAdapter
 import org.yuzu.yuzu_emu.databinding.FragmentSetupBinding
 import org.yuzu.yuzu_emu.databinding.FragmentSetupBinding
 import org.yuzu.yuzu_emu.features.settings.model.Settings
 import org.yuzu.yuzu_emu.features.settings.model.Settings
 import org.yuzu.yuzu_emu.model.HomeViewModel
 import org.yuzu.yuzu_emu.model.HomeViewModel
+import org.yuzu.yuzu_emu.model.SetupCallback
 import org.yuzu.yuzu_emu.model.SetupPage
 import org.yuzu.yuzu_emu.model.SetupPage
+import org.yuzu.yuzu_emu.model.StepState
 import org.yuzu.yuzu_emu.ui.main.MainActivity
 import org.yuzu.yuzu_emu.ui.main.MainActivity
 import org.yuzu.yuzu_emu.utils.DirectoryInitialization
 import org.yuzu.yuzu_emu.utils.DirectoryInitialization
 import org.yuzu.yuzu_emu.utils.GameHelper
 import org.yuzu.yuzu_emu.utils.GameHelper
+import org.yuzu.yuzu_emu.utils.ViewUtils
 
 
 class SetupFragment : Fragment() {
 class SetupFragment : Fragment() {
     private var _binding: FragmentSetupBinding? = null
     private var _binding: FragmentSetupBinding? = null
@@ -112,14 +115,22 @@ class SetupFragment : Fragment() {
                         0,
                         0,
                         false,
                         false,
                         R.string.give_permission,
                         R.string.give_permission,
-                        { permissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS) },
+                        {
+                            notificationCallback = it
+                            permissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
+                        },
                         true,
                         true,
                         R.string.notification_warning,
                         R.string.notification_warning,
                         R.string.notification_warning_description,
                         R.string.notification_warning_description,
                         0,
                         0,
                         {
                         {
-                            NotificationManagerCompat.from(requireContext())
+                            if (NotificationManagerCompat.from(requireContext())
                                 .areNotificationsEnabled()
                                 .areNotificationsEnabled()
+                            ) {
+                                StepState.COMPLETE
+                            } else {
+                                StepState.INCOMPLETE
+                            }
                         }
                         }
                     )
                     )
                 )
                 )
@@ -133,12 +144,22 @@ class SetupFragment : Fragment() {
                     R.drawable.ic_add,
                     R.drawable.ic_add,
                     true,
                     true,
                     R.string.select_keys,
                     R.string.select_keys,
-                    { mainActivity.getProdKey.launch(arrayOf("*/*")) },
+                    {
+                        keyCallback = it
+                        getProdKey.launch(arrayOf("*/*"))
+                    },
                     true,
                     true,
                     R.string.install_prod_keys_warning,
                     R.string.install_prod_keys_warning,
                     R.string.install_prod_keys_warning_description,
                     R.string.install_prod_keys_warning_description,
                     R.string.install_prod_keys_warning_help,
                     R.string.install_prod_keys_warning_help,
-                    { File(DirectoryInitialization.userDirectory + "/keys/prod.keys").exists() }
+                    {
+                        val file = File(DirectoryInitialization.userDirectory + "/keys/prod.keys")
+                        if (file.exists()) {
+                            StepState.COMPLETE
+                        } else {
+                            StepState.INCOMPLETE
+                        }
+                    }
                 )
                 )
             )
             )
             add(
             add(
@@ -150,9 +171,8 @@ class SetupFragment : Fragment() {
                     true,
                     true,
                     R.string.add_games,
                     R.string.add_games,
                     {
                     {
-                        mainActivity.getGamesDirectory.launch(
-                            Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data
-                        )
+                        gamesDirCallback = it
+                        getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data)
                     },
                     },
                     true,
                     true,
                     R.string.add_games_warning,
                     R.string.add_games_warning,
@@ -163,7 +183,11 @@ class SetupFragment : Fragment() {
                             PreferenceManager.getDefaultSharedPreferences(
                             PreferenceManager.getDefaultSharedPreferences(
                                 YuzuApplication.appContext
                                 YuzuApplication.appContext
                             )
                             )
-                        preferences.getString(GameHelper.KEY_GAME_PATH, "")!!.isNotEmpty()
+                        if (preferences.getString(GameHelper.KEY_GAME_PATH, "")!!.isNotEmpty()) {
+                            StepState.COMPLETE
+                        } else {
+                            StepState.INCOMPLETE
+                        }
                     }
                     }
                 )
                 )
             )
             )
@@ -194,15 +218,15 @@ class SetupFragment : Fragment() {
                 super.onPageSelected(position)
                 super.onPageSelected(position)
 
 
                 if (position == 1 && previousPosition == 0) {
                 if (position == 1 && previousPosition == 0) {
-                    showView(binding.buttonNext)
-                    showView(binding.buttonBack)
+                    ViewUtils.showView(binding.buttonNext)
+                    ViewUtils.showView(binding.buttonBack)
                 } else if (position == 0 && previousPosition == 1) {
                 } else if (position == 0 && previousPosition == 1) {
-                    hideView(binding.buttonBack)
-                    hideView(binding.buttonNext)
+                    ViewUtils.hideView(binding.buttonBack)
+                    ViewUtils.hideView(binding.buttonNext)
                 } else if (position == pages.size - 1 && previousPosition == pages.size - 2) {
                 } else if (position == pages.size - 1 && previousPosition == pages.size - 2) {
-                    hideView(binding.buttonNext)
+                    ViewUtils.hideView(binding.buttonNext)
                 } else if (position == pages.size - 2 && previousPosition == pages.size - 1) {
                 } else if (position == pages.size - 2 && previousPosition == pages.size - 1) {
-                    showView(binding.buttonNext)
+                    ViewUtils.showView(binding.buttonNext)
                 }
                 }
 
 
                 previousPosition = position
                 previousPosition = position
@@ -215,7 +239,8 @@ class SetupFragment : Fragment() {
 
 
             // Checks if the user has completed the task on the current page
             // Checks if the user has completed the task on the current page
             if (currentPage.hasWarning) {
             if (currentPage.hasWarning) {
-                if (currentPage.taskCompleted.invoke()) {
+                val stepState = currentPage.stepCompleted.invoke()
+                if (stepState != StepState.INCOMPLETE) {
                     pageForward()
                     pageForward()
                     return@setOnClickListener
                     return@setOnClickListener
                 }
                 }
@@ -264,9 +289,15 @@ class SetupFragment : Fragment() {
         _binding = null
         _binding = null
     }
     }
 
 
+    private lateinit var notificationCallback: SetupCallback
+
     @RequiresApi(Build.VERSION_CODES.TIRAMISU)
     @RequiresApi(Build.VERSION_CODES.TIRAMISU)
     private val permissionLauncher =
     private val permissionLauncher =
         registerForActivityResult(ActivityResultContracts.RequestPermission()) {
         registerForActivityResult(ActivityResultContracts.RequestPermission()) {
+            if (it) {
+                notificationCallback.onStepCompleted()
+            }
+
             if (!it &&
             if (!it &&
                 !shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)
                 !shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)
             ) {
             ) {
@@ -277,6 +308,27 @@ class SetupFragment : Fragment() {
             }
             }
         }
         }
 
 
+    private lateinit var keyCallback: SetupCallback
+
+    val getProdKey =
+        registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
+            if (result != null) {
+                if (mainActivity.processKey(result)) {
+                    keyCallback.onStepCompleted()
+                }
+            }
+        }
+
+    private lateinit var gamesDirCallback: SetupCallback
+
+    val getGamesDirectory =
+        registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
+            if (result != null) {
+                mainActivity.processGamesDir(result)
+                gamesDirCallback.onStepCompleted()
+            }
+        }
+
     private fun finishSetup() {
     private fun finishSetup() {
         PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit()
         PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit()
             .putBoolean(Settings.PREF_FIRST_APP_LAUNCH, false)
             .putBoolean(Settings.PREF_FIRST_APP_LAUNCH, false)
@@ -284,33 +336,6 @@ class SetupFragment : Fragment() {
         mainActivity.finishSetup(binding.root.findNavController())
         mainActivity.finishSetup(binding.root.findNavController())
     }
     }
 
 
-    private fun showView(view: View) {
-        view.apply {
-            alpha = 0f
-            visibility = View.VISIBLE
-            isClickable = true
-        }.animate().apply {
-            duration = 300
-            alpha(1f)
-        }.start()
-    }
-
-    private fun hideView(view: View) {
-        if (view.visibility == View.INVISIBLE) {
-            return
-        }
-
-        view.apply {
-            alpha = 1f
-            isClickable = false
-        }.animate().apply {
-            duration = 300
-            alpha(0f)
-        }.withEndAction {
-            view.visibility = View.INVISIBLE
-        }
-    }
-
     fun pageForward() {
     fun pageForward() {
         binding.viewPager2.currentItem = binding.viewPager2.currentItem + 1
         binding.viewPager2.currentItem = binding.viewPager2.currentItem + 1
     }
     }

+ 12 - 2
src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SetupPage.kt

@@ -10,10 +10,20 @@ data class SetupPage(
     val buttonIconId: Int,
     val buttonIconId: Int,
     val leftAlignedIcon: Boolean,
     val leftAlignedIcon: Boolean,
     val buttonTextId: Int,
     val buttonTextId: Int,
-    val buttonAction: () -> Unit,
+    val buttonAction: (callback: SetupCallback) -> Unit,
     val hasWarning: Boolean,
     val hasWarning: Boolean,
     val warningTitleId: Int = 0,
     val warningTitleId: Int = 0,
     val warningDescriptionId: Int = 0,
     val warningDescriptionId: Int = 0,
     val warningHelpLinkId: Int = 0,
     val warningHelpLinkId: Int = 0,
-    val taskCompleted: () -> Boolean = { true }
+    val stepCompleted: () -> StepState = { StepState.UNDEFINED }
 )
 )
+
+interface SetupCallback {
+    fun onStepCompleted()
+}
+
+enum class StepState {
+    COMPLETE,
+    INCOMPLETE,
+    UNDEFINED
+}

+ 58 - 51
src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt

@@ -266,73 +266,80 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
 
 
     val getGamesDirectory =
     val getGamesDirectory =
         registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
         registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
-            if (result == null) {
-                return@registerForActivityResult
+            if (result != null) {
+                processGamesDir(result)
             }
             }
+        }
 
 
-            contentResolver.takePersistableUriPermission(
-                result,
-                Intent.FLAG_GRANT_READ_URI_PERMISSION
-            )
+    fun processGamesDir(result: Uri) {
+        contentResolver.takePersistableUriPermission(
+            result,
+            Intent.FLAG_GRANT_READ_URI_PERMISSION
+        )
 
 
-            // When a new directory is picked, we currently will reset the existing games
-            // database. This effectively means that only one game directory is supported.
-            PreferenceManager.getDefaultSharedPreferences(applicationContext).edit()
-                .putString(GameHelper.KEY_GAME_PATH, result.toString())
-                .apply()
+        // When a new directory is picked, we currently will reset the existing games
+        // database. This effectively means that only one game directory is supported.
+        PreferenceManager.getDefaultSharedPreferences(applicationContext).edit()
+            .putString(GameHelper.KEY_GAME_PATH, result.toString())
+            .apply()
 
 
-            Toast.makeText(
-                applicationContext,
-                R.string.games_dir_selected,
-                Toast.LENGTH_LONG
-            ).show()
+        Toast.makeText(
+            applicationContext,
+            R.string.games_dir_selected,
+            Toast.LENGTH_LONG
+        ).show()
 
 
-            gamesViewModel.reloadGames(true)
-        }
+        gamesViewModel.reloadGames(true)
+    }
 
 
     val getProdKey =
     val getProdKey =
         registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
         registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
-            if (result == null) {
-                return@registerForActivityResult
+            if (result != null) {
+                processKey(result)
             }
             }
+        }
 
 
-            if (FileUtil.getExtension(result) != "keys") {
-                MessageDialogFragment.newInstance(
-                    R.string.reading_keys_failure,
-                    R.string.install_prod_keys_failure_extension_description
-                ).show(supportFragmentManager, MessageDialogFragment.TAG)
-                return@registerForActivityResult
-            }
+    fun processKey(result: Uri): Boolean {
+        if (FileUtil.getExtension(result) != "keys") {
+            MessageDialogFragment.newInstance(
+                R.string.reading_keys_failure,
+                R.string.install_prod_keys_failure_extension_description
+            ).show(supportFragmentManager, MessageDialogFragment.TAG)
+            return false
+        }
 
 
-            contentResolver.takePersistableUriPermission(
+        contentResolver.takePersistableUriPermission(
+            result,
+            Intent.FLAG_GRANT_READ_URI_PERMISSION
+        )
+
+        val dstPath = DirectoryInitialization.userDirectory + "/keys/"
+        if (FileUtil.copyUriToInternalStorage(
+                applicationContext,
                 result,
                 result,
-                Intent.FLAG_GRANT_READ_URI_PERMISSION
+                dstPath,
+                "prod.keys"
             )
             )
-
-            val dstPath = DirectoryInitialization.userDirectory + "/keys/"
-            if (FileUtil.copyUriToInternalStorage(
+        ) {
+            if (NativeLibrary.reloadKeys()) {
+                Toast.makeText(
                     applicationContext,
                     applicationContext,
-                    result,
-                    dstPath,
-                    "prod.keys"
-                )
-            ) {
-                if (NativeLibrary.reloadKeys()) {
-                    Toast.makeText(
-                        applicationContext,
-                        R.string.install_keys_success,
-                        Toast.LENGTH_SHORT
-                    ).show()
-                    gamesViewModel.reloadGames(true)
-                } else {
-                    MessageDialogFragment.newInstance(
-                        R.string.invalid_keys_error,
-                        R.string.install_keys_failure_description,
-                        R.string.dumping_keys_quickstart_link
-                    ).show(supportFragmentManager, MessageDialogFragment.TAG)
-                }
+                    R.string.install_keys_success,
+                    Toast.LENGTH_SHORT
+                ).show()
+                gamesViewModel.reloadGames(true)
+                return true
+            } else {
+                MessageDialogFragment.newInstance(
+                    R.string.invalid_keys_error,
+                    R.string.install_keys_failure_description,
+                    R.string.dumping_keys_quickstart_link
+                ).show(supportFragmentManager, MessageDialogFragment.TAG)
+                return false
             }
             }
         }
         }
+        return false
+    }
 
 
     val getFirmware =
     val getFirmware =
         registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
         registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->

+ 35 - 0
src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt

@@ -0,0 +1,35 @@
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.utils
+
+import android.view.View
+
+object ViewUtils {
+    fun showView(view: View, length: Long = 300) {
+        view.apply {
+            alpha = 0f
+            visibility = View.VISIBLE
+            isClickable = true
+        }.animate().apply {
+            duration = length
+            alpha(1f)
+        }.start()
+    }
+
+    fun hideView(view: View, length: Long = 300) {
+        if (view.visibility == View.INVISIBLE) {
+            return
+        }
+
+        view.apply {
+            alpha = 1f
+            isClickable = false
+        }.animate().apply {
+            duration = length
+            alpha(0f)
+        }.withEndAction {
+            view.visibility = View.INVISIBLE
+        }.start()
+    }
+}

+ 50 - 19
src/android/app/src/main/res/layout-w600dp/page_setup.xml

@@ -21,45 +21,76 @@
 
 
     </LinearLayout>
     </LinearLayout>
 
 
-    <LinearLayout
+    <androidx.constraintlayout.widget.ConstraintLayout
         android:layout_width="match_parent"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:layout_height="match_parent"
-        android:layout_weight="1"
-        android:orientation="vertical"
-        android:gravity="center">
+        android:layout_weight="1">
 
 
         <com.google.android.material.textview.MaterialTextView
         <com.google.android.material.textview.MaterialTextView
-            style="@style/TextAppearance.Material3.DisplaySmall"
             android:id="@+id/text_title"
             android:id="@+id/text_title"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:textAlignment="center"
+            style="@style/TextAppearance.Material3.DisplaySmall"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:gravity="center"
             android:textColor="?attr/colorOnSurface"
             android:textColor="?attr/colorOnSurface"
             android:textStyle="bold"
             android:textStyle="bold"
+            app:layout_constraintBottom_toTopOf="@+id/text_description"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintVertical_weight="2"
             tools:text="@string/welcome" />
             tools:text="@string/welcome" />
 
 
         <com.google.android.material.textview.MaterialTextView
         <com.google.android.material.textview.MaterialTextView
-            style="@style/TextAppearance.Material3.TitleLarge"
             android:id="@+id/text_description"
             android:id="@+id/text_description"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="16dp"
-            android:paddingHorizontal="32dp"
-            android:textAlignment="center"
-            android:textSize="26sp"
-            app:lineHeight="40sp"
+            style="@style/TextAppearance.Material3.TitleLarge"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:gravity="center"
+            android:textSize="20sp"
+            android:paddingHorizontal="16dp"
+            app:layout_constraintBottom_toTopOf="@+id/button_action"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@+id/text_title"
+            app:layout_constraintVertical_weight="2"
+            app:lineHeight="30sp"
             tools:text="@string/welcome_description" />
             tools:text="@string/welcome_description" />
 
 
+        <com.google.android.material.textview.MaterialTextView
+            android:id="@+id/text_confirmation"
+            style="@style/TextAppearance.Material3.TitleLarge"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:paddingHorizontal="16dp"
+            android:paddingBottom="20dp"
+            android:gravity="center"
+            android:textSize="30sp"
+            android:visibility="invisible"
+            android:text="@string/step_complete"
+            android:textStyle="bold"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@+id/text_description"
+            app:layout_constraintVertical_weight="1"
+            app:lineHeight="30sp" />
+
         <com.google.android.material.button.MaterialButton
         <com.google.android.material.button.MaterialButton
             android:id="@+id/button_action"
             android:id="@+id/button_action"
             android:layout_width="wrap_content"
             android:layout_width="wrap_content"
             android:layout_height="56dp"
             android:layout_height="56dp"
-            android:layout_marginTop="32dp"
+            android:layout_marginTop="16dp"
+            android:layout_marginBottom="48dp"
             android:textSize="20sp"
             android:textSize="20sp"
-            app:iconSize="24sp"
             app:iconGravity="end"
             app:iconGravity="end"
+            app:iconSize="24sp"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@+id/text_description"
             tools:text="Get started" />
             tools:text="Get started" />
 
 
-    </LinearLayout>
+    </androidx.constraintlayout.widget.ConstraintLayout>
 
 
 </LinearLayout>
 </LinearLayout>

+ 25 - 5
src/android/app/src/main/res/layout/page_setup.xml

@@ -21,11 +21,12 @@
         app:layout_constraintVertical_chainStyle="spread"
         app:layout_constraintVertical_chainStyle="spread"
         app:layout_constraintWidth_max="220dp"
         app:layout_constraintWidth_max="220dp"
         app:layout_constraintWidth_min="110dp"
         app:layout_constraintWidth_min="110dp"
-        app:layout_constraintVertical_weight="3" />
+        app:layout_constraintVertical_weight="3"
+        tools:src="@drawable/ic_notification" />
 
 
     <com.google.android.material.textview.MaterialTextView
     <com.google.android.material.textview.MaterialTextView
         android:id="@+id/text_title"
         android:id="@+id/text_title"
-        style="@style/TextAppearance.Material3.DisplayMedium"
+        style="@style/TextAppearance.Material3.DisplaySmall"
         android:layout_width="0dp"
         android:layout_width="0dp"
         android:layout_height="0dp"
         android:layout_height="0dp"
         android:textAlignment="center"
         android:textAlignment="center"
@@ -44,23 +45,42 @@
         android:layout_width="0dp"
         android:layout_width="0dp"
         android:layout_height="0dp"
         android:layout_height="0dp"
         android:textAlignment="center"
         android:textAlignment="center"
-        android:textSize="26sp"
+        android:textSize="20sp"
         android:paddingHorizontal="16dp"
         android:paddingHorizontal="16dp"
         app:layout_constraintBottom_toTopOf="@+id/button_action"
         app:layout_constraintBottom_toTopOf="@+id/button_action"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toBottomOf="@+id/text_title"
         app:layout_constraintTop_toBottomOf="@+id/text_title"
         app:layout_constraintVertical_weight="2"
         app:layout_constraintVertical_weight="2"
-        app:lineHeight="40sp"
+        app:lineHeight="30sp"
         tools:text="@string/welcome_description" />
         tools:text="@string/welcome_description" />
 
 
+    <com.google.android.material.textview.MaterialTextView
+        android:id="@+id/text_confirmation"
+        style="@style/TextAppearance.Material3.TitleLarge"
+        android:layout_width="wrap_content"
+        android:layout_height="0dp"
+        android:paddingHorizontal="16dp"
+        android:paddingTop="24dp"
+        android:textAlignment="center"
+        android:textSize="30sp"
+        android:visibility="invisible"
+        android:text="@string/step_complete"
+        android:textStyle="bold"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/text_description"
+        app:layout_constraintVertical_weight="1"
+        app:lineHeight="30sp" />
+
     <com.google.android.material.button.MaterialButton
     <com.google.android.material.button.MaterialButton
         android:id="@+id/button_action"
         android:id="@+id/button_action"
         android:layout_width="wrap_content"
         android:layout_width="wrap_content"
         android:layout_height="56dp"
         android:layout_height="56dp"
-        android:textSize="20sp"
         android:layout_marginTop="16dp"
         android:layout_marginTop="16dp"
         android:layout_marginBottom="48dp"
         android:layout_marginBottom="48dp"
+        android:textSize="20sp"
         app:iconGravity="end"
         app:iconGravity="end"
         app:iconSize="24sp"
         app:iconSize="24sp"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintBottom_toBottomOf="parent"

+ 1 - 0
src/android/app/src/main/res/values/strings.xml

@@ -29,6 +29,7 @@
     <string name="back">Back</string>
     <string name="back">Back</string>
     <string name="add_games">Add Games</string>
     <string name="add_games">Add Games</string>
     <string name="add_games_description">Select your games folder</string>
     <string name="add_games_description">Select your games folder</string>
+    <string name="step_complete">Complete!</string>
 
 
     <!-- Home strings -->
     <!-- Home strings -->
     <string name="home_games">Games</string>
     <string name="home_games">Games</string>