mirror of
https://github.com/Lime3DS/Lime3DS.git
synced 2025-03-13 17:22:30 +01:00
Merge 6405e74b27763f5f58ea0d8da34092ee71969183 into 42d77cd720eb42845c2afb77c6d7157e02c8c325
This commit is contained in:
commit
ca555fc307
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
package org.citra.citra_emu.adapters
|
package org.citra.citra_emu.adapters
|
||||||
|
|
||||||
|
import android.content.res.ColorStateList
|
||||||
import android.text.Html
|
import android.text.Html
|
||||||
import android.text.method.LinkMovementMethod
|
import android.text.method.LinkMovementMethod
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
@ -14,10 +15,12 @@ import androidx.core.content.res.ResourcesCompat
|
|||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.google.android.material.button.MaterialButton
|
import com.google.android.material.button.MaterialButton
|
||||||
import org.citra.citra_emu.databinding.PageSetupBinding
|
import org.citra.citra_emu.databinding.PageSetupBinding
|
||||||
|
import org.citra.citra_emu.model.PageState
|
||||||
import org.citra.citra_emu.model.SetupCallback
|
import org.citra.citra_emu.model.SetupCallback
|
||||||
import org.citra.citra_emu.model.SetupPage
|
import org.citra.citra_emu.model.SetupPage
|
||||||
import org.citra.citra_emu.model.StepState
|
|
||||||
import org.citra.citra_emu.utils.ViewUtils
|
import org.citra.citra_emu.utils.ViewUtils
|
||||||
|
import org.citra.citra_emu.R
|
||||||
|
import org.citra.citra_emu.model.ButtonState
|
||||||
|
|
||||||
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>() {
|
||||||
@ -42,8 +45,40 @@ 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.STEP_COMPLETE) {
|
if (page.pageSteps.invoke() == PageState.PAGE_STEPS_COMPLETE) {
|
||||||
onStepCompleted()
|
onStepCompleted(0, pageFullyCompleted = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (page.pageButton != null && page.pageSteps.invoke() != PageState.PAGE_STEPS_COMPLETE) {
|
||||||
|
for (pageButton in page.pageButton) {
|
||||||
|
val pageButtonView = LayoutInflater.from(activity)
|
||||||
|
.inflate(
|
||||||
|
R.layout.page_button,
|
||||||
|
binding.pegeButtonContainer,
|
||||||
|
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.pegeButtonContainer.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(
|
||||||
@ -57,31 +92,26 @@ class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>)
|
|||||||
binding.textDescription.text =
|
binding.textDescription.text =
|
||||||
Html.fromHtml(activity.resources.getString(page.descriptionId), 0)
|
Html.fromHtml(activity.resources.getString(page.descriptionId), 0)
|
||||||
binding.textDescription.movementMethod = LinkMovementMethod.getInstance()
|
binding.textDescription.movementMethod = LinkMovementMethod.getInstance()
|
||||||
|
|
||||||
binding.buttonAction.apply {
|
|
||||||
text = activity.resources.getString(page.buttonTextId)
|
|
||||||
if (page.buttonIconId != 0) {
|
|
||||||
icon = ResourcesCompat.getDrawable(
|
|
||||||
activity.resources,
|
|
||||||
page.buttonIconId,
|
|
||||||
activity.theme
|
|
||||||
)
|
|
||||||
}
|
|
||||||
iconGravity =
|
|
||||||
if (page.leftAlignedIcon) {
|
|
||||||
MaterialButton.ICON_GRAVITY_START
|
|
||||||
} else {
|
|
||||||
MaterialButton.ICON_GRAVITY_END
|
|
||||||
}
|
|
||||||
setOnClickListener {
|
|
||||||
page.buttonAction.invoke(this@SetupPageViewHolder)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStepCompleted() {
|
override fun onStepCompleted(pageButtonId: Int, pageFullyCompleted: Boolean) {
|
||||||
ViewUtils.hideView(binding.buttonAction, 200)
|
val button = binding.pegeButtonContainer.findViewById<MaterialButton>(pageButtonId)
|
||||||
|
|
||||||
|
if (pageFullyCompleted) {
|
||||||
|
ViewUtils.hideView(binding.pegeButtonContainer, 200)
|
||||||
ViewUtils.showView(binding.textConfirmation, 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))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -141,7 +141,7 @@ class CopyDirProgressDialog : DialogFragment() {
|
|||||||
|
|
||||||
override fun onComplete() {
|
override fun onComplete() {
|
||||||
CitraDirectoryHelper.initializeCitraDirectory(path)
|
CitraDirectoryHelper.initializeCitraDirectory(path)
|
||||||
callback?.onStepCompleted()
|
callback?.onStepCompleted(0, false)
|
||||||
viewModel.setCopyComplete(true)
|
viewModel.setCopyComplete(true)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -35,9 +35,11 @@ import org.citra.citra_emu.R
|
|||||||
import org.citra.citra_emu.adapters.SetupAdapter
|
import org.citra.citra_emu.adapters.SetupAdapter
|
||||||
import org.citra.citra_emu.databinding.FragmentSetupBinding
|
import org.citra.citra_emu.databinding.FragmentSetupBinding
|
||||||
import org.citra.citra_emu.features.settings.model.Settings
|
import org.citra.citra_emu.features.settings.model.Settings
|
||||||
|
import org.citra.citra_emu.model.ButtonState
|
||||||
|
import org.citra.citra_emu.model.PageButton
|
||||||
|
import org.citra.citra_emu.model.PageState
|
||||||
import org.citra.citra_emu.model.SetupCallback
|
import org.citra.citra_emu.model.SetupCallback
|
||||||
import org.citra.citra_emu.model.SetupPage
|
import org.citra.citra_emu.model.SetupPage
|
||||||
import org.citra.citra_emu.model.StepState
|
|
||||||
import org.citra.citra_emu.ui.main.MainActivity
|
import org.citra.citra_emu.ui.main.MainActivity
|
||||||
import org.citra.citra_emu.utils.CitraDirectoryHelper
|
import org.citra.citra_emu.utils.CitraDirectoryHelper
|
||||||
import org.citra.citra_emu.utils.GameHelper
|
import org.citra.citra_emu.utils.GameHelper
|
||||||
@ -113,150 +115,194 @@ class SetupFragment : Fragment() {
|
|||||||
0,
|
0,
|
||||||
true,
|
true,
|
||||||
R.string.get_started,
|
R.string.get_started,
|
||||||
{ pageForward() }
|
pageButton = mutableListOf<PageButton>().apply {
|
||||||
)
|
add(
|
||||||
)
|
PageButton(
|
||||||
|
R.drawable.ic_arrow_forward,
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
R.string.get_started,
|
||||||
add(
|
0,
|
||||||
SetupPage(
|
buttonAction = {
|
||||||
R.drawable.ic_notification,
|
pageForward()
|
||||||
R.string.notifications,
|
},
|
||||||
R.string.notifications_description,
|
buttonState = {
|
||||||
0,
|
ButtonState.BUTTON_ACTION_UNDEFINED
|
||||||
false,
|
}
|
||||||
R.string.give_permission,
|
)
|
||||||
{
|
)
|
||||||
notificationCallback = it
|
|
||||||
permissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
true,
|
|
||||||
{
|
|
||||||
if (NotificationManagerCompat.from(requireContext())
|
|
||||||
.areNotificationsEnabled()
|
|
||||||
) {
|
|
||||||
StepState.STEP_COMPLETE
|
|
||||||
} else {
|
|
||||||
StepState.STEP_INCOMPLETE
|
|
||||||
}
|
|
||||||
},
|
|
||||||
R.string.notification_warning,
|
|
||||||
R.string.notification_warning_description,
|
|
||||||
0
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
add(
|
|
||||||
SetupPage(
|
|
||||||
R.drawable.ic_microphone,
|
|
||||||
R.string.microphone_permission,
|
|
||||||
R.string.microphone_permission_description,
|
|
||||||
0,
|
|
||||||
false,
|
|
||||||
R.string.give_permission,
|
|
||||||
{
|
|
||||||
microphoneCallback = it
|
|
||||||
permissionLauncher.launch(Manifest.permission.RECORD_AUDIO)
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
{
|
|
||||||
if (
|
|
||||||
ContextCompat.checkSelfPermission(
|
|
||||||
requireContext(),
|
|
||||||
Manifest.permission.RECORD_AUDIO
|
|
||||||
) == PackageManager.PERMISSION_GRANTED
|
|
||||||
) {
|
|
||||||
StepState.STEP_COMPLETE
|
|
||||||
} else {
|
|
||||||
StepState.STEP_INCOMPLETE
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
add(
|
add(
|
||||||
SetupPage(
|
SetupPage(
|
||||||
R.drawable.ic_camera,
|
R.drawable.ic_permission,
|
||||||
R.string.camera_permission,
|
R.string.permissions,
|
||||||
R.string.camera_permission_description,
|
R.string.permissions_description,
|
||||||
0,
|
0,
|
||||||
false,
|
false,
|
||||||
R.string.give_permission,
|
0,
|
||||||
{
|
pageButton = mutableListOf<PageButton>().apply {
|
||||||
cameraCallback = it
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
permissionLauncher.launch(Manifest.permission.CAMERA)
|
add(
|
||||||
},
|
PageButton(
|
||||||
false,
|
R.drawable.ic_notification,
|
||||||
false,
|
R.string.notifications,
|
||||||
{
|
R.string.notifications_description,
|
||||||
if (
|
buttonAction = {
|
||||||
ContextCompat.checkSelfPermission(
|
pageButtonCallback = it
|
||||||
requireContext(),
|
permissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
|
||||||
Manifest.permission.CAMERA
|
},
|
||||||
) == PackageManager.PERMISSION_GRANTED
|
buttonState = {
|
||||||
) {
|
if (NotificationManagerCompat.from(requireContext())
|
||||||
StepState.STEP_COMPLETE
|
.areNotificationsEnabled()
|
||||||
} else {
|
) {
|
||||||
StepState.STEP_INCOMPLETE
|
ButtonState.BUTTON_ACTION_COMPLETE
|
||||||
|
} else {
|
||||||
|
ButtonState.BUTTON_ACTION_INCOMPLETE
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isUnskippable = false,
|
||||||
|
hasWarning = true,
|
||||||
|
R.string.notification_warning,
|
||||||
|
R.string.notification_warning_description,
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
add(
|
||||||
)
|
PageButton(
|
||||||
)
|
R.drawable.ic_microphone,
|
||||||
add(
|
R.string.microphone_permission,
|
||||||
SetupPage(
|
R.string.microphone_permission_description,
|
||||||
R.drawable.ic_home,
|
buttonAction = {
|
||||||
R.string.select_citra_user_folder,
|
pageButtonCallback = it
|
||||||
R.string.select_citra_user_folder_description,
|
permissionLauncher.launch(Manifest.permission.RECORD_AUDIO)
|
||||||
0,
|
},
|
||||||
true,
|
buttonState = {
|
||||||
R.string.select,
|
if (ContextCompat.checkSelfPermission(
|
||||||
{
|
requireContext(),
|
||||||
userDirCallback = it
|
Manifest.permission.RECORD_AUDIO
|
||||||
openCitraDirectory.launch(null)
|
) == PackageManager.PERMISSION_GRANTED
|
||||||
},
|
) {
|
||||||
true,
|
ButtonState.BUTTON_ACTION_COMPLETE
|
||||||
true,
|
} else {
|
||||||
{
|
ButtonState.BUTTON_ACTION_INCOMPLETE
|
||||||
if (PermissionsHandler.hasWriteAccess(requireContext())) {
|
}
|
||||||
StepState.STEP_COMPLETE
|
},
|
||||||
} else {
|
)
|
||||||
StepState.STEP_INCOMPLETE
|
)
|
||||||
}
|
add(
|
||||||
},
|
PageButton(
|
||||||
R.string.cannot_skip,
|
R.drawable.ic_camera,
|
||||||
R.string.cannot_skip_directory_description,
|
R.string.camera_permission,
|
||||||
R.string.cannot_skip_directory_help
|
R.string.camera_permission_description,
|
||||||
)
|
buttonAction = {
|
||||||
)
|
pageButtonCallback = it
|
||||||
add(
|
permissionLauncher.launch(Manifest.permission.CAMERA)
|
||||||
SetupPage(
|
},
|
||||||
R.drawable.ic_controller,
|
buttonState = {
|
||||||
R.string.games,
|
if (ContextCompat.checkSelfPermission(
|
||||||
R.string.games_description,
|
requireContext(),
|
||||||
0,
|
Manifest.permission.CAMERA
|
||||||
true,
|
) == PackageManager.PERMISSION_GRANTED
|
||||||
R.string.select,
|
) {
|
||||||
{
|
ButtonState.BUTTON_ACTION_COMPLETE
|
||||||
gamesDirCallback = it
|
} else {
|
||||||
getGamesDirectory.launch(
|
ButtonState.BUTTON_ACTION_INCOMPLETE
|
||||||
Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
false,
|
) {
|
||||||
|
if (
|
||||||
|
ContextCompat.checkSelfPermission(
|
||||||
|
requireContext(),
|
||||||
|
Manifest.permission.RECORD_AUDIO
|
||||||
|
) == PackageManager.PERMISSION_GRANTED &&
|
||||||
|
ContextCompat.checkSelfPermission(
|
||||||
|
requireContext(),
|
||||||
|
Manifest.permission.CAMERA
|
||||||
|
) == PackageManager.PERMISSION_GRANTED &&
|
||||||
|
NotificationManagerCompat.from(requireContext())
|
||||||
|
.areNotificationsEnabled()
|
||||||
|
) {
|
||||||
|
PageState.PAGE_STEPS_COMPLETE
|
||||||
|
} else {
|
||||||
|
PageState.PAGE_STEPS_INCOMPLETE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
add(
|
||||||
|
SetupPage(
|
||||||
|
R.drawable.ic_folder,
|
||||||
|
R.string.select_emulalator_data_folder,
|
||||||
|
R.string.select_emulalator_data_folder_description,
|
||||||
|
0,
|
||||||
true,
|
true,
|
||||||
{
|
R.string.select,
|
||||||
if (preferences.getString(GameHelper.KEY_GAME_PATH, "")!!.isNotEmpty()) {
|
pageButton = mutableListOf<PageButton>().apply {
|
||||||
StepState.STEP_COMPLETE
|
add(
|
||||||
} else {
|
PageButton(
|
||||||
StepState.STEP_INCOMPLETE
|
R.drawable.ic_home,
|
||||||
}
|
R.string.select_citra_user_folder,
|
||||||
|
R.string.select_citra_user_folder_description,
|
||||||
|
buttonAction = {
|
||||||
|
pageButtonCallback = it
|
||||||
|
openCitraDirectory.launch(null)
|
||||||
|
},
|
||||||
|
buttonState = {
|
||||||
|
if (PermissionsHandler.hasWriteAccess(requireContext())) {
|
||||||
|
ButtonState.BUTTON_ACTION_COMPLETE
|
||||||
|
} else {
|
||||||
|
ButtonState.BUTTON_ACTION_INCOMPLETE
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isUnskippable = true,
|
||||||
|
hasWarning = false,
|
||||||
|
R.string.cannot_skip,
|
||||||
|
R.string.cannot_skip_directory_description,
|
||||||
|
R.string.cannot_skip_directory_help
|
||||||
|
|
||||||
|
)
|
||||||
|
)
|
||||||
|
add(
|
||||||
|
PageButton(
|
||||||
|
R.drawable.ic_controller,
|
||||||
|
R.string.games,
|
||||||
|
R.string.games_description,
|
||||||
|
buttonAction = {
|
||||||
|
pageButtonCallback = it
|
||||||
|
getGamesDirectory.launch(
|
||||||
|
Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data
|
||||||
|
)
|
||||||
|
},
|
||||||
|
buttonState = {
|
||||||
|
if (preferences.getString(GameHelper.KEY_GAME_PATH, "")!!.isNotEmpty()) {
|
||||||
|
ButtonState.BUTTON_ACTION_COMPLETE
|
||||||
|
} else {
|
||||||
|
ButtonState.BUTTON_ACTION_INCOMPLETE
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isUnskippable = false,
|
||||||
|
hasWarning = true,
|
||||||
|
R.string.add_games_warning,
|
||||||
|
R.string.add_games_warning_description,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
},
|
},
|
||||||
R.string.add_games_warning,
|
) {
|
||||||
R.string.add_games_warning_description,
|
if (PermissionsHandler.hasWriteAccess(requireContext()) && preferences.getString(
|
||||||
R.string.add_games_warning_help
|
GameHelper.KEY_GAME_PATH,
|
||||||
)
|
""
|
||||||
|
)!!.isNotEmpty()
|
||||||
|
) {
|
||||||
|
PageState.PAGE_STEPS_COMPLETE
|
||||||
|
|
||||||
|
} else {
|
||||||
|
PageState.PAGE_STEPS_INCOMPLETE
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
add(
|
add(
|
||||||
SetupPage(
|
SetupPage(
|
||||||
@ -266,7 +312,22 @@ class SetupFragment : Fragment() {
|
|||||||
R.drawable.ic_arrow_forward,
|
R.drawable.ic_arrow_forward,
|
||||||
false,
|
false,
|
||||||
R.string.text_continue,
|
R.string.text_continue,
|
||||||
{ finishSetup() }
|
pageButton = mutableListOf<PageButton>().apply {
|
||||||
|
add(
|
||||||
|
PageButton(
|
||||||
|
R.drawable.ic_arrow_forward,
|
||||||
|
R.string.text_continue,
|
||||||
|
0,
|
||||||
|
buttonAction = {
|
||||||
|
finishSetup()
|
||||||
|
},
|
||||||
|
buttonState = {
|
||||||
|
ButtonState.BUTTON_ACTION_UNDEFINED
|
||||||
|
}
|
||||||
|
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -303,35 +364,47 @@ 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
|
// This allows multiple sets of warning messages to be displayed om the same dialog if necessary
|
||||||
if (currentPage.hasWarning || currentPage.isUnskippable) {
|
val warningMessages =
|
||||||
val stepState = currentPage.stepCompleted.invoke()
|
mutableListOf<Triple<Int, Int, Int>>() // title, description, helpLink
|
||||||
if (stepState == StepState.STEP_COMPLETE ||
|
|
||||||
stepState == StepState.STEP_UNDEFINED
|
|
||||||
) {
|
|
||||||
pageForward()
|
|
||||||
return@setOnClickListener
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentPage.isUnskippable) {
|
currentPage.pageButton?.forEach { button ->
|
||||||
MessageDialogFragment.newInstance(
|
if (button.hasWarning || button.isUnskippable) {
|
||||||
currentPage.warningTitleId,
|
val buttonState = button.buttonState()
|
||||||
currentPage.warningDescriptionId,
|
if (buttonState == ButtonState.BUTTON_ACTION_COMPLETE) {
|
||||||
currentPage.warningHelpLinkId
|
return@forEach
|
||||||
).show(childFragmentManager, MessageDialogFragment.TAG)
|
}
|
||||||
return@setOnClickListener
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasBeenWarned[index]) {
|
if (button.isUnskippable) {
|
||||||
SetupWarningDialogFragment.newInstance(
|
MessageDialogFragment.newInstance(
|
||||||
currentPage.warningTitleId,
|
button.warningTitleId,
|
||||||
currentPage.warningDescriptionId,
|
button.warningDescriptionId,
|
||||||
currentPage.warningHelpLinkId,
|
button.warningHelpLinkId
|
||||||
index
|
).show(childFragmentManager, MessageDialogFragment.TAG)
|
||||||
).show(childFragmentManager, SetupWarningDialogFragment.TAG)
|
return@setOnClickListener
|
||||||
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() }
|
||||||
@ -352,6 +425,25 @@ class SetupFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setInsets()
|
setInsets()
|
||||||
|
|
||||||
|
binding.buttonBack.setOnClickListener { pageBackward() }
|
||||||
|
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
val nextIsVisible = savedInstanceState.getBoolean(KEY_NEXT_VISIBILITY)
|
||||||
|
val backIsVisible = savedInstanceState.getBoolean(KEY_BACK_VISIBILITY)
|
||||||
|
hasBeenWarned = savedInstanceState.getBooleanArray(KEY_HAS_BEEN_WARNED)!!
|
||||||
|
|
||||||
|
if (nextIsVisible) {
|
||||||
|
binding.buttonNext.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
if (backIsVisible) {
|
||||||
|
binding.buttonBack.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
hasBeenWarned = BooleanArray(pages.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
setInsets()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSaveInstanceState(outState: Bundle) {
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
@ -366,19 +458,24 @@ class SetupFragment : Fragment() {
|
|||||||
_binding = null
|
_binding = null
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var notificationCallback: SetupCallback
|
private lateinit var pageButtonCallback: SetupCallback
|
||||||
private lateinit var microphoneCallback: SetupCallback
|
private val checkForButtonState: () -> Unit = {
|
||||||
private lateinit var cameraCallback: SetupCallback
|
val page = pages[binding.viewPager2.currentItem]
|
||||||
|
page.pageButton?.forEach {
|
||||||
|
if (it.buttonState() == ButtonState.BUTTON_ACTION_COMPLETE) {
|
||||||
|
pageButtonCallback.onStepCompleted(it.titleId, pageFullyCompleted = false)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (page.pageSteps() == PageState.PAGE_STEPS_COMPLETE) {
|
||||||
|
pageButtonCallback.onStepCompleted(0, pageFullyCompleted = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private val permissionLauncher =
|
private val permissionLauncher =
|
||||||
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
|
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
|
||||||
if (isGranted) {
|
if (isGranted) {
|
||||||
val page = pages[binding.viewPager2.currentItem]
|
checkForButtonState.invoke()
|
||||||
when (page.titleId) {
|
|
||||||
R.string.notifications -> notificationCallback.onStepCompleted()
|
|
||||||
R.string.microphone_permission -> microphoneCallback.onStepCompleted()
|
|
||||||
R.string.camera_permission -> cameraCallback.onStepCompleted()
|
|
||||||
}
|
|
||||||
return@registerForActivityResult
|
return@registerForActivityResult
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -394,8 +491,6 @@ class SetupFragment : Fragment() {
|
|||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var userDirCallback: SetupCallback
|
|
||||||
|
|
||||||
private val openCitraDirectory = registerForActivityResult<Uri, Uri>(
|
private val openCitraDirectory = registerForActivityResult<Uri, Uri>(
|
||||||
ActivityResultContracts.OpenDocumentTree()
|
ActivityResultContracts.OpenDocumentTree()
|
||||||
) { result: Uri? ->
|
) { result: Uri? ->
|
||||||
@ -403,11 +498,9 @@ class SetupFragment : Fragment() {
|
|||||||
return@registerForActivityResult
|
return@registerForActivityResult
|
||||||
}
|
}
|
||||||
|
|
||||||
CitraDirectoryHelper(requireActivity()).showCitraDirectoryDialog(result, userDirCallback)
|
CitraDirectoryHelper(requireActivity()).showCitraDirectoryDialog(result, pageButtonCallback, checkForButtonState)
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var gamesDirCallback: SetupCallback
|
|
||||||
|
|
||||||
private val getGamesDirectory =
|
private val getGamesDirectory =
|
||||||
registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
|
registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
@ -427,7 +520,8 @@ class SetupFragment : Fragment() {
|
|||||||
|
|
||||||
homeViewModel.setGamesDir(requireActivity(), result.path!!)
|
homeViewModel.setGamesDir(requireActivity(), result.path!!)
|
||||||
|
|
||||||
gamesDirCallback.onStepCompleted()
|
checkForButtonState.invoke()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun finishSetup() {
|
private fun finishSetup() {
|
||||||
|
@ -14,18 +14,18 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|||||||
import org.citra.citra_emu.R
|
import org.citra.citra_emu.R
|
||||||
|
|
||||||
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
|
||||||
@ -39,16 +39,23 @@ class SetupWarningDialogFragment : DialogFragment() {
|
|||||||
}
|
}
|
||||||
.setNegativeButton(R.string.warning_cancel, null)
|
.setNegativeButton(R.string.warning_cancel, null)
|
||||||
|
|
||||||
if (titleId != 0) {
|
// Message builder to build multiple strings into one
|
||||||
builder.setTitle(titleId)
|
val messageBuilder = StringBuilder()
|
||||||
} else {
|
for (i in titleIds.indices) {
|
||||||
builder.setTitle("")
|
if (titleIds[i] != 0) {
|
||||||
|
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 helpLinkId = helpLinkIds.first { it != 0 }
|
||||||
val helpLink = resources.getString(helpLinkId)
|
val helpLink = resources.getString(helpLinkId)
|
||||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(helpLink))
|
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(helpLink))
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
@ -61,23 +68,23 @@ 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
|
||||||
|
@ -11,21 +11,35 @@ data class SetupPage(
|
|||||||
val buttonIconId: Int,
|
val buttonIconId: Int,
|
||||||
val leftAlignedIcon: Boolean,
|
val leftAlignedIcon: Boolean,
|
||||||
val buttonTextId: Int,
|
val buttonTextId: Int,
|
||||||
|
val pageButton: List<PageButton>? = null,
|
||||||
|
val pageSteps: () -> PageState = { PageState.PAGE_STEPS_UNDEFINED },
|
||||||
|
)
|
||||||
|
|
||||||
|
data class PageButton(
|
||||||
|
val iconId: Int,
|
||||||
|
val titleId: Int,
|
||||||
|
val descriptionId: Int,
|
||||||
val buttonAction: (callback: SetupCallback) -> Unit,
|
val buttonAction: (callback: SetupCallback) -> Unit,
|
||||||
|
val buttonState: () -> ButtonState = { ButtonState.BUTTON_ACTION_UNDEFINED },
|
||||||
val isUnskippable: Boolean = false,
|
val isUnskippable: Boolean = false,
|
||||||
val hasWarning: Boolean = false,
|
val hasWarning: Boolean = false,
|
||||||
val stepCompleted: () -> StepState = { StepState.STEP_UNDEFINED },
|
|
||||||
val warningTitleId: Int = 0,
|
val warningTitleId: Int = 0,
|
||||||
val warningDescriptionId: Int = 0,
|
val warningDescriptionId: Int = 0,
|
||||||
val warningHelpLinkId: Int = 0
|
val warningHelpLinkId: Int = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
interface SetupCallback {
|
interface SetupCallback {
|
||||||
fun onStepCompleted()
|
fun onStepCompleted(pageButtonId : Int, pageFullyCompleted: Boolean)
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class StepState {
|
enum class PageState {
|
||||||
STEP_COMPLETE,
|
PAGE_STEPS_COMPLETE,
|
||||||
STEP_INCOMPLETE,
|
PAGE_STEPS_INCOMPLETE,
|
||||||
STEP_UNDEFINED
|
PAGE_STEPS_UNDEFINED
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class ButtonState {
|
||||||
|
BUTTON_ACTION_COMPLETE,
|
||||||
|
BUTTON_ACTION_INCOMPLETE,
|
||||||
|
BUTTON_ACTION_UNDEFINED
|
||||||
}
|
}
|
||||||
|
@ -307,7 +307,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||||||
return@registerForActivityResult
|
return@registerForActivityResult
|
||||||
}
|
}
|
||||||
|
|
||||||
CitraDirectoryHelper(this@MainActivity).showCitraDirectoryDialog(result)
|
CitraDirectoryHelper(this@MainActivity).showCitraDirectoryDialog(result, buttonState = {})
|
||||||
}
|
}
|
||||||
|
|
||||||
val ciaFileInstaller = registerForActivityResult(
|
val ciaFileInstaller = registerForActivityResult(
|
||||||
|
@ -17,7 +17,7 @@ import org.citra.citra_emu.viewmodel.HomeViewModel
|
|||||||
* Citra directory initialization ui flow controller.
|
* Citra directory initialization ui flow controller.
|
||||||
*/
|
*/
|
||||||
class CitraDirectoryHelper(private val fragmentActivity: FragmentActivity) {
|
class CitraDirectoryHelper(private val fragmentActivity: FragmentActivity) {
|
||||||
fun showCitraDirectoryDialog(result: Uri, callback: SetupCallback? = null) {
|
fun showCitraDirectoryDialog(result: Uri, callback: SetupCallback? = null, buttonState: () -> Unit) {
|
||||||
val citraDirectoryDialog = CitraDirectoryDialogFragment.newInstance(
|
val citraDirectoryDialog = CitraDirectoryDialogFragment.newInstance(
|
||||||
fragmentActivity,
|
fragmentActivity,
|
||||||
result.toString(),
|
result.toString(),
|
||||||
@ -36,7 +36,7 @@ class CitraDirectoryHelper(private val fragmentActivity: FragmentActivity) {
|
|||||||
)
|
)
|
||||||
if (!moveData || previous.toString().isEmpty()) {
|
if (!moveData || previous.toString().isEmpty()) {
|
||||||
initializeCitraDirectory(path)
|
initializeCitraDirectory(path)
|
||||||
callback?.onStepCompleted()
|
buttonState()
|
||||||
val viewModel = ViewModelProvider(fragmentActivity)[HomeViewModel::class.java]
|
val viewModel = ViewModelProvider(fragmentActivity)[HomeViewModel::class.java]
|
||||||
viewModel.setUserDir(fragmentActivity, path.path!!)
|
viewModel.setUserDir(fragmentActivity, path.path!!)
|
||||||
viewModel.setPickingUserDir(false)
|
viewModel.setPickingUserDir(false)
|
||||||
|
9
src/android/app/src/main/res/drawable/ic_permission.xml
Normal file
9
src/android/app/src/main/res/drawable/ic_permission.xml
Normal file
@ -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>
|
8
src/android/app/src/main/res/layout/page_button.xml
Normal file
8
src/android/app/src/main/res/layout/page_button.xml
Normal file
@ -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"
|
||||||
@ -43,11 +42,11 @@
|
|||||||
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="78dp"
|
||||||
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"
|
||||||
@ -58,15 +57,15 @@
|
|||||||
<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/TextAppearance.Material3.TitleLarge"
|
||||||
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:text="@string/step_complete"
|
||||||
android:textAlignment="center"
|
android:textAlignment="center"
|
||||||
android:textSize="30sp"
|
android:textSize="30sp"
|
||||||
android:visibility="invisible"
|
|
||||||
android:text="@string/step_complete"
|
|
||||||
android:textStyle="bold"
|
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"
|
||||||
@ -74,19 +73,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/pege_button_container"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="56dp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
android:orientation="vertical"
|
||||||
android:layout_marginBottom="48dp"
|
android:padding="16dp"
|
||||||
android:textSize="20sp"
|
android:gravity="center"
|
||||||
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>
|
||||||
|
@ -85,13 +85,17 @@
|
|||||||
<string name="add_games_warning">Skip selecting applications folder?</string>
|
<string name="add_games_warning">Skip selecting applications folder?</string>
|
||||||
<string name="add_games_warning_description">Software won\'t be displayed in the Applications list if a folder isn\'t selected.</string>
|
<string name="add_games_warning_description">Software won\'t be displayed in the Applications list if a folder isn\'t selected.</string>
|
||||||
<string name="add_games_warning_help" translatable="false">https://web.archive.org/web/20240304210021/https://citra-emu.org/wiki/dumping-game-cartridges/</string>
|
<string name="add_games_warning_help" translatable="false">https://web.archive.org/web/20240304210021/https://citra-emu.org/wiki/dumping-game-cartridges/</string>
|
||||||
|
<string name="permissions">Permissions</string>
|
||||||
|
<string name="select_emulalator_data_folder">Data Folders</string>
|
||||||
|
<string name="select_emulalator_data_folder_description">Select data folders (User folder is required)</string>
|
||||||
|
<string name="permissions_description">Grant optional permissions to use specific features of the emulator</string>
|
||||||
<string name="warning_help">Help</string>
|
<string name="warning_help">Help</string>
|
||||||
<string name="warning_skip">Skip</string>
|
<string name="warning_skip">Skip</string>
|
||||||
<string name="warning_cancel">Cancel</string>
|
<string name="warning_cancel">Cancel</string>
|
||||||
<string name="select_citra_user_folder">Select User Folder</string>
|
<string name="select_citra_user_folder">Select User Folder</string>
|
||||||
<string name="select_citra_user_folder_description"><![CDATA[Select your <a href="https://web.archive.org/web/20240304193549/https://github.com/citra-emu/citra/wiki/Citra-Android-user-data-and-storage">user data</a> directory with the button below.]]></string>
|
<string name="select_citra_user_folder_description"><![CDATA[Select your <a href="https://web.archive.org/web/20240304193549/https://github.com/citra-emu/citra/wiki/Citra-Android-user-data-and-storage">user data</a> directory with the button below.]]></string>
|
||||||
<string name="select">Select</string>
|
<string name="select">Select</string>
|
||||||
<string name="cannot_skip">You can\'t skip this step</string>
|
<string name="cannot_skip">You can\'t skip setting the user folder</string>
|
||||||
<string name="cannot_skip_directory_description">This step is required to allow Azahar to work. Please select a directory and then you can continue.</string>
|
<string name="cannot_skip_directory_description">This step is required to allow Azahar to work. Please select a directory and then you can continue.</string>
|
||||||
<string name="cannot_skip_directory_help" translatable="false">https://web.archive.org/web/20240304193549/https://github.com/citra-emu/citra/wiki/Citra-Android-user-data-and-storage</string>
|
<string name="cannot_skip_directory_help" translatable="false">https://web.archive.org/web/20240304193549/https://github.com/citra-emu/citra/wiki/Citra-Android-user-data-and-storage</string>
|
||||||
<string name="set_up_theme_settings">Theme Settings</string>
|
<string name="set_up_theme_settings">Theme Settings</string>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user