android: Rework setup fragment to use multiple buttons per-page (#2854)
Adapted from f771952e62 (diff-e59f69380a076aef2745f7ab65072ca25fc26c598e2ed177475a15fe44121b4d)
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2854
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Co-authored-by: kleidis <kleidis1@protonmail.com>
Co-committed-by: kleidis <kleidis1@protonmail.com>
This commit is contained in:
parent
55cc4d5ede
commit
f882ff72eb
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|
@ -8,16 +11,16 @@ import android.view.LayoutInflater
|
||||||
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 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.PageState
|
||||||
import org.yuzu.yuzu_emu.model.SetupCallback
|
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
|
import org.yuzu.yuzu_emu.utils.ViewUtils
|
||||||
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
|
|
||||||
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
|
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
|
||||||
|
import android.content.res.ColorStateList
|
||||||
|
import org.yuzu.yuzu_emu.R
|
||||||
|
import org.yuzu.yuzu_emu.model.ButtonState
|
||||||
|
|
||||||
class SetupAdapter(val activity: AppCompatActivity, pages: List<SetupPage>) :
|
class SetupAdapter(val activity: AppCompatActivity, pages: List<SetupPage>) :
|
||||||
AbstractListAdapter<SetupPage, SetupAdapter.SetupPageViewHolder>(pages) {
|
AbstractListAdapter<SetupPage, SetupAdapter.SetupPageViewHolder>(pages) {
|
||||||
|
|
@ -29,9 +32,40 @@ class SetupAdapter(val activity: AppCompatActivity, pages: List<SetupPage>) :
|
||||||
inner class SetupPageViewHolder(val binding: PageSetupBinding) :
|
inner class SetupPageViewHolder(val binding: PageSetupBinding) :
|
||||||
AbstractViewHolder<SetupPage>(binding), SetupCallback {
|
AbstractViewHolder<SetupPage>(binding), SetupCallback {
|
||||||
override fun bind(model: SetupPage) {
|
override fun bind(model: SetupPage) {
|
||||||
if (model.stepCompleted.invoke() == StepState.COMPLETE) {
|
if (model.pageSteps.invoke() == PageState.COMPLETE) {
|
||||||
binding.buttonAction.setVisible(visible = false, gone = false)
|
onStepCompleted(0, pageFullyCompleted = true)
|
||||||
binding.textConfirmation.setVisible(true)
|
}
|
||||||
|
|
||||||
|
if (model.pageButtons != null && model.pageSteps.invoke() != PageState.COMPLETE) {
|
||||||
|
for (pageButton in model.pageButtons) {
|
||||||
|
val pageButtonView = LayoutInflater.from(activity)
|
||||||
|
.inflate(
|
||||||
|
R.layout.page_button,
|
||||||
|
binding.pageButtonContainer,
|
||||||
|
false
|
||||||
|
) as MaterialButton
|
||||||
|
|
||||||
|
pageButtonView.apply {
|
||||||
|
id = pageButton.titleId
|
||||||
|
icon = ResourcesCompat.getDrawable(
|
||||||
|
activity.resources,
|
||||||
|
pageButton.iconId,
|
||||||
|
activity.theme
|
||||||
|
)
|
||||||
|
text = activity.resources.getString(pageButton.titleId)
|
||||||
|
}
|
||||||
|
|
||||||
|
pageButtonView.setOnClickListener {
|
||||||
|
pageButton.buttonAction.invoke(this@SetupPageViewHolder)
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.pageButtonContainer.addView(pageButtonView)
|
||||||
|
|
||||||
|
// Disable buton add if its already completed
|
||||||
|
if (pageButton.buttonState.invoke() == ButtonState.BUTTON_ACTION_COMPLETE) {
|
||||||
|
onStepCompleted(pageButton.titleId, pageFullyCompleted = false)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.icon.setImageDrawable(
|
binding.icon.setImageDrawable(
|
||||||
|
|
@ -44,32 +78,26 @@ class SetupAdapter(val activity: AppCompatActivity, pages: List<SetupPage>) :
|
||||||
binding.textTitle.text = activity.resources.getString(model.titleId)
|
binding.textTitle.text = activity.resources.getString(model.titleId)
|
||||||
binding.textDescription.text =
|
binding.textDescription.text =
|
||||||
Html.fromHtml(activity.resources.getString(model.descriptionId), 0)
|
Html.fromHtml(activity.resources.getString(model.descriptionId), 0)
|
||||||
|
|
||||||
binding.buttonAction.apply {
|
|
||||||
text = activity.resources.getString(model.buttonTextId)
|
|
||||||
if (model.buttonIconId != 0) {
|
|
||||||
icon = ResourcesCompat.getDrawable(
|
|
||||||
activity.resources,
|
|
||||||
model.buttonIconId,
|
|
||||||
activity.theme
|
|
||||||
)
|
|
||||||
}
|
|
||||||
iconGravity =
|
|
||||||
if (model.leftAlignedIcon) {
|
|
||||||
MaterialButton.ICON_GRAVITY_START
|
|
||||||
} else {
|
|
||||||
MaterialButton.ICON_GRAVITY_END
|
|
||||||
}
|
|
||||||
setOnClickListener {
|
|
||||||
model.buttonAction.invoke(this@SetupPageViewHolder)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStepCompleted() {
|
override fun onStepCompleted(pageButtonId: Int, pageFullyCompleted: Boolean) {
|
||||||
ViewUtils.hideView(binding.buttonAction, 200)
|
val button = binding.pageButtonContainer.findViewById<MaterialButton>(pageButtonId)
|
||||||
ViewUtils.showView(binding.textConfirmation, 200)
|
|
||||||
ViewModelProvider(activity)[HomeViewModel::class.java].setShouldPageForward(true)
|
if (pageFullyCompleted) {
|
||||||
|
ViewUtils.hideView(binding.pageButtonContainer, 200)
|
||||||
|
ViewUtils.showView(binding.textConfirmation, 200)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (button != null) {
|
||||||
|
button.isEnabled = false
|
||||||
|
button.animate()
|
||||||
|
.alpha(0.38f)
|
||||||
|
.setDuration(200)
|
||||||
|
.start()
|
||||||
|
button.setTextColor(button.context.getColor(com.google.android.material.R.color.material_on_surface_disabled))
|
||||||
|
button.iconTint =
|
||||||
|
ColorStateList.valueOf(button.context.getColor(com.google.android.material.R.color.material_on_surface_disabled))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
|
@ -32,12 +35,14 @@ class AddGameFolderDialogFragment : DialogFragment() {
|
||||||
.setTitle(R.string.add_game_folder)
|
.setTitle(R.string.add_game_folder)
|
||||||
.setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int ->
|
.setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int ->
|
||||||
val newGameDir = GameDir(folderUriString!!, binding.deepScanSwitch.isChecked)
|
val newGameDir = GameDir(folderUriString!!, binding.deepScanSwitch.isChecked)
|
||||||
homeViewModel.setGamesDirSelected(true)
|
|
||||||
val calledFromGameFragment = requireArguments().getBoolean(
|
val calledFromGameFragment = requireArguments().getBoolean(
|
||||||
"calledFromGameFragment",
|
"calledFromGameFragment",
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
gamesViewModel.addFolder(newGameDir, calledFromGameFragment)
|
val job = gamesViewModel.addFolder(newGameDir, calledFromGameFragment)
|
||||||
|
job.invokeOnCompletion {
|
||||||
|
homeViewModel.setGamesDirSelected(true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.setNegativeButton(android.R.string.cancel, null)
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
.setView(binding.root)
|
.setView(binding.root)
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ import androidx.navigation.findNavController
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
|
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
|
||||||
import com.google.android.material.transition.MaterialFadeThrough
|
import com.google.android.material.transition.MaterialFadeThrough
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import org.yuzu.yuzu_emu.NativeLibrary
|
import org.yuzu.yuzu_emu.NativeLibrary
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
|
|
@ -34,10 +33,13 @@ import org.yuzu.yuzu_emu.YuzuApplication
|
||||||
import org.yuzu.yuzu_emu.adapters.SetupAdapter
|
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.ButtonState
|
||||||
|
import org.yuzu.yuzu_emu.model.GamesViewModel
|
||||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||||
|
import org.yuzu.yuzu_emu.model.PageButton
|
||||||
import org.yuzu.yuzu_emu.model.SetupCallback
|
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.model.PageState
|
||||||
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.NativeConfig
|
import org.yuzu.yuzu_emu.utils.NativeConfig
|
||||||
|
|
@ -50,11 +52,16 @@ class SetupFragment : Fragment() {
|
||||||
private val binding get() = _binding!!
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
private val homeViewModel: HomeViewModel by activityViewModels()
|
private val homeViewModel: HomeViewModel by activityViewModels()
|
||||||
|
private val gamesViewModel: GamesViewModel by activityViewModels()
|
||||||
|
|
||||||
private lateinit var mainActivity: MainActivity
|
private lateinit var mainActivity: MainActivity
|
||||||
|
|
||||||
private lateinit var hasBeenWarned: BooleanArray
|
private lateinit var hasBeenWarned: BooleanArray
|
||||||
|
|
||||||
|
private lateinit var pages: MutableList<SetupPage>
|
||||||
|
|
||||||
|
private lateinit var pageButtonCallback: SetupCallback
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val KEY_NEXT_VISIBILITY = "NextButtonVisibility"
|
const val KEY_NEXT_VISIBILITY = "NextButtonVisibility"
|
||||||
const val KEY_BACK_VISIBILITY = "BackButtonVisibility"
|
const val KEY_BACK_VISIBILITY = "BackButtonVisibility"
|
||||||
|
|
@ -94,124 +101,142 @@ class SetupFragment : Fragment() {
|
||||||
requireActivity().window.navigationBarColor =
|
requireActivity().window.navigationBarColor =
|
||||||
ContextCompat.getColor(requireContext(), android.R.color.transparent)
|
ContextCompat.getColor(requireContext(), android.R.color.transparent)
|
||||||
|
|
||||||
val pages = mutableListOf<SetupPage>()
|
pages = mutableListOf<SetupPage>()
|
||||||
pages.apply {
|
pages.apply {
|
||||||
add(
|
add(
|
||||||
SetupPage(
|
SetupPage(
|
||||||
R.drawable.ic_yuzu_title,
|
R.drawable.ic_permission,
|
||||||
R.string.welcome,
|
R.string.permissions,
|
||||||
R.string.welcome_description,
|
R.string.permissions_description,
|
||||||
0,
|
mutableListOf<PageButton>().apply {
|
||||||
true,
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
R.string.get_started,
|
add(
|
||||||
{ pageForward() },
|
PageButton(
|
||||||
false
|
R.drawable.ic_notification,
|
||||||
)
|
R.string.notifications,
|
||||||
)
|
R.string.notifications_description,
|
||||||
|
{
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
pageButtonCallback = it
|
||||||
add(
|
permissionLauncher.launch(
|
||||||
SetupPage(
|
Manifest.permission.POST_NOTIFICATIONS
|
||||||
R.drawable.ic_notification,
|
)
|
||||||
R.string.notifications,
|
},
|
||||||
R.string.notifications_description,
|
{
|
||||||
0,
|
if (NotificationManagerCompat.from(requireContext())
|
||||||
false,
|
.areNotificationsEnabled()
|
||||||
R.string.give_permission,
|
) {
|
||||||
{
|
ButtonState.BUTTON_ACTION_COMPLETE
|
||||||
notificationCallback = it
|
} else {
|
||||||
permissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
|
ButtonState.BUTTON_ACTION_INCOMPLETE
|
||||||
},
|
}
|
||||||
true,
|
},
|
||||||
R.string.notification_warning,
|
false,
|
||||||
R.string.notification_warning_description,
|
false,
|
||||||
0,
|
)
|
||||||
{
|
)
|
||||||
if (NotificationManagerCompat.from(requireContext())
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
if (NotificationManagerCompat.from(requireContext())
|
||||||
.areNotificationsEnabled()
|
.areNotificationsEnabled()
|
||||||
) {
|
) {
|
||||||
StepState.COMPLETE
|
PageState.COMPLETE
|
||||||
} else {
|
|
||||||
StepState.INCOMPLETE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
add(
|
|
||||||
SetupPage(
|
|
||||||
R.drawable.ic_key,
|
|
||||||
R.string.keys,
|
|
||||||
R.string.keys_description,
|
|
||||||
R.drawable.ic_add,
|
|
||||||
true,
|
|
||||||
R.string.select_keys,
|
|
||||||
{
|
|
||||||
keyCallback = it
|
|
||||||
getProdKey.launch(arrayOf("*/*"))
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
R.string.install_prod_keys_warning,
|
|
||||||
R.string.install_prod_keys_warning_description,
|
|
||||||
R.string.install_prod_keys_warning_help,
|
|
||||||
{
|
|
||||||
val file = File(DirectoryInitialization.userDirectory + "/keys/prod.keys")
|
|
||||||
if (file.exists() && NativeLibrary.areKeysPresent()) {
|
|
||||||
StepState.COMPLETE
|
|
||||||
} else {
|
} else {
|
||||||
StepState.INCOMPLETE
|
PageState.INCOMPLETE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
add(
|
add(
|
||||||
SetupPage(
|
SetupPage(
|
||||||
R.drawable.ic_firmware,
|
R.drawable.ic_folder_open,
|
||||||
R.string.firmware,
|
R.string.emulator_data,
|
||||||
R.string.firmware_description,
|
R.string.emulator_data_description,
|
||||||
R.drawable.ic_add,
|
mutableListOf<PageButton>().apply {
|
||||||
true,
|
add(
|
||||||
R.string.select_firmware,
|
PageButton(
|
||||||
{
|
R.drawable.ic_key,
|
||||||
firmwareCallback = it
|
R.string.keys,
|
||||||
getFirmware.launch(arrayOf("application/zip"))
|
R.string.keys_description,
|
||||||
|
{
|
||||||
|
pageButtonCallback = it
|
||||||
|
getProdKey.launch(arrayOf("*/*"))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
val file = File(
|
||||||
|
DirectoryInitialization.userDirectory + "/keys/prod.keys"
|
||||||
|
)
|
||||||
|
if (file.exists() && NativeLibrary.areKeysPresent()) {
|
||||||
|
ButtonState.BUTTON_ACTION_COMPLETE
|
||||||
|
} else {
|
||||||
|
ButtonState.BUTTON_ACTION_INCOMPLETE
|
||||||
|
}
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
R.string.install_prod_keys_warning,
|
||||||
|
R.string.install_prod_keys_warning_description,
|
||||||
|
R.string.install_prod_keys_warning_help,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
add(
|
||||||
|
PageButton(
|
||||||
|
R.drawable.ic_firmware,
|
||||||
|
R.string.firmware,
|
||||||
|
R.string.firmware_description,
|
||||||
|
{
|
||||||
|
pageButtonCallback = it
|
||||||
|
getFirmware.launch(arrayOf("application/zip"))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
if (NativeLibrary.isFirmwareAvailable()) {
|
||||||
|
ButtonState.BUTTON_ACTION_COMPLETE
|
||||||
|
} else {
|
||||||
|
ButtonState.BUTTON_ACTION_INCOMPLETE
|
||||||
|
}
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
R.string.install_firmware_warning,
|
||||||
|
R.string.install_firmware_warning_description,
|
||||||
|
R.string.install_firmware_warning_help,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
add(
|
||||||
|
PageButton(
|
||||||
|
R.drawable.ic_controller,
|
||||||
|
R.string.games,
|
||||||
|
R.string.games_description,
|
||||||
|
{
|
||||||
|
pageButtonCallback = it
|
||||||
|
getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
if (NativeConfig.getGameDirs().isNotEmpty()) {
|
||||||
|
ButtonState.BUTTON_ACTION_COMPLETE
|
||||||
|
} else {
|
||||||
|
ButtonState.BUTTON_ACTION_INCOMPLETE
|
||||||
|
}
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
R.string.add_games_warning,
|
||||||
|
R.string.add_games_warning_description,
|
||||||
|
R.string.add_games_warning_help,
|
||||||
|
)
|
||||||
|
)
|
||||||
},
|
},
|
||||||
true,
|
|
||||||
R.string.install_firmware_warning,
|
|
||||||
R.string.install_firmware_warning_description,
|
|
||||||
R.string.install_firmware_warning_help,
|
|
||||||
{
|
{
|
||||||
if (NativeLibrary.isFirmwareAvailable()) {
|
val file = File(
|
||||||
StepState.COMPLETE
|
DirectoryInitialization.userDirectory + "/keys/prod.keys"
|
||||||
|
)
|
||||||
|
if (file.exists() && NativeLibrary.areKeysPresent() &&
|
||||||
|
NativeLibrary.isFirmwareAvailable() && NativeConfig.getGameDirs()
|
||||||
|
.isNotEmpty()
|
||||||
|
) {
|
||||||
|
PageState.COMPLETE
|
||||||
} else {
|
} else {
|
||||||
StepState.INCOMPLETE
|
PageState.INCOMPLETE
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
add(
|
|
||||||
SetupPage(
|
|
||||||
R.drawable.ic_controller,
|
|
||||||
R.string.games,
|
|
||||||
R.string.games_description,
|
|
||||||
R.drawable.ic_add,
|
|
||||||
true,
|
|
||||||
R.string.add_games,
|
|
||||||
{
|
|
||||||
gamesDirCallback = it
|
|
||||||
getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data)
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
R.string.add_games_warning,
|
|
||||||
R.string.add_games_warning_description,
|
|
||||||
R.string.add_games_warning_help,
|
|
||||||
{
|
|
||||||
if (NativeConfig.getGameDirs().isNotEmpty()) {
|
|
||||||
StepState.COMPLETE
|
|
||||||
} else {
|
|
||||||
StepState.INCOMPLETE
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -221,12 +246,22 @@ class SetupFragment : Fragment() {
|
||||||
R.drawable.ic_check,
|
R.drawable.ic_check,
|
||||||
R.string.done,
|
R.string.done,
|
||||||
R.string.done_description,
|
R.string.done_description,
|
||||||
R.drawable.ic_arrow_forward,
|
mutableListOf<PageButton>().apply {
|
||||||
false,
|
add(
|
||||||
R.string.text_continue,
|
PageButton(
|
||||||
{ finishSetup() },
|
R.drawable.ic_arrow_forward,
|
||||||
false
|
R.string.get_started,
|
||||||
)
|
0,
|
||||||
|
buttonAction = {
|
||||||
|
finishSetup()
|
||||||
|
},
|
||||||
|
buttonState = {
|
||||||
|
ButtonState.BUTTON_ACTION_UNDEFINED
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) { PageState.UNDEFINED }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -237,7 +272,7 @@ class SetupFragment : Fragment() {
|
||||||
homeViewModel.gamesDirSelected.collect(
|
homeViewModel.gamesDirSelected.collect(
|
||||||
viewLifecycleOwner,
|
viewLifecycleOwner,
|
||||||
resetState = { homeViewModel.setGamesDirSelected(false) }
|
resetState = { homeViewModel.setGamesDirSelected(false) }
|
||||||
) { if (it) gamesDirCallback.onStepCompleted() }
|
) { if (it) checkForButtonState.invoke() }
|
||||||
|
|
||||||
binding.viewPager2.apply {
|
binding.viewPager2.apply {
|
||||||
adapter = SetupAdapter(requireActivity() as AppCompatActivity, pages)
|
adapter = SetupAdapter(requireActivity() as AppCompatActivity, pages)
|
||||||
|
|
@ -251,15 +286,18 @@ class SetupFragment : Fragment() {
|
||||||
override fun onPageSelected(position: Int) {
|
override fun onPageSelected(position: Int) {
|
||||||
super.onPageSelected(position)
|
super.onPageSelected(position)
|
||||||
|
|
||||||
if (position == 1 && previousPosition == 0) {
|
val isFirstPage = position == 0
|
||||||
ViewUtils.showView(binding.buttonNext)
|
val isLastPage = position == pages.size - 1
|
||||||
ViewUtils.showView(binding.buttonBack)
|
|
||||||
} else if (position == 0 && previousPosition == 1) {
|
if (isFirstPage) {
|
||||||
ViewUtils.hideView(binding.buttonBack)
|
ViewUtils.hideView(binding.buttonBack)
|
||||||
|
} else {
|
||||||
|
ViewUtils.showView(binding.buttonBack)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLastPage) {
|
||||||
ViewUtils.hideView(binding.buttonNext)
|
ViewUtils.hideView(binding.buttonNext)
|
||||||
} else if (position == pages.size - 1 && previousPosition == pages.size - 2) {
|
} else {
|
||||||
ViewUtils.hideView(binding.buttonNext)
|
|
||||||
} else if (position == pages.size - 2 && previousPosition == pages.size - 1) {
|
|
||||||
ViewUtils.showView(binding.buttonNext)
|
ViewUtils.showView(binding.buttonNext)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -271,35 +309,63 @@ class SetupFragment : Fragment() {
|
||||||
val index = binding.viewPager2.currentItem
|
val index = binding.viewPager2.currentItem
|
||||||
val currentPage = pages[index]
|
val currentPage = pages[index]
|
||||||
|
|
||||||
// Checks if the user has completed the task on the current page
|
val warningMessages =
|
||||||
if (currentPage.hasWarning) {
|
mutableListOf<Triple<Int, Int, Int>>() // title, description, helpLink
|
||||||
val stepState = currentPage.stepCompleted.invoke()
|
|
||||||
if (stepState != StepState.INCOMPLETE) {
|
|
||||||
pageForward()
|
|
||||||
return@setOnClickListener
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasBeenWarned[index]) {
|
currentPage.pageButtons?.forEach { button ->
|
||||||
SetupWarningDialogFragment.newInstance(
|
if (button.hasWarning || button.isUnskippable) {
|
||||||
currentPage.warningTitleId,
|
val buttonState = button.buttonState()
|
||||||
currentPage.warningDescriptionId,
|
if (buttonState == ButtonState.BUTTON_ACTION_COMPLETE) {
|
||||||
currentPage.warningHelpLinkId,
|
return@forEach
|
||||||
index
|
}
|
||||||
).show(childFragmentManager, SetupWarningDialogFragment.TAG)
|
|
||||||
return@setOnClickListener
|
if (button.isUnskippable) {
|
||||||
|
MessageDialogFragment.newInstance(
|
||||||
|
activity = requireActivity(),
|
||||||
|
titleId = button.warningTitleId,
|
||||||
|
descriptionId = button.warningDescriptionId,
|
||||||
|
helpLinkId = button.warningHelpLinkId
|
||||||
|
).show(childFragmentManager, MessageDialogFragment.TAG)
|
||||||
|
return@setOnClickListener
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasBeenWarned[index]) {
|
||||||
|
warningMessages.add(
|
||||||
|
Triple(
|
||||||
|
button.warningTitleId,
|
||||||
|
button.warningDescriptionId,
|
||||||
|
button.warningHelpLinkId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (warningMessages.isNotEmpty()) {
|
||||||
|
SetupWarningDialogFragment.newInstance(
|
||||||
|
warningMessages.map { it.first }.toIntArray(),
|
||||||
|
warningMessages.map { it.second }.toIntArray(),
|
||||||
|
warningMessages.map { it.third }.toIntArray(),
|
||||||
|
index
|
||||||
|
).show(childFragmentManager, SetupWarningDialogFragment.TAG)
|
||||||
|
return@setOnClickListener
|
||||||
|
}
|
||||||
pageForward()
|
pageForward()
|
||||||
}
|
}
|
||||||
binding.buttonBack.setOnClickListener { pageBackward() }
|
binding.buttonBack.setOnClickListener { pageBackward() }
|
||||||
|
|
||||||
|
|
||||||
if (savedInstanceState != null) {
|
if (savedInstanceState != null) {
|
||||||
val nextIsVisible = savedInstanceState.getBoolean(KEY_NEXT_VISIBILITY)
|
val nextIsVisible = savedInstanceState.getBoolean(KEY_NEXT_VISIBILITY)
|
||||||
val backIsVisible = savedInstanceState.getBoolean(KEY_BACK_VISIBILITY)
|
val backIsVisible = savedInstanceState.getBoolean(KEY_BACK_VISIBILITY)
|
||||||
hasBeenWarned = savedInstanceState.getBooleanArray(KEY_HAS_BEEN_WARNED)!!
|
hasBeenWarned = savedInstanceState.getBooleanArray(KEY_HAS_BEEN_WARNED)!!
|
||||||
|
|
||||||
binding.buttonNext.setVisible(nextIsVisible)
|
if (nextIsVisible) {
|
||||||
binding.buttonBack.setVisible(backIsVisible)
|
binding.buttonNext.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
if (backIsVisible) {
|
||||||
|
binding.buttonBack.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
hasBeenWarned = BooleanArray(pages.size)
|
hasBeenWarned = BooleanArray(pages.size)
|
||||||
}
|
}
|
||||||
|
|
@ -307,6 +373,7 @@ class SetupFragment : Fragment() {
|
||||||
setInsets()
|
setInsets()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun onStop() {
|
override fun onStop() {
|
||||||
super.onStop()
|
super.onStop()
|
||||||
NativeConfig.saveGlobalConfig()
|
NativeConfig.saveGlobalConfig()
|
||||||
|
|
@ -314,10 +381,8 @@ class SetupFragment : Fragment() {
|
||||||
|
|
||||||
override fun onSaveInstanceState(outState: Bundle) {
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
super.onSaveInstanceState(outState)
|
super.onSaveInstanceState(outState)
|
||||||
if (_binding != null) {
|
outState.putBoolean(KEY_NEXT_VISIBILITY, binding.buttonNext.isVisible)
|
||||||
outState.putBoolean(KEY_NEXT_VISIBILITY, binding.buttonNext.isVisible)
|
outState.putBoolean(KEY_BACK_VISIBILITY, binding.buttonBack.isVisible)
|
||||||
outState.putBoolean(KEY_BACK_VISIBILITY, binding.buttonBack.isVisible)
|
|
||||||
}
|
|
||||||
outState.putBooleanArray(KEY_HAS_BEEN_WARNED, hasBeenWarned)
|
outState.putBooleanArray(KEY_HAS_BEEN_WARNED, hasBeenWarned)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -326,13 +391,27 @@ class SetupFragment : Fragment() {
|
||||||
_binding = null
|
_binding = null
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var notificationCallback: SetupCallback
|
private val checkForButtonState: () -> Unit = {
|
||||||
|
val page = pages[binding.viewPager2.currentItem]
|
||||||
|
page.pageButtons?.forEach {
|
||||||
|
if (it.buttonState() == ButtonState.BUTTON_ACTION_COMPLETE) {
|
||||||
|
pageButtonCallback.onStepCompleted(
|
||||||
|
it.titleId,
|
||||||
|
pageFullyCompleted = false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (page.pageSteps() == PageState.COMPLETE) {
|
||||||
|
pageButtonCallback.onStepCompleted(0, pageFullyCompleted = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
|
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
|
||||||
private val permissionLauncher =
|
private val permissionLauncher =
|
||||||
registerForActivityResult(ActivityResultContracts.RequestPermission()) {
|
registerForActivityResult(ActivityResultContracts.RequestPermission()) {
|
||||||
if (it) {
|
if (it) {
|
||||||
notificationCallback.onStepCompleted()
|
checkForButtonState.invoke()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!it &&
|
if (!it &&
|
||||||
|
|
@ -345,15 +424,13 @@ class SetupFragment : Fragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var keyCallback: SetupCallback
|
|
||||||
private lateinit var firmwareCallback: SetupCallback
|
|
||||||
|
|
||||||
val getProdKey =
|
val getProdKey =
|
||||||
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
|
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
mainActivity.processKey(result, "keys")
|
mainActivity.processKey(result, "keys")
|
||||||
if (NativeLibrary.areKeysPresent()) {
|
if (NativeLibrary.areKeysPresent()) {
|
||||||
keyCallback.onStepCompleted()
|
checkForButtonState.invoke()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -363,14 +440,12 @@ class SetupFragment : Fragment() {
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
mainActivity.processFirmware(result) {
|
mainActivity.processFirmware(result) {
|
||||||
if (NativeLibrary.isFirmwareAvailable()) {
|
if (NativeLibrary.isFirmwareAvailable()) {
|
||||||
firmwareCallback.onStepCompleted()
|
checkForButtonState.invoke()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var gamesDirCallback: SetupCallback
|
|
||||||
|
|
||||||
val getGamesDirectory =
|
val getGamesDirectory =
|
||||||
registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
|
registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
|
|
@ -379,9 +454,13 @@ class SetupFragment : Fragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
.apply()
|
.apply()
|
||||||
|
|
||||||
|
gamesViewModel.reloadGames(directoriesChanged = true, firstStartup = false)
|
||||||
|
|
||||||
mainActivity.finishSetup(binding.root.findNavController())
|
mainActivity.finishSetup(binding.root.findNavController())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -405,8 +484,10 @@ class SetupFragment : Fragment() {
|
||||||
ViewCompat.setOnApplyWindowInsetsListener(
|
ViewCompat.setOnApplyWindowInsetsListener(
|
||||||
binding.root
|
binding.root
|
||||||
) { _: View, windowInsets: WindowInsetsCompat ->
|
) { _: View, windowInsets: WindowInsetsCompat ->
|
||||||
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
val barInsets =
|
||||||
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
|
windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||||
|
val cutoutInsets =
|
||||||
|
windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
|
||||||
|
|
||||||
val leftPadding = barInsets.left + cutoutInsets.left
|
val leftPadding = barInsets.left + cutoutInsets.left
|
||||||
val topPadding = barInsets.top + cutoutInsets.top
|
val topPadding = barInsets.top + cutoutInsets.top
|
||||||
|
|
@ -415,11 +496,22 @@ class SetupFragment : Fragment() {
|
||||||
|
|
||||||
if (resources.getBoolean(R.bool.small_layout)) {
|
if (resources.getBoolean(R.bool.small_layout)) {
|
||||||
binding.viewPager2
|
binding.viewPager2
|
||||||
.updatePadding(left = leftPadding, top = topPadding, right = rightPadding)
|
.updatePadding(
|
||||||
|
left = leftPadding,
|
||||||
|
top = topPadding,
|
||||||
|
right = rightPadding
|
||||||
|
)
|
||||||
binding.constraintButtons
|
binding.constraintButtons
|
||||||
.updatePadding(left = leftPadding, right = rightPadding, bottom = bottomPadding)
|
.updatePadding(
|
||||||
|
left = leftPadding,
|
||||||
|
right = rightPadding,
|
||||||
|
bottom = bottomPadding
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
binding.viewPager2.updatePadding(top = topPadding, bottom = bottomPadding)
|
binding.viewPager2.updatePadding(
|
||||||
|
top = topPadding,
|
||||||
|
bottom = bottomPadding
|
||||||
|
)
|
||||||
binding.constraintButtons
|
binding.constraintButtons
|
||||||
.updatePadding(
|
.updatePadding(
|
||||||
left = leftPadding,
|
left = leftPadding,
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|
@ -11,20 +14,21 @@ import android.os.Bundle
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
|
import androidx.core.net.toUri
|
||||||
|
|
||||||
class SetupWarningDialogFragment : DialogFragment() {
|
class SetupWarningDialogFragment : DialogFragment() {
|
||||||
private var titleId: Int = 0
|
private var titleIds: IntArray = intArrayOf()
|
||||||
private var descriptionId: Int = 0
|
private var descriptionIds: IntArray = intArrayOf()
|
||||||
private var helpLinkId: Int = 0
|
private var helpLinkIds: IntArray = intArrayOf()
|
||||||
private var page: Int = 0
|
private var page: Int = 0
|
||||||
|
|
||||||
private lateinit var setupFragment: SetupFragment
|
private lateinit var setupFragment: SetupFragment
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
titleId = requireArguments().getInt(TITLE)
|
titleIds = requireArguments().getIntArray(TITLES) ?: intArrayOf()
|
||||||
descriptionId = requireArguments().getInt(DESCRIPTION)
|
descriptionIds = requireArguments().getIntArray(DESCRIPTIONS) ?: intArrayOf()
|
||||||
helpLinkId = requireArguments().getInt(HELP_LINK)
|
helpLinkIds = requireArguments().getIntArray(HELP_LINKS) ?: intArrayOf()
|
||||||
page = requireArguments().getInt(PAGE)
|
page = requireArguments().getInt(PAGE)
|
||||||
|
|
||||||
setupFragment = requireParentFragment() as SetupFragment
|
setupFragment = requireParentFragment() as SetupFragment
|
||||||
|
|
@ -38,18 +42,24 @@ class SetupWarningDialogFragment : DialogFragment() {
|
||||||
}
|
}
|
||||||
.setNegativeButton(R.string.warning_cancel, null)
|
.setNegativeButton(R.string.warning_cancel, null)
|
||||||
|
|
||||||
if (titleId != 0) {
|
val messageBuilder = StringBuilder()
|
||||||
builder.setTitle(titleId)
|
for (i in titleIds.indices) {
|
||||||
} else {
|
if (titleIds[i] != 0) {
|
||||||
builder.setTitle("")
|
messageBuilder.append(getString(titleIds[i])).append("\n\n")
|
||||||
|
}
|
||||||
|
if (descriptionIds[i] != 0) {
|
||||||
|
messageBuilder.append(getString(descriptionIds[i])).append("\n\n")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (descriptionId != 0) {
|
|
||||||
builder.setMessage(descriptionId)
|
builder.setTitle("Warning")
|
||||||
}
|
builder.setMessage(messageBuilder.toString().trim())
|
||||||
if (helpLinkId != 0) {
|
|
||||||
|
if (helpLinkIds.any { it != 0 }) {
|
||||||
builder.setNeutralButton(R.string.warning_help) { _: DialogInterface?, _: Int ->
|
builder.setNeutralButton(R.string.warning_help) { _: DialogInterface?, _: Int ->
|
||||||
val helpLink = resources.getString(R.string.install_prod_keys_warning_help)
|
val helpLinkId = helpLinkIds.first { it != 0 }
|
||||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(helpLink))
|
val helpLink = resources.getString(helpLinkId)
|
||||||
|
val intent = Intent(Intent.ACTION_VIEW, helpLink.toUri())
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -60,27 +70,27 @@ class SetupWarningDialogFragment : DialogFragment() {
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "SetupWarningDialogFragment"
|
const val TAG = "SetupWarningDialogFragment"
|
||||||
|
|
||||||
private const val TITLE = "Title"
|
private const val TITLES = "Titles"
|
||||||
private const val DESCRIPTION = "Description"
|
private const val DESCRIPTIONS = "Descriptions"
|
||||||
private const val HELP_LINK = "HelpLink"
|
private const val HELP_LINKS = "HelpLinks"
|
||||||
private const val PAGE = "Page"
|
private const val PAGE = "Page"
|
||||||
|
|
||||||
fun newInstance(
|
fun newInstance(
|
||||||
titleId: Int,
|
titleIds: IntArray,
|
||||||
descriptionId: Int,
|
descriptionIds: IntArray,
|
||||||
helpLinkId: Int,
|
helpLinkIds: IntArray,
|
||||||
page: Int
|
page: Int
|
||||||
): SetupWarningDialogFragment {
|
): SetupWarningDialogFragment {
|
||||||
val dialog = SetupWarningDialogFragment()
|
val dialog = SetupWarningDialogFragment()
|
||||||
val bundle = Bundle()
|
val bundle = Bundle()
|
||||||
bundle.apply {
|
bundle.apply {
|
||||||
putInt(TITLE, titleId)
|
putIntArray(TITLES, titleIds)
|
||||||
putInt(DESCRIPTION, descriptionId)
|
putIntArray(DESCRIPTIONS, descriptionIds)
|
||||||
putInt(HELP_LINK, helpLinkId)
|
putIntArray(HELP_LINKS, helpLinkIds)
|
||||||
putInt(PAGE, page)
|
putInt(PAGE, page)
|
||||||
}
|
}
|
||||||
dialog.arguments = bundle
|
dialog.arguments = bundle
|
||||||
return dialog
|
return dialog
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
package org.yuzu.yuzu_emu.model
|
package org.yuzu.yuzu_emu.model
|
||||||
|
|
@ -145,7 +145,10 @@ class GamesViewModel : ViewModel() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
NativeConfig.addGameDir(gameDir)
|
NativeConfig.addGameDir(gameDir)
|
||||||
getGameDirs(true)
|
val isFirstTimeSetup = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
|
||||||
|
.getBoolean(org.yuzu.yuzu_emu.features.settings.model.Settings.PREF_FIRST_APP_LAUNCH, true)
|
||||||
|
|
||||||
|
getGameDirs(!isFirstTimeSetup)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (savedFromGameFragment) {
|
if (savedFromGameFragment) {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|
@ -7,23 +10,36 @@ data class SetupPage(
|
||||||
val iconId: Int,
|
val iconId: Int,
|
||||||
val titleId: Int,
|
val titleId: Int,
|
||||||
val descriptionId: Int,
|
val descriptionId: Int,
|
||||||
val buttonIconId: Int,
|
val pageButtons: List<PageButton>? = null,
|
||||||
val leftAlignedIcon: Boolean,
|
val pageSteps: () -> PageState = { PageState.COMPLETE },
|
||||||
val buttonTextId: Int,
|
|
||||||
|
)
|
||||||
|
|
||||||
|
data class PageButton(
|
||||||
|
val iconId: Int,
|
||||||
|
val titleId: Int,
|
||||||
|
val descriptionId: Int,
|
||||||
val buttonAction: (callback: SetupCallback) -> Unit,
|
val buttonAction: (callback: SetupCallback) -> Unit,
|
||||||
val hasWarning: Boolean,
|
val buttonState: () -> ButtonState = { ButtonState.BUTTON_ACTION_UNDEFINED },
|
||||||
|
val isUnskippable: Boolean = false,
|
||||||
|
val hasWarning: Boolean = false,
|
||||||
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 stepCompleted: () -> StepState = { StepState.UNDEFINED }
|
|
||||||
)
|
)
|
||||||
|
|
||||||
interface SetupCallback {
|
interface SetupCallback {
|
||||||
fun onStepCompleted()
|
fun onStepCompleted(pageButtonId: Int, pageFullyCompleted: Boolean)
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class StepState {
|
enum class PageState {
|
||||||
COMPLETE,
|
COMPLETE,
|
||||||
INCOMPLETE,
|
INCOMPLETE,
|
||||||
UNDEFINED
|
UNDEFINED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class ButtonState {
|
||||||
|
BUTTON_ACTION_COMPLETE,
|
||||||
|
BUTTON_ACTION_INCOMPLETE,
|
||||||
|
BUTTON_ACTION_UNDEFINED
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="?attr/colorControlNormal"
|
||||||
|
android:pathData="M12,1L3,5v6c0,5.55 3.84,10.74 9,12 5.16,-1.26 9,-6.45 9,-12V5l-9,-4zM12,11.99h7c-0.53,4.12 -3.28,7.79 -7,8.94V12H5V6.3l7,-3.11v8.8z"/>
|
||||||
|
</vector>
|
||||||
|
|
@ -27,7 +27,7 @@
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/next"
|
android:text="@string/next"
|
||||||
android:visibility="invisible"
|
android:visibility="visible"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,97 +1,101 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
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"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<LinearLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_width="match_parent"
|
android:id="@+id/left_content"
|
||||||
|
android:layout_width="0dp"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
android:layout_weight="1"
|
app:layout_constraintEnd_toStartOf="@+id/right_content"
|
||||||
android:gravity="center">
|
app:layout_constraintHorizontal_weight="2">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/icon"
|
android:id="@+id/icon"
|
||||||
android:layout_width="260dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="260dp"
|
android:layout_height="0dp"
|
||||||
android:layout_gravity="center" />
|
android:layout_marginTop="32dp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/text_title"
|
||||||
</LinearLayout>
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHeight_max="160dp"
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
app:layout_constraintHeight_min="80dp"
|
||||||
android:layout_width="match_parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
android:layout_height="match_parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
android:layout_weight="1">
|
app:layout_constraintWidth_max="160dp"
|
||||||
|
app:layout_constraintWidth_min="80dp"
|
||||||
|
app:layout_constraintVertical_chainStyle="packed"
|
||||||
|
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.DisplaySmall"
|
style="@style/SynthwaveText.Header"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="0dp"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center"
|
android:textAlignment="center"
|
||||||
android:textColor="?attr/colorOnSurface"
|
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/text_description"
|
app:layout_constraintBottom_toTopOf="@+id/text_description"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toBottomOf="@+id/icon"
|
||||||
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
|
||||||
android:id="@+id/text_description"
|
android:id="@+id/text_description"
|
||||||
style="@style/TextAppearance.Material3.TitleLarge"
|
style="@style/TextAppearance.Material3.TitleLarge"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="0dp"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center"
|
|
||||||
android:textSize="20sp"
|
|
||||||
android:paddingHorizontal="16dp"
|
android:paddingHorizontal="16dp"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/button_action"
|
android:textAlignment="center"
|
||||||
|
android:textSize="20sp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/text_confirmation"
|
||||||
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:lineHeight="30sp"
|
app:lineHeight="30sp"
|
||||||
tools:text="@string/welcome_description" />
|
tools:text="@string/welcome_description" />
|
||||||
|
|
||||||
<com.google.android.material.textview.MaterialTextView
|
<com.google.android.material.textview.MaterialTextView
|
||||||
android:id="@+id/text_confirmation"
|
android:id="@+id/text_confirmation"
|
||||||
style="@style/TextAppearance.Material3.TitleLarge"
|
style="@style/SynthwaveText.Accent"
|
||||||
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
|
|
||||||
android:id="@+id/button_action"
|
|
||||||
style="@style/EdenButton.Primary"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="56dp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
android:paddingHorizontal="16dp"
|
||||||
android:layout_marginBottom="48dp"
|
android:text="@string/step_complete"
|
||||||
android:textSize="20sp"
|
android:textAlignment="center"
|
||||||
app:iconGravity="end"
|
android:textSize="30sp"
|
||||||
app:iconSize="24sp"
|
android:textStyle="bold"
|
||||||
|
android:visibility="invisible"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
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_description"
|
app:layout_constraintTop_toBottomOf="@+id/text_description"
|
||||||
tools:text="Get started" />
|
app:lineHeight="30sp" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
<LinearLayout
|
||||||
|
android:id="@+id/right_content"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/left_content"
|
||||||
|
app:layout_constraintHorizontal_weight="1">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/page_button_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:gravity="center" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
@ -27,7 +27,7 @@
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/next"
|
android:text="@string/next"
|
||||||
android:visibility="invisible"
|
android:visibility="visible"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
<com.google.android.material.button.MaterialButton xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="170dp"
|
||||||
|
android:layout_height="55dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
app:iconTint="?attr/colorOnPrimary"
|
||||||
|
app:iconSize="24dp"
|
||||||
|
style="@style/Widget.Material3.Button.UnelevatedButton" />
|
||||||
|
|
@ -11,7 +11,6 @@
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:layout_marginTop="64dp"
|
android:layout_marginTop="64dp"
|
||||||
android:layout_marginBottom="32dp"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/text_title"
|
app:layout_constraintBottom_toTopOf="@+id/text_title"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintHeight_max="220dp"
|
app:layout_constraintHeight_max="220dp"
|
||||||
|
|
@ -45,7 +44,7 @@
|
||||||
android:textAlignment="center"
|
android:textAlignment="center"
|
||||||
android:textSize="20sp"
|
android:textSize="20sp"
|
||||||
android:paddingHorizontal="16dp"
|
android:paddingHorizontal="16dp"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/button_action"
|
app:layout_constraintBottom_toTopOf="@+id/text_confirmation"
|
||||||
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"
|
||||||
|
|
@ -56,8 +55,8 @@
|
||||||
<com.google.android.material.textview.MaterialTextView
|
<com.google.android.material.textview.MaterialTextView
|
||||||
android:id="@+id/text_confirmation"
|
android:id="@+id/text_confirmation"
|
||||||
style="@style/SynthwaveText.Accent"
|
style="@style/SynthwaveText.Accent"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="213dp"
|
||||||
android:layout_height="0dp"
|
android:layout_height="226dp"
|
||||||
android:paddingHorizontal="16dp"
|
android:paddingHorizontal="16dp"
|
||||||
android:paddingTop="24dp"
|
android:paddingTop="24dp"
|
||||||
android:textAlignment="center"
|
android:textAlignment="center"
|
||||||
|
|
@ -71,20 +70,16 @@
|
||||||
app:layout_constraintVertical_weight="1"
|
app:layout_constraintVertical_weight="1"
|
||||||
app:lineHeight="30sp" />
|
app:lineHeight="30sp" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<LinearLayout
|
||||||
android:id="@+id/button_action"
|
android:id="@+id/page_button_container"
|
||||||
style="@style/EdenButton.Primary"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_height="56dp"
|
android:orientation="vertical"
|
||||||
android:layout_marginTop="16dp"
|
android:padding="16dp"
|
||||||
android:layout_marginBottom="48dp"
|
android:gravity="center"
|
||||||
android:textSize="20sp"
|
|
||||||
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"
|
app:layout_constraintTop_toBottomOf="@+id/text_description"
|
||||||
tools:text="Get started" />
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
|
||||||
|
|
@ -298,6 +298,10 @@
|
||||||
<string name="install_prod_keys_warning_description">Valid keys are required to emulate retail games. Only homebrew apps will function if you continue.</string>
|
<string name="install_prod_keys_warning_description">Valid keys are required to emulate retail games. Only homebrew apps will function if you continue.</string>
|
||||||
<string name="install_prod_keys_warning_help">https://yuzu-mirror.github.io/help/quickstart/#guide-introduction</string>
|
<string name="install_prod_keys_warning_help">https://yuzu-mirror.github.io/help/quickstart/#guide-introduction</string>
|
||||||
<string name="install_firmware_warning">Skip adding firmware?</string>
|
<string name="install_firmware_warning">Skip adding firmware?</string>
|
||||||
|
<string name="emulator_data">Setup Emulator Data</string>
|
||||||
|
<string name="emulator_data_description">Keys are required in order for the emulator to work and firmware is recommended and required for using the QLaunch applet</string>
|
||||||
|
<string name="permissions">Grant Permissions</string>
|
||||||
|
<string name="permissions_description">Grant optional permissions to use specific features of the emulator</string>
|
||||||
<string name="install_firmware_warning_description">Many games require access to firmware to run properly.</string>
|
<string name="install_firmware_warning_description">Many games require access to firmware to run properly.</string>
|
||||||
<string name="install_firmware_warning_help">https://yuzu-mirror.github.io/help/quickstart/#guide-introduction</string>
|
<string name="install_firmware_warning_help">https://yuzu-mirror.github.io/help/quickstart/#guide-introduction</string>
|
||||||
<string name="notifications">Notifications</string>
|
<string name="notifications">Notifications</string>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue