Merge pull request #11273 from t895/setup-completion
android: Setup additions
This commit is contained in:
commit
bbc6b08fc7
|
@ -5,13 +5,19 @@ 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.lifecycle.ViewModelProvider
|
||||||
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.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.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 +32,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 +41,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 +74,15 @@ 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)
|
||||||
|
ViewModelProvider(activity)[HomeViewModel::class.java].setShouldPageForward(true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.core.view.WindowInsetsCompat
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.core.view.updatePadding
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.navigation.findNavController
|
import androidx.navigation.findNavController
|
||||||
|
@ -32,10 +33,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 +116,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 +145,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 +172,8 @@ class SetupFragment : Fragment() {
|
||||||
true,
|
true,
|
||||||
R.string.add_games,
|
R.string.add_games,
|
||||||
{
|
{
|
||||||
mainActivity.getGamesDirectory.launch(
|
gamesDirCallback = it
|
||||||
Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data
|
getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data)
|
||||||
)
|
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
R.string.add_games_warning,
|
R.string.add_games_warning,
|
||||||
|
@ -163,7 +184,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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -181,6 +206,13 @@ class SetupFragment : Fragment() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
homeViewModel.shouldPageForward.observe(viewLifecycleOwner) {
|
||||||
|
if (it) {
|
||||||
|
pageForward()
|
||||||
|
homeViewModel.setShouldPageForward(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
binding.viewPager2.apply {
|
binding.viewPager2.apply {
|
||||||
adapter = SetupAdapter(requireActivity() as AppCompatActivity, pages)
|
adapter = SetupAdapter(requireActivity() as AppCompatActivity, pages)
|
||||||
offscreenPageLimit = 2
|
offscreenPageLimit = 2
|
||||||
|
@ -194,15 +226,15 @@ class SetupFragment : Fragment() {
|
||||||
super.onPageSelected(position)
|
super.onPageSelected(position)
|
||||||
|
|
||||||
if (position == 1 && previousPosition == 0) {
|
if (position == 1 && previousPosition == 0) {
|
||||||
showView(binding.buttonNext)
|
ViewUtils.showView(binding.buttonNext)
|
||||||
showView(binding.buttonBack)
|
ViewUtils.showView(binding.buttonBack)
|
||||||
} else if (position == 0 && previousPosition == 1) {
|
} else if (position == 0 && previousPosition == 1) {
|
||||||
hideView(binding.buttonBack)
|
ViewUtils.hideView(binding.buttonBack)
|
||||||
hideView(binding.buttonNext)
|
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 +247,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 +297,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 +316,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 +344,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
|
||||||
}
|
}
|
||||||
|
@ -326,15 +359,29 @@ class SetupFragment : Fragment() {
|
||||||
private fun setInsets() =
|
private fun setInsets() =
|
||||||
ViewCompat.setOnApplyWindowInsetsListener(
|
ViewCompat.setOnApplyWindowInsetsListener(
|
||||||
binding.root
|
binding.root
|
||||||
) { view: View, windowInsets: WindowInsetsCompat ->
|
) { _: View, windowInsets: WindowInsetsCompat ->
|
||||||
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||||
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
|
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
|
||||||
view.setPadding(
|
|
||||||
barInsets.left + cutoutInsets.left,
|
val leftPadding = barInsets.left + cutoutInsets.left
|
||||||
barInsets.top + cutoutInsets.top,
|
val topPadding = barInsets.top + cutoutInsets.top
|
||||||
barInsets.right + cutoutInsets.right,
|
val rightPadding = barInsets.right + cutoutInsets.right
|
||||||
barInsets.bottom + cutoutInsets.bottom
|
val bottomPadding = barInsets.bottom + cutoutInsets.bottom
|
||||||
)
|
|
||||||
|
if (resources.getBoolean(R.bool.small_layout)) {
|
||||||
|
binding.viewPager2
|
||||||
|
.updatePadding(left = leftPadding, top = topPadding, right = rightPadding)
|
||||||
|
binding.constraintButtons
|
||||||
|
.updatePadding(left = leftPadding, right = rightPadding, bottom = bottomPadding)
|
||||||
|
} else {
|
||||||
|
binding.viewPager2.updatePadding(top = topPadding, bottom = bottomPadding)
|
||||||
|
binding.constraintButtons
|
||||||
|
.updatePadding(
|
||||||
|
left = leftPadding,
|
||||||
|
right = rightPadding,
|
||||||
|
bottom = bottomPadding
|
||||||
|
)
|
||||||
|
}
|
||||||
windowInsets
|
windowInsets
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,9 @@ class HomeViewModel : ViewModel() {
|
||||||
private val _statusBarShadeVisible = MutableLiveData(true)
|
private val _statusBarShadeVisible = MutableLiveData(true)
|
||||||
val statusBarShadeVisible: LiveData<Boolean> get() = _statusBarShadeVisible
|
val statusBarShadeVisible: LiveData<Boolean> get() = _statusBarShadeVisible
|
||||||
|
|
||||||
|
private val _shouldPageForward = MutableLiveData(false)
|
||||||
|
val shouldPageForward: LiveData<Boolean> get() = _shouldPageForward
|
||||||
|
|
||||||
var navigatedToSetup = false
|
var navigatedToSetup = false
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
@ -33,4 +36,8 @@ class HomeViewModel : ViewModel() {
|
||||||
}
|
}
|
||||||
_statusBarShadeVisible.value = visible
|
_statusBarShadeVisible.value = visible
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setShouldPageForward(pageForward: Boolean) {
|
||||||
|
_shouldPageForward.value = pageForward
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -266,74 +266,81 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||||
|
|
||||||
val getGamesDirectory =
|
val getGamesDirectory =
|
||||||
registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
|
registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
|
||||||
if (result == null) {
|
if (result != null) {
|
||||||
return@registerForActivityResult
|
processGamesDir(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
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()
|
|
||||||
|
|
||||||
Toast.makeText(
|
|
||||||
applicationContext,
|
|
||||||
R.string.games_dir_selected,
|
|
||||||
Toast.LENGTH_LONG
|
|
||||||
).show()
|
|
||||||
|
|
||||||
gamesViewModel.reloadGames(true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
Toast.makeText(
|
||||||
|
applicationContext,
|
||||||
|
R.string.games_dir_selected,
|
||||||
|
Toast.LENGTH_LONG
|
||||||
|
).show()
|
||||||
|
|
||||||
|
gamesViewModel.reloadGames(true)
|
||||||
|
}
|
||||||
|
|
||||||
val getProdKey =
|
val getProdKey =
|
||||||
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
|
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
|
||||||
if (result == null) {
|
if (result != null) {
|
||||||
return@registerForActivityResult
|
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
|
|
||||||
}
|
|
||||||
|
|
||||||
contentResolver.takePersistableUriPermission(
|
|
||||||
result,
|
|
||||||
Intent.FLAG_GRANT_READ_URI_PERMISSION
|
|
||||||
)
|
|
||||||
|
|
||||||
val dstPath = DirectoryInitialization.userDirectory + "/keys/"
|
|
||||||
if (FileUtil.copyUriToInternalStorage(
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(
|
||||||
|
result,
|
||||||
|
Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||||
|
)
|
||||||
|
|
||||||
|
val dstPath = DirectoryInitialization.userDirectory + "/keys/"
|
||||||
|
if (FileUtil.copyUriToInternalStorage(
|
||||||
|
applicationContext,
|
||||||
|
result,
|
||||||
|
dstPath,
|
||||||
|
"prod.keys"
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
if (NativeLibrary.reloadKeys()) {
|
||||||
|
Toast.makeText(
|
||||||
|
applicationContext,
|
||||||
|
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 ->
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<RelativeLayout
|
||||||
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/setup_root"
|
android:id="@+id/setup_root"
|
||||||
|
@ -8,33 +8,39 @@
|
||||||
|
|
||||||
<androidx.viewpager2.widget.ViewPager2
|
<androidx.viewpager2.widget.ViewPager2
|
||||||
android:id="@+id/viewPager2"
|
android:id="@+id/viewPager2"
|
||||||
android:layout_width="0dp"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="match_parent"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
android:layout_alignParentTop="true"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
android:layout_alignParentBottom="true"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
android:clipToPadding="false" />
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
style="@style/Widget.Material3.Button.TextButton"
|
android:id="@+id/constraint_buttons"
|
||||||
android:id="@+id/button_next"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="16dp"
|
android:layout_alignParentBottom="true"
|
||||||
android:text="@string/next"
|
android:layout_margin="8dp">
|
||||||
android:visibility="invisible"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/button_back"
|
android:id="@+id/button_next"
|
||||||
style="@style/Widget.Material3.Button.TextButton"
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="16dp"
|
android:text="@string/next"
|
||||||
android:text="@string/back"
|
android:visibility="invisible"
|
||||||
android:visibility="invisible"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
app:layout_constraintStart_toStartOf="parent" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/button_back"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/back"
|
||||||
|
android:visibility="invisible"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
|
@ -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:layout_weight="1">
|
||||||
android:orientation="vertical"
|
|
||||||
android:gravity="center">
|
|
||||||
|
|
||||||
<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"
|
style="@style/TextAppearance.Material3.DisplaySmall"
|
||||||
android:layout_height="wrap_content"
|
android:layout_width="0dp"
|
||||||
android:textAlignment="center"
|
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"
|
style="@style/TextAppearance.Material3.TitleLarge"
|
||||||
android:layout_height="wrap_content"
|
android:layout_width="0dp"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_height="0dp"
|
||||||
android:paddingHorizontal="32dp"
|
android:gravity="center"
|
||||||
android:textAlignment="center"
|
android:textSize="20sp"
|
||||||
android:textSize="26sp"
|
android:paddingHorizontal="16dp"
|
||||||
app:lineHeight="40sp"
|
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>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<RelativeLayout
|
||||||
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/setup_root"
|
android:id="@+id/setup_root"
|
||||||
|
@ -8,35 +8,39 @@
|
||||||
|
|
||||||
<androidx.viewpager2.widget.ViewPager2
|
<androidx.viewpager2.widget.ViewPager2
|
||||||
android:id="@+id/viewPager2"
|
android:id="@+id/viewPager2"
|
||||||
android:layout_width="0dp"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
android:layout_marginBottom="16dp"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/button_next"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
style="@style/Widget.Material3.Button.TextButton"
|
|
||||||
android:id="@+id/button_next"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="12dp"
|
android:layout_above="@+id/constraint_buttons"
|
||||||
android:text="@string/next"
|
android:layout_alignParentTop="true"
|
||||||
android:visibility="invisible"
|
android:clipToPadding="false" />
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
style="@style/Widget.Material3.Button.TextButton"
|
android:id="@+id/constraint_buttons"
|
||||||
android:id="@+id/button_back"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="12dp"
|
android:layout_margin="8dp"
|
||||||
android:text="@string/back"
|
android:layout_alignParentBottom="true">
|
||||||
android:visibility="invisible"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/button_next"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/next"
|
||||||
|
android:visibility="invisible"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/button_back"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/back"
|
||||||
|
android:visibility="invisible"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Reference in New Issue