mirror of
https://git.eden-emu.dev/eden-emu/eden
synced 2026-04-10 05:28:56 +02:00
[android,ui] chore: settings subscreens transition and other minor conformances
This commit is contained in:
parent
0dad29698e
commit
329a8a31a7
24 changed files with 1064 additions and 476 deletions
|
|
@ -73,6 +73,11 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||
android:theme="@style/Theme.Yuzu.Main"
|
||||
android:label="@string/preferences_settings"/>
|
||||
|
||||
<activity
|
||||
android:name="org.yuzu.yuzu_emu.features.settings.ui.SettingsSubscreenActivity"
|
||||
android:theme="@style/Theme.Yuzu.Main"
|
||||
android:label="@string/preferences_settings"/>
|
||||
|
||||
<activity
|
||||
android:name="org.yuzu.yuzu_emu.activities.EmulationActivity"
|
||||
android:theme="@style/Theme.Yuzu.Main"
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -6,10 +9,10 @@ package org.yuzu.yuzu_emu.adapters
|
|||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.navigation.findNavController
|
||||
import org.yuzu.yuzu_emu.HomeNavigationDirections
|
||||
import org.yuzu.yuzu_emu.NativeLibrary
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.YuzuApplication
|
||||
|
|
@ -67,8 +70,13 @@ class AppletAdapter(val activity: FragmentActivity, applets: List<Applet>) :
|
|||
title = YuzuApplication.appContext.getString(applet.titleId),
|
||||
path = appletPath
|
||||
)
|
||||
val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame)
|
||||
binding.root.findNavController().navigate(action)
|
||||
binding.root.findNavController().navigate(
|
||||
R.id.action_global_emulationActivity,
|
||||
bundleOf(
|
||||
"game" to appletGame,
|
||||
"custom" to false
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -111,18 +111,10 @@ class SettingsActivity : AppCompatActivity() {
|
|||
if (navHostFragment.childFragmentManager.backStackEntryCount > 0) {
|
||||
navHostFragment.navController.popBackStack()
|
||||
} else {
|
||||
finishWithFragmentLikeAnimation()
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
private fun finishWithFragmentLikeAnimation() {
|
||||
finish()
|
||||
overridePendingTransition(
|
||||
androidx.navigation.ui.R.anim.nav_default_pop_enter_anim,
|
||||
androidx.navigation.ui.R.anim.nav_default_pop_exit_anim
|
||||
)
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
if (!DirectoryInitialization.areDirectoriesReady) {
|
||||
|
|
@ -178,7 +170,7 @@ class SettingsActivity : AppCompatActivity() {
|
|||
getString(R.string.settings_reset),
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
finishWithFragmentLikeAnimation()
|
||||
finish()
|
||||
}
|
||||
|
||||
private fun setInsets() {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,152 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.features.settings.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewGroup.MarginLayoutParams
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.navigation.fragment.NavHostFragment
|
||||
import androidx.navigation.navArgs
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.YuzuApplication
|
||||
import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding
|
||||
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
|
||||
import org.yuzu.yuzu_emu.utils.InsetsHelper
|
||||
import org.yuzu.yuzu_emu.utils.ThemeHelper
|
||||
|
||||
enum class SettingsSubscreen {
|
||||
PROFILE_MANAGER,
|
||||
DRIVER_MANAGER,
|
||||
DRIVER_FETCHER,
|
||||
FREEDRENO_SETTINGS,
|
||||
APPLET_LAUNCHER,
|
||||
INSTALLABLE,
|
||||
GAME_FOLDERS,
|
||||
ABOUT,
|
||||
LICENSES,
|
||||
GAME_INFO,
|
||||
ADDONS,
|
||||
}
|
||||
|
||||
class SettingsSubscreenActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ActivitySettingsBinding
|
||||
|
||||
private val args by navArgs<SettingsSubscreenActivityArgs>()
|
||||
|
||||
override fun attachBaseContext(base: Context) {
|
||||
super.attachBaseContext(YuzuApplication.applyLanguage(base))
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
ThemeHelper.setTheme(this)
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
binding = ActivitySettingsBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
val navHostFragment =
|
||||
supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment
|
||||
if (savedInstanceState == null) {
|
||||
val navController = navHostFragment.navController
|
||||
val navGraph = navController.navInflater.inflate(
|
||||
R.navigation.settings_subscreen_navigation
|
||||
)
|
||||
navGraph.setStartDestination(resolveStartDestination())
|
||||
navController.setGraph(navGraph, createStartDestinationArgs())
|
||||
}
|
||||
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
|
||||
if (InsetsHelper.getSystemGestureType(applicationContext) !=
|
||||
InsetsHelper.GESTURE_NAVIGATION
|
||||
) {
|
||||
binding.navigationBarShade.setBackgroundColor(
|
||||
ThemeHelper.getColorWithOpacity(
|
||||
MaterialColors.getColor(
|
||||
binding.navigationBarShade,
|
||||
com.google.android.material.R.attr.colorSurface
|
||||
),
|
||||
ThemeHelper.SYSTEM_BAR_ALPHA
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
onBackPressedDispatcher.addCallback(
|
||||
this,
|
||||
object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() = navigateBack()
|
||||
}
|
||||
)
|
||||
|
||||
setInsets()
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
if (!DirectoryInitialization.areDirectoriesReady) {
|
||||
DirectoryInitialization.start()
|
||||
}
|
||||
}
|
||||
|
||||
fun navigateBack() {
|
||||
val navHostFragment =
|
||||
supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment
|
||||
if (!navHostFragment.navController.popBackStack()) {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
private fun resolveStartDestination(): Int =
|
||||
when (args.destination) {
|
||||
SettingsSubscreen.PROFILE_MANAGER -> R.id.profileManagerFragment
|
||||
SettingsSubscreen.DRIVER_MANAGER -> R.id.driverManagerFragment
|
||||
SettingsSubscreen.DRIVER_FETCHER -> R.id.driverFetcherFragment
|
||||
SettingsSubscreen.FREEDRENO_SETTINGS -> R.id.freedrenoSettingsFragment
|
||||
SettingsSubscreen.APPLET_LAUNCHER -> R.id.appletLauncherFragment
|
||||
SettingsSubscreen.INSTALLABLE -> R.id.installableFragment
|
||||
SettingsSubscreen.GAME_FOLDERS -> R.id.gameFoldersFragment
|
||||
SettingsSubscreen.ABOUT -> R.id.aboutFragment
|
||||
SettingsSubscreen.LICENSES -> R.id.licensesFragment
|
||||
SettingsSubscreen.GAME_INFO -> R.id.gameInfoFragment
|
||||
SettingsSubscreen.ADDONS -> R.id.addonsFragment
|
||||
}
|
||||
|
||||
private fun createStartDestinationArgs(): Bundle =
|
||||
when (args.destination) {
|
||||
SettingsSubscreen.DRIVER_MANAGER,
|
||||
SettingsSubscreen.FREEDRENO_SETTINGS -> bundleOf("game" to args.game)
|
||||
|
||||
SettingsSubscreen.GAME_INFO,
|
||||
SettingsSubscreen.ADDONS -> bundleOf(
|
||||
"game" to requireNotNull(args.game) {
|
||||
"Game is required for ${args.destination}"
|
||||
}
|
||||
)
|
||||
|
||||
else -> Bundle()
|
||||
}
|
||||
|
||||
private fun setInsets() {
|
||||
ViewCompat.setOnApplyWindowInsetsListener(
|
||||
binding.navigationBarShade
|
||||
) { _: View, windowInsets: WindowInsetsCompat ->
|
||||
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
|
||||
val mlpNavShade = binding.navigationBarShade.layoutParams as MarginLayoutParams
|
||||
mlpNavShade.height = barInsets.bottom
|
||||
binding.navigationBarShade.layoutParams = mlpNavShade
|
||||
|
||||
windowInsets
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -21,9 +21,10 @@ import androidx.fragment.app.Fragment
|
|||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.navigation.findNavController
|
||||
import com.google.android.material.transition.MaterialSharedAxis
|
||||
import org.yuzu.yuzu_emu.BuildConfig
|
||||
import org.yuzu.yuzu_emu.HomeNavigationDirections
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.databinding.FragmentAboutBinding
|
||||
import org.yuzu.yuzu_emu.features.settings.ui.SettingsSubscreen
|
||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
|
||||
import org.yuzu.yuzu_emu.NativeLibrary
|
||||
|
|
@ -54,7 +55,7 @@ class AboutFragment : Fragment() {
|
|||
super.onViewCreated(view, savedInstanceState)
|
||||
homeViewModel.setStatusBarShadeVisibility(visible = false)
|
||||
binding.toolbarAbout.setNavigationOnClickListener {
|
||||
binding.root.findNavController().popBackStack()
|
||||
requireActivity().onBackPressedDispatcher.onBackPressed()
|
||||
}
|
||||
|
||||
binding.imageLogo.setOnLongClickListener {
|
||||
|
|
@ -72,8 +73,11 @@ class AboutFragment : Fragment() {
|
|||
)
|
||||
}
|
||||
binding.buttonLicenses.setOnClickListener {
|
||||
exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
|
||||
binding.root.findNavController().navigate(R.id.action_aboutFragment_to_licensesFragment)
|
||||
val action = HomeNavigationDirections.actionGlobalSettingsSubscreenActivity(
|
||||
SettingsSubscreen.LICENSES,
|
||||
null
|
||||
)
|
||||
binding.root.findNavController().navigate(action)
|
||||
}
|
||||
|
||||
val buildName = getString(R.string.app_name_suffixed)
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import androidx.core.view.updatePadding
|
|||
import androidx.documentfile.provider.DocumentFile
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.transition.MaterialSharedAxis
|
||||
|
|
@ -61,7 +60,7 @@ class AddonsFragment : Fragment() {
|
|||
homeViewModel.setStatusBarShadeVisibility(false)
|
||||
|
||||
binding.toolbarAddons.setNavigationOnClickListener {
|
||||
binding.root.findNavController().popBackStack()
|
||||
requireActivity().onBackPressedDispatcher.onBackPressed()
|
||||
}
|
||||
|
||||
binding.toolbarAddons.title = getString(R.string.addons_game, args.game.title)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.fragments
|
||||
|
|
@ -12,7 +12,6 @@ import androidx.core.view.WindowInsetsCompat
|
|||
import androidx.core.view.updatePadding
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import com.google.android.material.transition.MaterialSharedAxis
|
||||
import org.yuzu.yuzu_emu.R
|
||||
|
|
@ -50,7 +49,7 @@ class AppletLauncherFragment : Fragment() {
|
|||
homeViewModel.setStatusBarShadeVisibility(visible = false)
|
||||
|
||||
binding.toolbarApplets.setNavigationOnClickListener {
|
||||
binding.root.findNavController().popBackStack()
|
||||
requireActivity().onBackPressedDispatcher.onBackPressed()
|
||||
}
|
||||
|
||||
val applets = listOf(
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import androidx.core.view.WindowInsetsCompat
|
|||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.fasterxml.jackson.databind.JsonNode
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
|
|
@ -142,7 +141,7 @@ class DriverFetcherFragment : Fragment() {
|
|||
super.onViewCreated(view, savedInstanceState)
|
||||
homeViewModel.setStatusBarShadeVisibility(visible = false)
|
||||
binding.toolbarDrivers.setNavigationOnClickListener {
|
||||
binding.root.findNavController().popBackStack()
|
||||
requireActivity().onBackPressedDispatcher.onBackPressed()
|
||||
}
|
||||
|
||||
binding.listDrivers.layoutManager = LinearLayoutManager(context)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.fragments
|
||||
|
|
@ -19,6 +19,7 @@ import androidx.navigation.fragment.navArgs
|
|||
import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import com.google.android.material.transition.MaterialSharedAxis
|
||||
import org.yuzu.yuzu_emu.HomeNavigationDirections
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
|
@ -27,6 +28,7 @@ import org.yuzu.yuzu_emu.adapters.DriverAdapter
|
|||
import org.yuzu.yuzu_emu.databinding.FragmentDriverManagerBinding
|
||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
||||
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.ui.SettingsSubscreen
|
||||
import org.yuzu.yuzu_emu.model.Driver.Companion.toDriver
|
||||
import org.yuzu.yuzu_emu.model.DriverViewModel
|
||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||
|
|
@ -105,7 +107,7 @@ class DriverManagerFragment : Fragment() {
|
|||
}
|
||||
|
||||
binding.toolbarDrivers.setNavigationOnClickListener {
|
||||
binding.root.findNavController().popBackStack()
|
||||
requireActivity().onBackPressedDispatcher.onBackPressed()
|
||||
}
|
||||
|
||||
binding.buttonInstall.setOnClickListener {
|
||||
|
|
@ -113,9 +115,11 @@ class DriverManagerFragment : Fragment() {
|
|||
}
|
||||
|
||||
binding.buttonFetch.setOnClickListener {
|
||||
binding.root.findNavController().navigate(
|
||||
R.id.action_driverManagerFragment_to_driverFetcherFragment
|
||||
val action = HomeNavigationDirections.actionGlobalSettingsSubscreenActivity(
|
||||
SettingsSubscreen.DRIVER_FETCHER,
|
||||
null
|
||||
)
|
||||
binding.root.findNavController().navigate(action)
|
||||
}
|
||||
|
||||
binding.listDrivers.apply {
|
||||
|
|
|
|||
|
|
@ -4,20 +4,21 @@
|
|||
package org.yuzu.yuzu_emu.fragments
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.transition.MaterialSharedAxis
|
||||
import kotlinx.coroutines.launch
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.adapters.FolderAdapter
|
||||
import org.yuzu.yuzu_emu.databinding.FragmentFoldersBinding
|
||||
|
|
@ -25,7 +26,6 @@ import org.yuzu.yuzu_emu.model.DirectoryType
|
|||
import org.yuzu.yuzu_emu.model.GameDir
|
||||
import org.yuzu.yuzu_emu.model.GamesViewModel
|
||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||
import org.yuzu.yuzu_emu.ui.main.MainActivity
|
||||
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
|
||||
import org.yuzu.yuzu_emu.utils.collect
|
||||
|
||||
|
|
@ -36,6 +36,20 @@ class GameFoldersFragment : Fragment() {
|
|||
private val homeViewModel: HomeViewModel by activityViewModels()
|
||||
private val gamesViewModel: GamesViewModel by activityViewModels()
|
||||
|
||||
private val getGamesDirectory =
|
||||
registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
|
||||
if (result != null) {
|
||||
processGamesDir(result)
|
||||
}
|
||||
}
|
||||
|
||||
private val getExternalContentDirectory =
|
||||
registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
|
||||
if (result != null) {
|
||||
processExternalContentDir(result)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
|
||||
|
|
@ -59,7 +73,7 @@ class GameFoldersFragment : Fragment() {
|
|||
homeViewModel.setStatusBarShadeVisibility(visible = false)
|
||||
|
||||
binding.toolbarFolders.setNavigationOnClickListener {
|
||||
binding.root.findNavController().popBackStack()
|
||||
requireActivity().onBackPressedDispatcher.onBackPressed()
|
||||
}
|
||||
|
||||
binding.listFolders.apply {
|
||||
|
|
@ -74,7 +88,6 @@ class GameFoldersFragment : Fragment() {
|
|||
(binding.listFolders.adapter as FolderAdapter).submitList(it)
|
||||
}
|
||||
|
||||
val mainActivity = requireActivity() as MainActivity
|
||||
binding.buttonAdd.setOnClickListener {
|
||||
// Show a model to choose between Game and External Content
|
||||
val options = arrayOf(
|
||||
|
|
@ -87,10 +100,10 @@ class GameFoldersFragment : Fragment() {
|
|||
.setItems(options) { _, which ->
|
||||
when (which) {
|
||||
0 -> { // Game Folder
|
||||
mainActivity.getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data)
|
||||
getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data)
|
||||
}
|
||||
1 -> { // External Content Folder
|
||||
mainActivity.getExternalContentDirectory.launch(null)
|
||||
getExternalContentDirectory.launch(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -105,6 +118,50 @@ class GameFoldersFragment : Fragment() {
|
|||
gamesViewModel.onCloseGameFoldersFragment()
|
||||
}
|
||||
|
||||
private fun processGamesDir(result: Uri) {
|
||||
requireContext().contentResolver.takePersistableUriPermission(
|
||||
result,
|
||||
Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
)
|
||||
|
||||
val uriString = result.toString()
|
||||
val folder = gamesViewModel.folders.value.firstOrNull { it.uriString == uriString }
|
||||
if (folder != null) {
|
||||
Toast.makeText(
|
||||
requireContext().applicationContext,
|
||||
R.string.folder_already_added,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
return
|
||||
}
|
||||
|
||||
AddGameFolderDialogFragment.newInstance(uriString, calledFromGameFragment = false)
|
||||
.show(parentFragmentManager, AddGameFolderDialogFragment.TAG)
|
||||
}
|
||||
|
||||
private fun processExternalContentDir(result: Uri) {
|
||||
requireContext().contentResolver.takePersistableUriPermission(
|
||||
result,
|
||||
Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
)
|
||||
|
||||
val uriString = result.toString()
|
||||
val folder = gamesViewModel.folders.value.firstOrNull {
|
||||
it.uriString == uriString && it.type == DirectoryType.EXTERNAL_CONTENT
|
||||
}
|
||||
if (folder != null) {
|
||||
Toast.makeText(
|
||||
requireContext().applicationContext,
|
||||
R.string.folder_already_added,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
return
|
||||
}
|
||||
|
||||
val externalContentDir = GameDir(uriString, deepScan = false, DirectoryType.EXTERNAL_CONTENT)
|
||||
gamesViewModel.addFolder(externalContentDir, savedFromGameFragment = false)
|
||||
}
|
||||
|
||||
private fun setInsets() =
|
||||
ViewCompat.setOnApplyWindowInsetsListener(
|
||||
binding.root
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.fragments
|
||||
|
|
@ -18,7 +18,6 @@ import androidx.core.view.WindowInsetsCompat
|
|||
import androidx.core.view.updatePadding
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import com.google.android.material.transition.MaterialSharedAxis
|
||||
import org.yuzu.yuzu_emu.NativeLibrary
|
||||
|
|
@ -64,7 +63,7 @@ class GameInfoFragment : Fragment() {
|
|||
binding.apply {
|
||||
toolbarInfo.title = args.game.title
|
||||
toolbarInfo.setNavigationOnClickListener {
|
||||
view.findNavController().popBackStack()
|
||||
requireActivity().onBackPressedDispatcher.onBackPressed()
|
||||
}
|
||||
|
||||
val pathString = Uri.parse(args.game.path).path ?: ""
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ import org.yuzu.yuzu_emu.adapters.GamePropertiesAdapter
|
|||
import org.yuzu.yuzu_emu.databinding.FragmentGamePropertiesBinding
|
||||
import org.yuzu.yuzu_emu.features.DocumentProvider
|
||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
||||
import org.yuzu.yuzu_emu.features.settings.ui.SettingsSubscreen
|
||||
import org.yuzu.yuzu_emu.model.DriverViewModel
|
||||
import org.yuzu.yuzu_emu.model.GameProperty
|
||||
import org.yuzu.yuzu_emu.model.GamesViewModel
|
||||
|
|
@ -250,8 +251,10 @@ class GamePropertiesFragment : Fragment() {
|
|||
R.string.info_description,
|
||||
R.drawable.ic_info_outline,
|
||||
action = {
|
||||
val action = GamePropertiesFragmentDirections
|
||||
.actionPerGamePropertiesFragmentToGameInfoFragment(args.game)
|
||||
val action = HomeNavigationDirections.actionGlobalSettingsSubscreenActivity(
|
||||
SettingsSubscreen.GAME_INFO,
|
||||
args.game
|
||||
)
|
||||
binding.root.findNavController().navigate(action)
|
||||
}
|
||||
)
|
||||
|
|
@ -317,8 +320,11 @@ class GamePropertiesFragment : Fragment() {
|
|||
R.string.add_ons_description,
|
||||
R.drawable.ic_edit,
|
||||
action = {
|
||||
val action = GamePropertiesFragmentDirections
|
||||
.actionPerGamePropertiesFragmentToAddonsFragment(args.game)
|
||||
val action =
|
||||
HomeNavigationDirections.actionGlobalSettingsSubscreenActivity(
|
||||
SettingsSubscreen.ADDONS,
|
||||
args.game
|
||||
)
|
||||
binding.root.findNavController().navigate(action)
|
||||
}
|
||||
)
|
||||
|
|
@ -333,8 +339,11 @@ class GamePropertiesFragment : Fragment() {
|
|||
R.drawable.ic_build,
|
||||
detailsFlow = driverViewModel.selectedDriverTitle,
|
||||
action = {
|
||||
val action = GamePropertiesFragmentDirections
|
||||
.actionPerGamePropertiesFragmentToDriverManagerFragment(args.game)
|
||||
val action =
|
||||
HomeNavigationDirections.actionGlobalSettingsSubscreenActivity(
|
||||
SettingsSubscreen.DRIVER_MANAGER,
|
||||
args.game
|
||||
)
|
||||
binding.root.findNavController().navigate(action)
|
||||
}
|
||||
)
|
||||
|
|
@ -347,8 +356,11 @@ class GamePropertiesFragment : Fragment() {
|
|||
R.string.freedreno_per_game_description,
|
||||
R.drawable.ic_graphics,
|
||||
action = {
|
||||
val action = GamePropertiesFragmentDirections
|
||||
.actionPerGamePropertiesFragmentToFreedrenoSettingsFragment(args.game)
|
||||
val action =
|
||||
HomeNavigationDirections.actionGlobalSettingsSubscreenActivity(
|
||||
SettingsSubscreen.FREEDRENO_SETTINGS,
|
||||
args.game
|
||||
)
|
||||
binding.root.findNavController().navigate(action)
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import org.yuzu.yuzu_emu.databinding.FragmentHomeSettingsBinding
|
|||
import org.yuzu.yuzu_emu.features.DocumentProvider
|
||||
import org.yuzu.yuzu_emu.features.fetcher.SpacingItemDecoration
|
||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
||||
import org.yuzu.yuzu_emu.features.settings.ui.SettingsSubscreen
|
||||
import org.yuzu.yuzu_emu.model.DriverViewModel
|
||||
import org.yuzu.yuzu_emu.model.HomeSetting
|
||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||
|
|
@ -126,8 +127,11 @@ class HomeSettingsFragment : Fragment() {
|
|||
R.string.profile_manager_description,
|
||||
R.drawable.ic_account_circle,
|
||||
{
|
||||
binding.root.findNavController()
|
||||
.navigate(R.id.action_homeSettingsFragment_to_profileManagerFragment)
|
||||
val action = HomeNavigationDirections.actionGlobalSettingsSubscreenActivity(
|
||||
SettingsSubscreen.PROFILE_MANAGER,
|
||||
null
|
||||
)
|
||||
binding.root.findNavController().navigate(action)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
|
@ -137,8 +141,10 @@ class HomeSettingsFragment : Fragment() {
|
|||
R.string.install_gpu_driver_description,
|
||||
R.drawable.ic_build,
|
||||
{
|
||||
val action = HomeSettingsFragmentDirections
|
||||
.actionHomeSettingsFragmentToDriverManagerFragment(null)
|
||||
val action = HomeNavigationDirections.actionGlobalSettingsSubscreenActivity(
|
||||
SettingsSubscreen.DRIVER_MANAGER,
|
||||
null
|
||||
)
|
||||
binding.root.findNavController().navigate(action)
|
||||
},
|
||||
{ true },
|
||||
|
|
@ -154,7 +160,12 @@ class HomeSettingsFragment : Fragment() {
|
|||
R.string.gpu_driver_settings,
|
||||
R.drawable.ic_graphics,
|
||||
{
|
||||
binding.root.findNavController().navigate(R.id.freedrenoSettingsFragment)
|
||||
val action =
|
||||
HomeNavigationDirections.actionGlobalSettingsSubscreenActivity(
|
||||
SettingsSubscreen.FREEDRENO_SETTINGS,
|
||||
null
|
||||
)
|
||||
binding.root.findNavController().navigate(action)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
|
@ -175,8 +186,11 @@ class HomeSettingsFragment : Fragment() {
|
|||
R.string.applets_description,
|
||||
R.drawable.ic_applet,
|
||||
{
|
||||
binding.root.findNavController()
|
||||
.navigate(R.id.action_homeSettingsFragment_to_appletLauncherFragment)
|
||||
val action = HomeNavigationDirections.actionGlobalSettingsSubscreenActivity(
|
||||
SettingsSubscreen.APPLET_LAUNCHER,
|
||||
null
|
||||
)
|
||||
binding.root.findNavController().navigate(action)
|
||||
},
|
||||
{ NativeLibrary.isFirmwareAvailable() },
|
||||
R.string.applets_error_firmware,
|
||||
|
|
@ -189,8 +203,11 @@ class HomeSettingsFragment : Fragment() {
|
|||
R.string.manage_yuzu_data_description,
|
||||
R.drawable.ic_install,
|
||||
{
|
||||
binding.root.findNavController()
|
||||
.navigate(R.id.action_homeSettingsFragment_to_installableFragment)
|
||||
val action = HomeNavigationDirections.actionGlobalSettingsSubscreenActivity(
|
||||
SettingsSubscreen.INSTALLABLE,
|
||||
null
|
||||
)
|
||||
binding.root.findNavController().navigate(action)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
|
@ -200,8 +217,11 @@ class HomeSettingsFragment : Fragment() {
|
|||
R.string.select_games_folder_description,
|
||||
R.drawable.ic_add,
|
||||
{
|
||||
binding.root.findNavController()
|
||||
.navigate(R.id.action_homeSettingsFragment_to_gameFoldersFragment)
|
||||
val action = HomeNavigationDirections.actionGlobalSettingsSubscreenActivity(
|
||||
SettingsSubscreen.GAME_FOLDERS,
|
||||
null
|
||||
)
|
||||
binding.root.findNavController().navigate(action)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
|
@ -284,9 +304,11 @@ class HomeSettingsFragment : Fragment() {
|
|||
R.string.about_description,
|
||||
R.drawable.ic_info_outline,
|
||||
{
|
||||
exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
|
||||
parentFragmentManager.primaryNavigationFragment?.findNavController()
|
||||
?.navigate(R.id.action_homeSettingsFragment_to_aboutFragment)
|
||||
val action = HomeNavigationDirections.actionGlobalSettingsSubscreenActivity(
|
||||
SettingsSubscreen.ABOUT,
|
||||
null
|
||||
)
|
||||
binding.root.findNavController().navigate(action)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.fragments
|
||||
|
|
@ -14,23 +14,23 @@ import androidx.core.view.WindowInsetsCompat
|
|||
import androidx.core.view.updatePadding
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import com.google.android.material.transition.MaterialSharedAxis
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.yuzu.yuzu_emu.NativeLibrary
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.YuzuApplication
|
||||
import org.yuzu.yuzu_emu.adapters.InstallableAdapter
|
||||
import org.yuzu.yuzu_emu.databinding.FragmentInstallablesBinding
|
||||
import org.yuzu.yuzu_emu.model.AddonViewModel
|
||||
import org.yuzu.yuzu_emu.model.DriverViewModel
|
||||
import org.yuzu.yuzu_emu.model.GamesViewModel
|
||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||
import org.yuzu.yuzu_emu.model.Installable
|
||||
import org.yuzu.yuzu_emu.model.TaskState
|
||||
import org.yuzu.yuzu_emu.ui.main.MainActivity
|
||||
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
|
||||
import org.yuzu.yuzu_emu.utils.FileUtil
|
||||
import org.yuzu.yuzu_emu.utils.InstallableActions
|
||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
||||
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
|
||||
import org.yuzu.yuzu_emu.utils.collect
|
||||
|
|
@ -45,6 +45,9 @@ class InstallableFragment : Fragment() {
|
|||
private val binding get() = _binding!!
|
||||
|
||||
private val homeViewModel: HomeViewModel by activityViewModels()
|
||||
private val gamesViewModel: GamesViewModel by activityViewModels()
|
||||
private val addonViewModel: AddonViewModel by activityViewModels()
|
||||
private val driverViewModel: DriverViewModel by activityViewModels()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
|
@ -65,12 +68,10 @@ class InstallableFragment : Fragment() {
|
|||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val mainActivity = requireActivity() as MainActivity
|
||||
|
||||
homeViewModel.setStatusBarShadeVisibility(visible = false)
|
||||
|
||||
binding.toolbarInstallables.setNavigationOnClickListener {
|
||||
binding.root.findNavController().popBackStack()
|
||||
requireActivity().onBackPressedDispatcher.onBackPressed()
|
||||
}
|
||||
|
||||
homeViewModel.openImportSaves.collect(viewLifecycleOwner) {
|
||||
|
|
@ -84,8 +85,8 @@ class InstallableFragment : Fragment() {
|
|||
Installable(
|
||||
R.string.user_data,
|
||||
R.string.user_data_description,
|
||||
install = { mainActivity.importUserData.launch(arrayOf("application/zip")) },
|
||||
export = { mainActivity.exportUserData.launch("export.zip") }
|
||||
install = { importUserDataLauncher.launch(arrayOf("application/zip")) },
|
||||
export = { exportUserDataLauncher.launch("export.zip") }
|
||||
),
|
||||
Installable(
|
||||
R.string.manage_save_data,
|
||||
|
|
@ -127,27 +128,33 @@ class InstallableFragment : Fragment() {
|
|||
Installable(
|
||||
R.string.install_game_content,
|
||||
R.string.install_game_content_description,
|
||||
install = { mainActivity.installGameUpdate.launch(arrayOf("*/*")) }
|
||||
install = { installGameUpdateLauncher.launch(arrayOf("*/*")) }
|
||||
),
|
||||
Installable(
|
||||
R.string.install_firmware,
|
||||
R.string.install_firmware_description,
|
||||
install = { mainActivity.getFirmware.launch(arrayOf("application/zip")) }
|
||||
install = { getFirmwareLauncher.launch(arrayOf("application/zip")) }
|
||||
),
|
||||
Installable(
|
||||
R.string.uninstall_firmware,
|
||||
R.string.uninstall_firmware_description,
|
||||
install = { mainActivity.uninstallFirmware() }
|
||||
install = {
|
||||
InstallableActions.uninstallFirmware(
|
||||
activity = requireActivity(),
|
||||
fragmentManager = parentFragmentManager,
|
||||
homeViewModel = homeViewModel
|
||||
)
|
||||
}
|
||||
),
|
||||
Installable(
|
||||
R.string.install_prod_keys,
|
||||
R.string.install_prod_keys_description,
|
||||
install = { mainActivity.getProdKey.launch(arrayOf("*/*")) }
|
||||
install = { getProdKeyLauncher.launch(arrayOf("*/*")) }
|
||||
),
|
||||
Installable(
|
||||
R.string.install_amiibo_keys,
|
||||
R.string.install_amiibo_keys_description,
|
||||
install = { mainActivity.getAmiiboKey.launch(arrayOf("*/*")) }
|
||||
install = { getAmiiboKeyLauncher.launch(arrayOf("*/*")) }
|
||||
)
|
||||
)
|
||||
|
||||
|
|
@ -180,6 +187,132 @@ class InstallableFragment : Fragment() {
|
|||
windowInsets
|
||||
}
|
||||
|
||||
private val getProdKeyLauncher =
|
||||
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
|
||||
if (result != null) {
|
||||
InstallableActions.processKey(
|
||||
activity = requireActivity(),
|
||||
fragmentManager = parentFragmentManager,
|
||||
gamesViewModel = gamesViewModel,
|
||||
result = result,
|
||||
extension = "keys"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val getAmiiboKeyLauncher =
|
||||
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
|
||||
if (result != null) {
|
||||
InstallableActions.processKey(
|
||||
activity = requireActivity(),
|
||||
fragmentManager = parentFragmentManager,
|
||||
gamesViewModel = gamesViewModel,
|
||||
result = result,
|
||||
extension = "bin"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val getFirmwareLauncher =
|
||||
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
|
||||
if (result != null) {
|
||||
InstallableActions.processFirmware(
|
||||
activity = requireActivity(),
|
||||
fragmentManager = parentFragmentManager,
|
||||
homeViewModel = homeViewModel,
|
||||
result = result
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val installGameUpdateLauncher =
|
||||
registerForActivityResult(ActivityResultContracts.OpenMultipleDocuments()) { documents ->
|
||||
if (documents.isEmpty()) {
|
||||
return@registerForActivityResult
|
||||
}
|
||||
|
||||
if (addonViewModel.game == null) {
|
||||
InstallableActions.installContent(
|
||||
activity = requireActivity(),
|
||||
fragmentManager = parentFragmentManager,
|
||||
addonViewModel = addonViewModel,
|
||||
documents = documents
|
||||
)
|
||||
return@registerForActivityResult
|
||||
}
|
||||
|
||||
ProgressDialogFragment.newInstance(
|
||||
requireActivity(),
|
||||
R.string.verifying_content,
|
||||
false
|
||||
) { _, _ ->
|
||||
var updatesMatchProgram = true
|
||||
for (document in documents) {
|
||||
val valid = NativeLibrary.doesUpdateMatchProgram(
|
||||
addonViewModel.game!!.programId,
|
||||
document.toString()
|
||||
)
|
||||
if (!valid) {
|
||||
updatesMatchProgram = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (updatesMatchProgram) {
|
||||
requireActivity().runOnUiThread {
|
||||
InstallableActions.installContent(
|
||||
activity = requireActivity(),
|
||||
fragmentManager = parentFragmentManager,
|
||||
addonViewModel = addonViewModel,
|
||||
documents = documents
|
||||
)
|
||||
}
|
||||
} else {
|
||||
requireActivity().runOnUiThread {
|
||||
MessageDialogFragment.newInstance(
|
||||
requireActivity(),
|
||||
titleId = R.string.content_install_notice,
|
||||
descriptionId = R.string.content_install_notice_description,
|
||||
positiveAction = {
|
||||
InstallableActions.installContent(
|
||||
activity = requireActivity(),
|
||||
fragmentManager = parentFragmentManager,
|
||||
addonViewModel = addonViewModel,
|
||||
documents = documents
|
||||
)
|
||||
},
|
||||
negativeAction = {}
|
||||
).show(parentFragmentManager, MessageDialogFragment.TAG)
|
||||
}
|
||||
}
|
||||
return@newInstance Any()
|
||||
}.show(parentFragmentManager, ProgressDialogFragment.TAG)
|
||||
}
|
||||
|
||||
private val importUserDataLauncher =
|
||||
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
|
||||
if (result != null) {
|
||||
InstallableActions.importUserData(
|
||||
activity = requireActivity(),
|
||||
fragmentManager = parentFragmentManager,
|
||||
gamesViewModel = gamesViewModel,
|
||||
driverViewModel = driverViewModel,
|
||||
result = result
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val exportUserDataLauncher =
|
||||
registerForActivityResult(ActivityResultContracts.CreateDocument("application/zip")) { result ->
|
||||
if (result != null) {
|
||||
InstallableActions.exportUserData(
|
||||
activity = requireActivity(),
|
||||
fragmentManager = parentFragmentManager,
|
||||
result = result
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val importSaves =
|
||||
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
|
||||
if (result == null) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.fragments
|
||||
|
|
@ -13,7 +13,6 @@ import androidx.core.view.WindowInsetsCompat
|
|||
import androidx.core.view.updatePadding
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.transition.MaterialSharedAxis
|
||||
import org.yuzu.yuzu_emu.R
|
||||
|
|
@ -48,7 +47,7 @@ class LicensesFragment : Fragment() {
|
|||
homeViewModel.setStatusBarShadeVisibility(visible = false)
|
||||
|
||||
binding.toolbarLicenses.setNavigationOnClickListener {
|
||||
binding.root.findNavController().popBackStack()
|
||||
requireActivity().onBackPressedDispatcher.onBackPressed()
|
||||
}
|
||||
|
||||
val licenses = listOf(
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ class ProfileManagerFragment : Fragment() {
|
|||
homeViewModel.setStatusBarShadeVisibility(visible = false)
|
||||
|
||||
binding.toolbarProfiles.setNavigationOnClickListener {
|
||||
findNavController().popBackStack()
|
||||
requireActivity().onBackPressedDispatcher.onBackPressed()
|
||||
}
|
||||
|
||||
setupRecyclerView()
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ import androidx.preference.PreferenceManager
|
|||
import com.google.android.material.color.MaterialColors
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import java.io.File
|
||||
import java.io.FilenameFilter
|
||||
import org.yuzu.yuzu_emu.NativeLibrary
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
|
||||
|
|
@ -39,16 +38,10 @@ import org.yuzu.yuzu_emu.model.AddonViewModel
|
|||
import org.yuzu.yuzu_emu.model.DriverViewModel
|
||||
import org.yuzu.yuzu_emu.model.GamesViewModel
|
||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||
import org.yuzu.yuzu_emu.model.InstallResult
|
||||
import android.os.Build
|
||||
import org.yuzu.yuzu_emu.model.TaskState
|
||||
import org.yuzu.yuzu_emu.model.TaskViewModel
|
||||
import org.yuzu.yuzu_emu.utils.*
|
||||
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.BufferedOutputStream
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipInputStream
|
||||
import androidx.core.content.edit
|
||||
import org.yuzu.yuzu_emu.activities.EmulationActivity
|
||||
import kotlin.text.compareTo
|
||||
|
|
@ -453,35 +446,13 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||
}
|
||||
|
||||
fun processKey(result: Uri, extension: String = "keys") {
|
||||
contentResolver.takePersistableUriPermission(
|
||||
result,
|
||||
Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
InstallableActions.processKey(
|
||||
activity = this,
|
||||
fragmentManager = supportFragmentManager,
|
||||
gamesViewModel = gamesViewModel,
|
||||
result = result,
|
||||
extension = extension
|
||||
)
|
||||
|
||||
val resultCode: Int = NativeLibrary.installKeys(result.toString(), extension)
|
||||
|
||||
if (resultCode == 0) {
|
||||
// TODO(crueter): It may be worth it to switch some of these Toasts to snackbars,
|
||||
// since most of it is foreground-only anyways.
|
||||
Toast.makeText(
|
||||
applicationContext,
|
||||
R.string.keys_install_success,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
|
||||
gamesViewModel.reloadGames(true)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
val resultString: String =
|
||||
resources.getStringArray(R.array.installKeysResults)[resultCode]
|
||||
|
||||
MessageDialogFragment.newInstance(
|
||||
titleId = R.string.keys_failed,
|
||||
descriptionString = resultString,
|
||||
helpLinkId = R.string.keys_missing_help
|
||||
).show(supportFragmentManager, MessageDialogFragment.TAG)
|
||||
}
|
||||
|
||||
val getFirmware = registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
|
||||
|
|
@ -491,75 +462,21 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||
}
|
||||
|
||||
fun processFirmware(result: Uri, onComplete: (() -> Unit)? = null) {
|
||||
val filterNCA = FilenameFilter { _, dirName -> dirName.endsWith(".nca") }
|
||||
|
||||
val firmwarePath =
|
||||
File(NativeConfig.getNandDir() + "/system/Contents/registered/")
|
||||
val cacheFirmwareDir = File("${cacheDir.path}/registered/")
|
||||
|
||||
ProgressDialogFragment.newInstance(
|
||||
this,
|
||||
R.string.firmware_installing
|
||||
) { progressCallback, _ ->
|
||||
var messageToShow: Any
|
||||
try {
|
||||
FileUtil.unzipToInternalStorage(
|
||||
result.toString(),
|
||||
cacheFirmwareDir,
|
||||
progressCallback
|
||||
)
|
||||
val unfilteredNumOfFiles = cacheFirmwareDir.list()?.size ?: -1
|
||||
val filteredNumOfFiles = cacheFirmwareDir.list(filterNCA)?.size ?: -2
|
||||
messageToShow = if (unfilteredNumOfFiles != filteredNumOfFiles) {
|
||||
MessageDialogFragment.newInstance(
|
||||
this,
|
||||
titleId = R.string.firmware_installed_failure,
|
||||
descriptionId = R.string.firmware_installed_failure_description
|
||||
)
|
||||
} else {
|
||||
firmwarePath.deleteRecursively()
|
||||
cacheFirmwareDir.copyRecursively(firmwarePath, true)
|
||||
NativeLibrary.initializeSystem(true)
|
||||
homeViewModel.setCheckKeys(true)
|
||||
getString(R.string.save_file_imported_success)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.error("[MainActivity] Firmware install failed - ${e.message}")
|
||||
messageToShow = getString(R.string.fatal_error)
|
||||
} finally {
|
||||
cacheFirmwareDir.deleteRecursively()
|
||||
}
|
||||
messageToShow
|
||||
}.apply {
|
||||
onDialogComplete = onComplete
|
||||
}.show(supportFragmentManager, ProgressDialogFragment.TAG)
|
||||
InstallableActions.processFirmware(
|
||||
activity = this,
|
||||
fragmentManager = supportFragmentManager,
|
||||
homeViewModel = homeViewModel,
|
||||
result = result,
|
||||
onComplete = onComplete
|
||||
)
|
||||
}
|
||||
|
||||
fun uninstallFirmware() {
|
||||
val firmwarePath =
|
||||
File(NativeConfig.getNandDir() + "/system/Contents/registered/")
|
||||
ProgressDialogFragment.newInstance(
|
||||
this,
|
||||
R.string.firmware_uninstalling
|
||||
) { progressCallback, _ ->
|
||||
var messageToShow: Any
|
||||
try {
|
||||
// Ensure the firmware directory exists before attempting to delete
|
||||
if (firmwarePath.exists()) {
|
||||
firmwarePath.deleteRecursively()
|
||||
// Optionally reinitialize the system or perform other necessary steps
|
||||
NativeLibrary.initializeSystem(true)
|
||||
homeViewModel.setCheckKeys(true)
|
||||
messageToShow = getString(R.string.firmware_uninstalled_success)
|
||||
} else {
|
||||
messageToShow = getString(R.string.firmware_uninstalled_failure)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.error("[MainActivity] Firmware uninstall failed - ${e.message}")
|
||||
messageToShow = getString(R.string.fatal_error)
|
||||
}
|
||||
messageToShow
|
||||
}.show(supportFragmentManager, ProgressDialogFragment.TAG)
|
||||
InstallableActions.uninstallFirmware(
|
||||
activity = this,
|
||||
fragmentManager = supportFragmentManager,
|
||||
homeViewModel = homeViewModel
|
||||
)
|
||||
}
|
||||
|
||||
val installGameUpdate = registerForActivityResult(
|
||||
|
|
@ -606,101 +523,12 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||
}
|
||||
|
||||
private fun installContent(documents: List<Uri>) {
|
||||
ProgressDialogFragment.newInstance(
|
||||
this@MainActivity,
|
||||
R.string.installing_game_content
|
||||
) { progressCallback, messageCallback ->
|
||||
var installSuccess = 0
|
||||
var installOverwrite = 0
|
||||
var errorBaseGame = 0
|
||||
var error = 0
|
||||
documents.forEach {
|
||||
messageCallback.invoke(FileUtil.getFilename(it))
|
||||
when (
|
||||
InstallResult.from(
|
||||
NativeLibrary.installFileToNand(
|
||||
it.toString(),
|
||||
progressCallback
|
||||
)
|
||||
)
|
||||
) {
|
||||
InstallResult.Success -> {
|
||||
installSuccess += 1
|
||||
}
|
||||
|
||||
InstallResult.Overwrite -> {
|
||||
installOverwrite += 1
|
||||
}
|
||||
|
||||
InstallResult.BaseInstallAttempted -> {
|
||||
errorBaseGame += 1
|
||||
}
|
||||
|
||||
InstallResult.Failure -> {
|
||||
error += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addonViewModel.refreshAddons(force = true)
|
||||
|
||||
val separator = System.lineSeparator() ?: "\n"
|
||||
val installResult = StringBuilder()
|
||||
if (installSuccess > 0) {
|
||||
installResult.append(
|
||||
getString(
|
||||
R.string.install_game_content_success_install,
|
||||
installSuccess
|
||||
)
|
||||
)
|
||||
installResult.append(separator)
|
||||
}
|
||||
if (installOverwrite > 0) {
|
||||
installResult.append(
|
||||
getString(
|
||||
R.string.install_game_content_success_overwrite,
|
||||
installOverwrite
|
||||
)
|
||||
)
|
||||
installResult.append(separator)
|
||||
}
|
||||
val errorTotal: Int = errorBaseGame + error
|
||||
if (errorTotal > 0) {
|
||||
installResult.append(separator)
|
||||
installResult.append(
|
||||
getString(
|
||||
R.string.install_game_content_failed_count,
|
||||
errorTotal
|
||||
)
|
||||
)
|
||||
installResult.append(separator)
|
||||
if (errorBaseGame > 0) {
|
||||
installResult.append(separator)
|
||||
installResult.append(
|
||||
getString(R.string.install_game_content_failure_base)
|
||||
)
|
||||
installResult.append(separator)
|
||||
}
|
||||
if (error > 0) {
|
||||
installResult.append(
|
||||
getString(R.string.install_game_content_failure_description)
|
||||
)
|
||||
installResult.append(separator)
|
||||
}
|
||||
return@newInstance MessageDialogFragment.newInstance(
|
||||
this,
|
||||
titleId = R.string.install_game_content_failure,
|
||||
descriptionString = installResult.toString().trim(),
|
||||
helpLinkId = R.string.install_game_content_help_link
|
||||
)
|
||||
} else {
|
||||
return@newInstance MessageDialogFragment.newInstance(
|
||||
this,
|
||||
titleId = R.string.install_game_content_success,
|
||||
descriptionString = installResult.toString().trim()
|
||||
)
|
||||
}
|
||||
}.show(supportFragmentManager, ProgressDialogFragment.TAG)
|
||||
InstallableActions.installContent(
|
||||
activity = this,
|
||||
fragmentManager = supportFragmentManager,
|
||||
addonViewModel = addonViewModel,
|
||||
documents = documents
|
||||
)
|
||||
}
|
||||
|
||||
val exportUserData = registerForActivityResult(
|
||||
|
|
@ -709,25 +537,11 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||
if (result == null) {
|
||||
return@registerForActivityResult
|
||||
}
|
||||
|
||||
ProgressDialogFragment.newInstance(
|
||||
this,
|
||||
R.string.exporting_user_data,
|
||||
true
|
||||
) { progressCallback, _ ->
|
||||
val zipResult = FileUtil.zipFromInternalStorage(
|
||||
File(DirectoryInitialization.userDirectory!!),
|
||||
DirectoryInitialization.userDirectory!!,
|
||||
BufferedOutputStream(contentResolver.openOutputStream(result)),
|
||||
progressCallback,
|
||||
compression = false
|
||||
)
|
||||
return@newInstance when (zipResult) {
|
||||
TaskState.Completed -> getString(R.string.user_data_export_success)
|
||||
TaskState.Failed -> R.string.export_failed
|
||||
TaskState.Cancelled -> R.string.user_data_export_cancelled
|
||||
}
|
||||
}.show(supportFragmentManager, ProgressDialogFragment.TAG)
|
||||
InstallableActions.exportUserData(
|
||||
activity = this,
|
||||
fragmentManager = supportFragmentManager,
|
||||
result = result
|
||||
)
|
||||
}
|
||||
|
||||
val importUserData =
|
||||
|
|
@ -735,58 +549,12 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||
if (result == null) {
|
||||
return@registerForActivityResult
|
||||
}
|
||||
|
||||
ProgressDialogFragment.newInstance(
|
||||
this,
|
||||
R.string.importing_user_data
|
||||
) { progressCallback, _ ->
|
||||
val checkStream =
|
||||
ZipInputStream(BufferedInputStream(contentResolver.openInputStream(result)))
|
||||
var isYuzuBackup = false
|
||||
checkStream.use { stream ->
|
||||
var ze: ZipEntry? = null
|
||||
while (stream.nextEntry?.also { ze = it } != null) {
|
||||
val itemName = ze!!.name.trim()
|
||||
if (itemName == "/config/config.ini" || itemName == "config/config.ini") {
|
||||
isYuzuBackup = true
|
||||
return@use
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!isYuzuBackup) {
|
||||
return@newInstance MessageDialogFragment.newInstance(
|
||||
this,
|
||||
titleId = R.string.invalid_yuzu_backup,
|
||||
descriptionId = R.string.user_data_import_failed_description
|
||||
)
|
||||
}
|
||||
|
||||
// Clear existing user data
|
||||
NativeConfig.unloadGlobalConfig()
|
||||
File(DirectoryInitialization.userDirectory!!).deleteRecursively()
|
||||
|
||||
// Copy archive to internal storage
|
||||
try {
|
||||
FileUtil.unzipToInternalStorage(
|
||||
result.toString(),
|
||||
File(DirectoryInitialization.userDirectory!!),
|
||||
progressCallback
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
return@newInstance MessageDialogFragment.newInstance(
|
||||
this,
|
||||
titleId = R.string.import_failed,
|
||||
descriptionId = R.string.user_data_import_failed_description
|
||||
)
|
||||
}
|
||||
|
||||
// Reinitialize relevant data
|
||||
NativeLibrary.initializeSystem(true)
|
||||
NativeConfig.initializeGlobalConfig()
|
||||
gamesViewModel.reloadGames(false)
|
||||
driverViewModel.reloadDriverData()
|
||||
|
||||
return@newInstance getString(R.string.user_data_import_success)
|
||||
}.show(supportFragmentManager, ProgressDialogFragment.TAG)
|
||||
InstallableActions.importUserData(
|
||||
activity = this,
|
||||
fragmentManager = supportFragmentManager,
|
||||
gamesViewModel = gamesViewModel,
|
||||
driverViewModel = driverViewModel,
|
||||
result = result
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,327 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.utils
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import org.yuzu.yuzu_emu.NativeLibrary
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
|
||||
import org.yuzu.yuzu_emu.fragments.ProgressDialogFragment
|
||||
import org.yuzu.yuzu_emu.model.AddonViewModel
|
||||
import org.yuzu.yuzu_emu.model.DriverViewModel
|
||||
import org.yuzu.yuzu_emu.model.GamesViewModel
|
||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||
import org.yuzu.yuzu_emu.model.InstallResult
|
||||
import org.yuzu.yuzu_emu.model.TaskState
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.BufferedOutputStream
|
||||
import java.io.File
|
||||
import java.io.FilenameFilter
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipInputStream
|
||||
|
||||
object InstallableActions {
|
||||
fun processKey(
|
||||
activity: FragmentActivity,
|
||||
fragmentManager: FragmentManager,
|
||||
gamesViewModel: GamesViewModel,
|
||||
result: Uri,
|
||||
extension: String = "keys"
|
||||
) {
|
||||
activity.contentResolver.takePersistableUriPermission(
|
||||
result,
|
||||
Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
)
|
||||
|
||||
val resultCode = NativeLibrary.installKeys(result.toString(), extension)
|
||||
if (resultCode == 0) {
|
||||
Toast.makeText(
|
||||
activity.applicationContext,
|
||||
R.string.keys_install_success,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
gamesViewModel.reloadGames(true)
|
||||
return
|
||||
}
|
||||
|
||||
val resultString = activity.resources.getStringArray(R.array.installKeysResults)[resultCode]
|
||||
MessageDialogFragment.newInstance(
|
||||
titleId = R.string.keys_failed,
|
||||
descriptionString = resultString,
|
||||
helpLinkId = R.string.keys_missing_help
|
||||
).show(fragmentManager, MessageDialogFragment.TAG)
|
||||
}
|
||||
|
||||
fun processFirmware(
|
||||
activity: FragmentActivity,
|
||||
fragmentManager: FragmentManager,
|
||||
homeViewModel: HomeViewModel,
|
||||
result: Uri,
|
||||
onComplete: (() -> Unit)? = null
|
||||
) {
|
||||
val filterNCA = FilenameFilter { _, dirName -> dirName.endsWith(".nca") }
|
||||
val firmwarePath = File(NativeConfig.getNandDir() + "/system/Contents/registered/")
|
||||
val cacheFirmwareDir = File("${activity.cacheDir.path}/registered/")
|
||||
|
||||
ProgressDialogFragment.newInstance(
|
||||
activity,
|
||||
R.string.firmware_installing
|
||||
) { progressCallback, _ ->
|
||||
var messageToShow: Any
|
||||
try {
|
||||
FileUtil.unzipToInternalStorage(
|
||||
result.toString(),
|
||||
cacheFirmwareDir,
|
||||
progressCallback
|
||||
)
|
||||
val unfilteredNumOfFiles = cacheFirmwareDir.list()?.size ?: -1
|
||||
val filteredNumOfFiles = cacheFirmwareDir.list(filterNCA)?.size ?: -2
|
||||
messageToShow = if (unfilteredNumOfFiles != filteredNumOfFiles) {
|
||||
MessageDialogFragment.newInstance(
|
||||
activity,
|
||||
titleId = R.string.firmware_installed_failure,
|
||||
descriptionId = R.string.firmware_installed_failure_description
|
||||
)
|
||||
} else {
|
||||
firmwarePath.deleteRecursively()
|
||||
cacheFirmwareDir.copyRecursively(firmwarePath, overwrite = true)
|
||||
NativeLibrary.initializeSystem(true)
|
||||
homeViewModel.setCheckKeys(true)
|
||||
activity.getString(R.string.save_file_imported_success)
|
||||
}
|
||||
} catch (_: Exception) {
|
||||
messageToShow = activity.getString(R.string.fatal_error)
|
||||
} finally {
|
||||
cacheFirmwareDir.deleteRecursively()
|
||||
}
|
||||
messageToShow
|
||||
}.apply {
|
||||
onDialogComplete = onComplete
|
||||
}.show(fragmentManager, ProgressDialogFragment.TAG)
|
||||
}
|
||||
|
||||
fun uninstallFirmware(
|
||||
activity: FragmentActivity,
|
||||
fragmentManager: FragmentManager,
|
||||
homeViewModel: HomeViewModel
|
||||
) {
|
||||
val firmwarePath = File(NativeConfig.getNandDir() + "/system/Contents/registered/")
|
||||
ProgressDialogFragment.newInstance(
|
||||
activity,
|
||||
R.string.firmware_uninstalling
|
||||
) { _, _ ->
|
||||
val messageToShow: Any = try {
|
||||
if (firmwarePath.exists()) {
|
||||
firmwarePath.deleteRecursively()
|
||||
NativeLibrary.initializeSystem(true)
|
||||
homeViewModel.setCheckKeys(true)
|
||||
activity.getString(R.string.firmware_uninstalled_success)
|
||||
} else {
|
||||
activity.getString(R.string.firmware_uninstalled_failure)
|
||||
}
|
||||
} catch (_: Exception) {
|
||||
activity.getString(R.string.fatal_error)
|
||||
}
|
||||
messageToShow
|
||||
}.show(fragmentManager, ProgressDialogFragment.TAG)
|
||||
}
|
||||
|
||||
fun installContent(
|
||||
activity: FragmentActivity,
|
||||
fragmentManager: FragmentManager,
|
||||
addonViewModel: AddonViewModel,
|
||||
documents: List<Uri>
|
||||
) {
|
||||
ProgressDialogFragment.newInstance(
|
||||
activity,
|
||||
R.string.installing_game_content
|
||||
) { progressCallback, messageCallback ->
|
||||
var installSuccess = 0
|
||||
var installOverwrite = 0
|
||||
var errorBaseGame = 0
|
||||
var error = 0
|
||||
documents.forEach {
|
||||
messageCallback.invoke(FileUtil.getFilename(it))
|
||||
when (
|
||||
InstallResult.from(
|
||||
NativeLibrary.installFileToNand(
|
||||
it.toString(),
|
||||
progressCallback
|
||||
)
|
||||
)
|
||||
) {
|
||||
InstallResult.Success -> installSuccess += 1
|
||||
InstallResult.Overwrite -> installOverwrite += 1
|
||||
InstallResult.BaseInstallAttempted -> errorBaseGame += 1
|
||||
InstallResult.Failure -> error += 1
|
||||
}
|
||||
}
|
||||
|
||||
addonViewModel.refreshAddons(force = true)
|
||||
|
||||
val separator = System.lineSeparator() ?: "\n"
|
||||
val installResult = StringBuilder()
|
||||
if (installSuccess > 0) {
|
||||
installResult.append(
|
||||
activity.getString(
|
||||
R.string.install_game_content_success_install,
|
||||
installSuccess
|
||||
)
|
||||
)
|
||||
installResult.append(separator)
|
||||
}
|
||||
if (installOverwrite > 0) {
|
||||
installResult.append(
|
||||
activity.getString(
|
||||
R.string.install_game_content_success_overwrite,
|
||||
installOverwrite
|
||||
)
|
||||
)
|
||||
installResult.append(separator)
|
||||
}
|
||||
val errorTotal = errorBaseGame + error
|
||||
if (errorTotal > 0) {
|
||||
installResult.append(separator)
|
||||
installResult.append(
|
||||
activity.getString(
|
||||
R.string.install_game_content_failed_count,
|
||||
errorTotal
|
||||
)
|
||||
)
|
||||
installResult.append(separator)
|
||||
if (errorBaseGame > 0) {
|
||||
installResult.append(separator)
|
||||
installResult.append(activity.getString(R.string.install_game_content_failure_base))
|
||||
installResult.append(separator)
|
||||
}
|
||||
if (error > 0) {
|
||||
installResult.append(
|
||||
activity.getString(R.string.install_game_content_failure_description)
|
||||
)
|
||||
installResult.append(separator)
|
||||
}
|
||||
return@newInstance MessageDialogFragment.newInstance(
|
||||
activity,
|
||||
titleId = R.string.install_game_content_failure,
|
||||
descriptionString = installResult.toString().trim(),
|
||||
helpLinkId = R.string.install_game_content_help_link
|
||||
)
|
||||
} else {
|
||||
return@newInstance MessageDialogFragment.newInstance(
|
||||
activity,
|
||||
titleId = R.string.install_game_content_success,
|
||||
descriptionString = installResult.toString().trim()
|
||||
)
|
||||
}
|
||||
}.show(fragmentManager, ProgressDialogFragment.TAG)
|
||||
}
|
||||
|
||||
fun exportUserData(
|
||||
activity: FragmentActivity,
|
||||
fragmentManager: FragmentManager,
|
||||
result: Uri
|
||||
) {
|
||||
val userDirectory = DirectoryInitialization.userDirectory
|
||||
if (userDirectory == null) {
|
||||
Toast.makeText(
|
||||
activity.applicationContext,
|
||||
R.string.fatal_error,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
return
|
||||
}
|
||||
|
||||
ProgressDialogFragment.newInstance(
|
||||
activity,
|
||||
R.string.exporting_user_data,
|
||||
true
|
||||
) { progressCallback, _ ->
|
||||
val zipResult = FileUtil.zipFromInternalStorage(
|
||||
File(userDirectory),
|
||||
userDirectory,
|
||||
BufferedOutputStream(activity.contentResolver.openOutputStream(result)),
|
||||
progressCallback,
|
||||
compression = false
|
||||
)
|
||||
return@newInstance when (zipResult) {
|
||||
TaskState.Completed -> activity.getString(R.string.user_data_export_success)
|
||||
TaskState.Failed -> R.string.export_failed
|
||||
TaskState.Cancelled -> R.string.user_data_export_cancelled
|
||||
}
|
||||
}.show(fragmentManager, ProgressDialogFragment.TAG)
|
||||
}
|
||||
|
||||
fun importUserData(
|
||||
activity: FragmentActivity,
|
||||
fragmentManager: FragmentManager,
|
||||
gamesViewModel: GamesViewModel,
|
||||
driverViewModel: DriverViewModel,
|
||||
result: Uri
|
||||
) {
|
||||
val userDirectory = DirectoryInitialization.userDirectory
|
||||
if (userDirectory == null) {
|
||||
Toast.makeText(
|
||||
activity.applicationContext,
|
||||
R.string.fatal_error,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
return
|
||||
}
|
||||
|
||||
ProgressDialogFragment.newInstance(
|
||||
activity,
|
||||
R.string.importing_user_data
|
||||
) { progressCallback, _ ->
|
||||
val checkStream = ZipInputStream(
|
||||
BufferedInputStream(activity.contentResolver.openInputStream(result))
|
||||
)
|
||||
var isYuzuBackup = false
|
||||
checkStream.use { stream ->
|
||||
var ze: ZipEntry? = null
|
||||
while (stream.nextEntry?.also { ze = it } != null) {
|
||||
val itemName = ze!!.name.trim()
|
||||
if (itemName == "/config/config.ini" || itemName == "config/config.ini") {
|
||||
isYuzuBackup = true
|
||||
return@use
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!isYuzuBackup) {
|
||||
return@newInstance MessageDialogFragment.newInstance(
|
||||
activity,
|
||||
titleId = R.string.invalid_yuzu_backup,
|
||||
descriptionId = R.string.user_data_import_failed_description
|
||||
)
|
||||
}
|
||||
|
||||
NativeConfig.unloadGlobalConfig()
|
||||
File(userDirectory).deleteRecursively()
|
||||
|
||||
try {
|
||||
FileUtil.unzipToInternalStorage(
|
||||
result.toString(),
|
||||
File(userDirectory),
|
||||
progressCallback
|
||||
)
|
||||
} catch (_: Exception) {
|
||||
return@newInstance MessageDialogFragment.newInstance(
|
||||
activity,
|
||||
titleId = R.string.import_failed,
|
||||
descriptionId = R.string.user_data_import_failed_description
|
||||
)
|
||||
}
|
||||
|
||||
NativeLibrary.initializeSystem(true)
|
||||
NativeConfig.initializeGlobalConfig()
|
||||
gamesViewModel.reloadGames(false)
|
||||
driverViewModel.reloadDriverData()
|
||||
|
||||
return@newInstance activity.getString(R.string.user_data_import_success)
|
||||
}.show(fragmentManager, ProgressDialogFragment.TAG)
|
||||
}
|
||||
}
|
||||
|
|
@ -10,12 +10,11 @@
|
|||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appbar_about"
|
||||
style="@style/Widget.Eden.TransparentTopAppBarLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true"
|
||||
android:touchscreenBlocksFocus="false"
|
||||
android:background="@android:color/transparent"
|
||||
app:elevation="0dp">
|
||||
android:touchscreenBlocksFocus="false">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar_about"
|
||||
|
|
@ -41,15 +40,41 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal"
|
||||
android:padding="24dp">
|
||||
android:paddingBottom="24dp"
|
||||
android:paddingStart="24dp"
|
||||
android:paddingTop="0dp"
|
||||
android:paddingEnd="24dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_logo"
|
||||
android:layout_width="200dp"
|
||||
android:layout_height="200dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:src="@drawable/ic_yuzu" />
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_logo"
|
||||
android:layout_width="200dp"
|
||||
android:layout_height="200dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:src="@drawable/ic_yuzu" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
style="@style/TextAppearance.Material3.TitleMedium"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/app_name"
|
||||
android:textAlignment="center" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
style="@style/TextAppearance.Material3.BodyMedium"
|
||||
android:layout_width="220dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="@string/about_app_description"
|
||||
android:textAlignment="center" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
|
|
@ -57,39 +82,6 @@
|
|||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:cardCornerRadius="16dp"
|
||||
app:cardBackgroundColor="?attr/colorSurface"
|
||||
app:strokeColor="?attr/colorOutline"
|
||||
app:strokeWidth="1dp"
|
||||
app:cardElevation="0dp">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="24dp"
|
||||
android:paddingVertical="20dp">
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
style="@style/TextAppearance.Material3.TitleMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/about"
|
||||
android:textAlignment="viewStart" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
style="@style/TextAppearance.Material3.BodyMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="6dp"
|
||||
android:text="@string/about_app_description"
|
||||
android:textAlignment="viewStart" />
|
||||
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/button_contributors"
|
||||
android:layout_width="match_parent"
|
||||
|
|
@ -205,7 +197,7 @@
|
|||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:gravity="start"
|
||||
android:orientation="horizontal">
|
||||
|
||||
|
|
|
|||
|
|
@ -10,12 +10,11 @@
|
|||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appbar_about"
|
||||
style="@style/Widget.Eden.TransparentTopAppBarLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true"
|
||||
android:touchscreenBlocksFocus="false"
|
||||
android:background="@android:color/transparent"
|
||||
app:elevation="0dp">
|
||||
android:touchscreenBlocksFocus="false">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar_about"
|
||||
|
|
@ -43,48 +42,35 @@
|
|||
android:orientation="vertical"
|
||||
android:paddingBottom="24dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_logo"
|
||||
android:layout_width="160dp"
|
||||
android:layout_height="160dp"
|
||||
android:layout_marginVertical="24dp"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:src="@drawable/ic_yuzu" />
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
app:cardBackgroundColor="@android:color/transparent"
|
||||
app:cardCornerRadius="16dp"
|
||||
app:cardElevation="0dp"
|
||||
app:strokeColor="?attr/colorOutline"
|
||||
app:strokeWidth="1dp">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center_horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_logo"
|
||||
android:layout_width="160dp"
|
||||
android:layout_height="160dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:src="@drawable/ic_yuzu" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
style="@style/TextAppearance.Material3.TitleMedium"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingVertical="20dp"
|
||||
android:paddingHorizontal="20dp"
|
||||
android:orientation="vertical">
|
||||
android:text="@string/app_name"
|
||||
android:textAlignment="center" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
style="@style/TextAppearance.Material3.TitleMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAlignment="viewStart"
|
||||
android:text="@string/about" />
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
style="@style/SynthwaveText.Body"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textAlignment="center"
|
||||
android:text="@string/about_app_description" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
style="@style/SynthwaveText.Body"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="6dp"
|
||||
android:textAlignment="viewStart"
|
||||
android:text="@string/about_app_description" />
|
||||
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/button_contributors"
|
||||
|
|
@ -206,7 +192,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_horizontal"
|
||||
android:layout_marginTop="24dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:layout_marginHorizontal="40dp">
|
||||
|
||||
|
|
@ -220,7 +206,9 @@
|
|||
app:icon="@drawable/ic_discord"
|
||||
app:iconSize="24dp"
|
||||
app:iconGravity="textStart"
|
||||
app:iconPadding="0dp" />
|
||||
app:iconPadding="0dp"
|
||||
app:strokeColor="?attr/colorOutline"
|
||||
app:strokeWidth="1dp" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
style="@style/EdenButton.Secondary"
|
||||
|
|
@ -232,7 +220,9 @@
|
|||
app:icon="@drawable/ic_stoat"
|
||||
app:iconSize="24dp"
|
||||
app:iconGravity="textStart"
|
||||
app:iconPadding="0dp" />
|
||||
app:iconPadding="0dp"
|
||||
app:strokeColor="?attr/colorOutline"
|
||||
app:strokeWidth="1dp" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
style="@style/EdenButton.Secondary"
|
||||
|
|
@ -244,7 +234,9 @@
|
|||
app:icon="@drawable/ic_x"
|
||||
app:iconSize="24dp"
|
||||
app:iconGravity="textStart"
|
||||
app:iconPadding="0dp" />
|
||||
app:iconPadding="0dp"
|
||||
app:strokeColor="?attr/colorOutline"
|
||||
app:strokeWidth="1dp" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
style="@style/EdenButton.Secondary"
|
||||
|
|
@ -256,7 +248,9 @@
|
|||
app:icon="@drawable/ic_website"
|
||||
app:iconSize="24dp"
|
||||
app:iconGravity="textStart"
|
||||
app:iconPadding="0dp" />
|
||||
app:iconPadding="0dp"
|
||||
app:strokeColor="?attr/colorOutline"
|
||||
app:strokeWidth="1dp" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_github"
|
||||
|
|
@ -268,7 +262,9 @@
|
|||
app:icon="@drawable/ic_github"
|
||||
app:iconSize="24dp"
|
||||
app:iconGravity="textStart"
|
||||
app:iconPadding="0dp" />
|
||||
app:iconPadding="0dp"
|
||||
app:strokeColor="?attr/colorOutline"
|
||||
app:strokeWidth="1dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
|
|
|||
|
|
@ -40,10 +40,6 @@
|
|||
|
||||
<action
|
||||
android:id="@+id/action_global_settingsActivity"
|
||||
app:destination="@id/settingsActivity"
|
||||
app:enterAnim="@anim/nav_default_enter_anim"
|
||||
app:exitAnim="@anim/nav_default_exit_anim"
|
||||
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
|
||||
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
|
||||
app:destination="@id/settingsActivity" />
|
||||
|
||||
</navigation>
|
||||
|
|
|
|||
|
|
@ -20,26 +20,7 @@
|
|||
<fragment
|
||||
android:id="@+id/homeSettingsFragment"
|
||||
android:name="org.yuzu.yuzu_emu.fragments.HomeSettingsFragment"
|
||||
android:label="HomeSettingsFragment" >
|
||||
<action
|
||||
android:id="@+id/action_homeSettingsFragment_to_aboutFragment"
|
||||
app:destination="@id/aboutFragment" />
|
||||
<action
|
||||
android:id="@+id/action_homeSettingsFragment_to_installableFragment"
|
||||
app:destination="@id/installableFragment" />
|
||||
<action
|
||||
android:id="@+id/action_homeSettingsFragment_to_driverManagerFragment"
|
||||
app:destination="@id/driverManagerFragment" />
|
||||
<action
|
||||
android:id="@+id/action_homeSettingsFragment_to_appletLauncherFragment"
|
||||
app:destination="@id/appletLauncherFragment" />
|
||||
<action
|
||||
android:id="@+id/action_homeSettingsFragment_to_gameFoldersFragment"
|
||||
app:destination="@id/gameFoldersFragment" />
|
||||
<action
|
||||
android:id="@+id/action_homeSettingsFragment_to_profileManagerFragment"
|
||||
app:destination="@id/profileManagerFragment" />
|
||||
</fragment>
|
||||
android:label="HomeSettingsFragment" />
|
||||
|
||||
<fragment
|
||||
android:id="@+id/firstTimeSetupFragment"
|
||||
|
|
@ -55,11 +36,7 @@
|
|||
<fragment
|
||||
android:id="@+id/aboutFragment"
|
||||
android:name="org.yuzu.yuzu_emu.fragments.AboutFragment"
|
||||
android:label="AboutFragment" >
|
||||
<action
|
||||
android:id="@+id/action_aboutFragment_to_licensesFragment"
|
||||
app:destination="@id/licensesFragment" />
|
||||
</fragment>
|
||||
android:label="AboutFragment" />
|
||||
|
||||
<fragment
|
||||
android:id="@+id/licensesFragment"
|
||||
|
|
@ -101,11 +78,23 @@
|
|||
|
||||
<action
|
||||
android:id="@+id/action_global_settingsActivity"
|
||||
app:destination="@id/settingsActivity"
|
||||
app:enterAnim="@anim/nav_default_enter_anim"
|
||||
app:exitAnim="@anim/nav_default_exit_anim"
|
||||
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
|
||||
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
|
||||
app:destination="@id/settingsActivity" />
|
||||
<activity
|
||||
android:id="@+id/settingsSubscreenActivity"
|
||||
android:name="org.yuzu.yuzu_emu.features.settings.ui.SettingsSubscreenActivity"
|
||||
android:label="SettingsSubscreenActivity">
|
||||
<argument
|
||||
android:name="destination"
|
||||
app:argType="org.yuzu.yuzu_emu.features.settings.ui.SettingsSubscreen" />
|
||||
<argument
|
||||
android:name="game"
|
||||
app:argType="org.yuzu.yuzu_emu.model.Game"
|
||||
app:nullable="true"
|
||||
android:defaultValue="@null" />
|
||||
</activity>
|
||||
<action
|
||||
android:id="@+id/action_global_settingsSubscreenActivity"
|
||||
app:destination="@id/settingsSubscreenActivity" />
|
||||
<fragment
|
||||
android:id="@+id/installableFragment"
|
||||
android:name="org.yuzu.yuzu_emu.fragments.InstallableFragment"
|
||||
|
|
@ -119,9 +108,6 @@
|
|||
app:argType="org.yuzu.yuzu_emu.model.Game"
|
||||
app:nullable="true"
|
||||
android:defaultValue="@null" />
|
||||
<action
|
||||
android:id="@+id/action_driverManagerFragment_to_driverFetcherFragment"
|
||||
app:destination="@id/driverFetcherFragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/appletLauncherFragment"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,139 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/settings_subscreen_navigation"
|
||||
app:startDestination="@id/profileManagerFragment">
|
||||
|
||||
<fragment
|
||||
android:id="@+id/profileManagerFragment"
|
||||
android:name="org.yuzu.yuzu_emu.fragments.ProfileManagerFragment"
|
||||
android:label="ProfileManagerFragment">
|
||||
<action
|
||||
android:id="@+id/action_profileManagerFragment_to_newUserDialog"
|
||||
app:destination="@id/newUserDialogFragment" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/newUserDialogFragment"
|
||||
android:name="org.yuzu.yuzu_emu.fragments.EditUserDialogFragment"
|
||||
android:label="NewUserDialogFragment" />
|
||||
|
||||
<fragment
|
||||
android:id="@+id/driverManagerFragment"
|
||||
android:name="org.yuzu.yuzu_emu.fragments.DriverManagerFragment"
|
||||
android:label="DriverManagerFragment">
|
||||
<argument
|
||||
android:name="game"
|
||||
app:argType="org.yuzu.yuzu_emu.model.Game"
|
||||
app:nullable="true"
|
||||
android:defaultValue="@null" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/driverFetcherFragment"
|
||||
android:name="org.yuzu.yuzu_emu.fragments.DriverFetcherFragment"
|
||||
android:label="fragment_driver_fetcher"
|
||||
tools:layout="@layout/fragment_driver_fetcher" />
|
||||
|
||||
<fragment
|
||||
android:id="@+id/freedrenoSettingsFragment"
|
||||
android:name="org.yuzu.yuzu_emu.fragments.FreedrenoSettingsFragment"
|
||||
android:label="@string/freedreno_settings_title">
|
||||
<argument
|
||||
android:name="game"
|
||||
app:argType="org.yuzu.yuzu_emu.model.Game"
|
||||
app:nullable="true"
|
||||
android:defaultValue="@null" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/appletLauncherFragment"
|
||||
android:name="org.yuzu.yuzu_emu.fragments.AppletLauncherFragment"
|
||||
android:label="AppletLauncherFragment">
|
||||
<action
|
||||
android:id="@+id/action_appletLauncherFragment_to_cabinetLauncherDialogFragment"
|
||||
app:destination="@id/cabinetLauncherDialogFragment" />
|
||||
</fragment>
|
||||
|
||||
<dialog
|
||||
android:id="@+id/cabinetLauncherDialogFragment"
|
||||
android:name="org.yuzu.yuzu_emu.fragments.CabinetLauncherDialogFragment"
|
||||
android:label="CabinetLauncherDialogFragment" />
|
||||
|
||||
<fragment
|
||||
android:id="@+id/aboutFragment"
|
||||
android:name="org.yuzu.yuzu_emu.fragments.AboutFragment"
|
||||
android:label="AboutFragment" />
|
||||
|
||||
<fragment
|
||||
android:id="@+id/licensesFragment"
|
||||
android:name="org.yuzu.yuzu_emu.fragments.LicensesFragment"
|
||||
android:label="LicensesFragment" />
|
||||
|
||||
<fragment
|
||||
android:id="@+id/gameInfoFragment"
|
||||
android:name="org.yuzu.yuzu_emu.fragments.GameInfoFragment"
|
||||
android:label="GameInfoFragment">
|
||||
<argument
|
||||
android:name="game"
|
||||
app:argType="org.yuzu.yuzu_emu.model.Game" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/addonsFragment"
|
||||
android:name="org.yuzu.yuzu_emu.fragments.AddonsFragment"
|
||||
android:label="AddonsFragment">
|
||||
<argument
|
||||
android:name="game"
|
||||
app:argType="org.yuzu.yuzu_emu.model.Game" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/installableFragment"
|
||||
android:name="org.yuzu.yuzu_emu.fragments.InstallableFragment"
|
||||
android:label="InstallableFragment" />
|
||||
|
||||
<fragment
|
||||
android:id="@+id/gameFoldersFragment"
|
||||
android:name="org.yuzu.yuzu_emu.fragments.GameFoldersFragment"
|
||||
android:label="GameFoldersFragment" />
|
||||
|
||||
<activity
|
||||
android:id="@+id/emulationActivity"
|
||||
android:name="org.yuzu.yuzu_emu.activities.EmulationActivity"
|
||||
android:label="EmulationActivity">
|
||||
<argument
|
||||
android:name="game"
|
||||
app:argType="org.yuzu.yuzu_emu.model.Game"
|
||||
app:nullable="true"
|
||||
android:defaultValue="@null" />
|
||||
<argument
|
||||
android:name="custom"
|
||||
app:argType="boolean"
|
||||
android:defaultValue="false" />
|
||||
</activity>
|
||||
|
||||
<action
|
||||
android:id="@+id/action_global_emulationActivity"
|
||||
app:destination="@id/emulationActivity"
|
||||
app:launchSingleTop="true" />
|
||||
|
||||
<activity
|
||||
android:id="@+id/settingsSubscreenActivity"
|
||||
android:name="org.yuzu.yuzu_emu.features.settings.ui.SettingsSubscreenActivity"
|
||||
android:label="SettingsSubscreenActivity">
|
||||
<argument
|
||||
android:name="destination"
|
||||
app:argType="org.yuzu.yuzu_emu.features.settings.ui.SettingsSubscreen" />
|
||||
<argument
|
||||
android:name="game"
|
||||
app:argType="org.yuzu.yuzu_emu.model.Game"
|
||||
app:nullable="true"
|
||||
android:defaultValue="@null" />
|
||||
</activity>
|
||||
|
||||
<action
|
||||
android:id="@+id/action_global_settingsSubscreenActivity"
|
||||
app:destination="@id/settingsSubscreenActivity" />
|
||||
</navigation>
|
||||
|
|
@ -400,7 +400,7 @@
|
|||
<string name="copied_to_clipboard">Copied to clipboard</string>
|
||||
<string name="about_app_description">An open-source Switch emulator</string>
|
||||
<string name="contributors">Contributors</string>
|
||||
<string name="contributors_description">Contributors who made Eden for Android possible</string>
|
||||
<string name="contributors_description">People who made Eden for Android possible</string>
|
||||
<string name="contributors_link" translatable="false">https://git.eden-emu.dev/eden-emu/eden/activity/contributors</string>
|
||||
<string name="licenses_description">Projects that make Eden for Android possible</string>
|
||||
<string name="build">Build</string>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue