mirror of
https://git.eden-emu.dev/eden-emu/eden
synced 2026-05-25 21:57:05 +02:00
[android,ui] chore: settings subscreens transition and other minor conformances (#3699)
- Fix black screen in transition animations - Adjustments to about fragment made about text more label and less button like, header transparency, spacing adjustments, word Contributors replaced by People in Contributors field for de-duplication. - installable actions code de-duplication Extracted install/update/import firmware/user data flows into InstallableActions.kt and reused it from MainActivity and InstallableFragment, reducing duplicated logic, ensuring single source of truth. Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3699 Reviewed-by: DraVee <chimera@dravee.dev> Reviewed-by: Lizzie <lizzie@eden-emu.dev> Co-authored-by: xbzk <xbzk@eden-emu.dev> Co-committed-by: xbzk <xbzk@eden-emu.dev>
This commit is contained in:
parent
0dad29698e
commit
2896fa3835
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:theme="@style/Theme.Yuzu.Main"
|
||||||
android:label="@string/preferences_settings"/>
|
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
|
<activity
|
||||||
android:name="org.yuzu.yuzu_emu.activities.EmulationActivity"
|
android:name="org.yuzu.yuzu_emu.activities.EmulationActivity"
|
||||||
android:theme="@style/Theme.Yuzu.Main"
|
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-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// 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.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.core.os.bundleOf
|
||||||
import androidx.core.content.res.ResourcesCompat
|
import androidx.core.content.res.ResourcesCompat
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import androidx.navigation.findNavController
|
import androidx.navigation.findNavController
|
||||||
import org.yuzu.yuzu_emu.HomeNavigationDirections
|
|
||||||
import org.yuzu.yuzu_emu.NativeLibrary
|
import org.yuzu.yuzu_emu.NativeLibrary
|
||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
import org.yuzu.yuzu_emu.YuzuApplication
|
import org.yuzu.yuzu_emu.YuzuApplication
|
||||||
|
|
@ -67,8 +70,13 @@ class AppletAdapter(val activity: FragmentActivity, applets: List<Applet>) :
|
||||||
title = YuzuApplication.appContext.getString(applet.titleId),
|
title = YuzuApplication.appContext.getString(applet.titleId),
|
||||||
path = appletPath
|
path = appletPath
|
||||||
)
|
)
|
||||||
val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame)
|
binding.root.findNavController().navigate(
|
||||||
binding.root.findNavController().navigate(action)
|
R.id.action_global_emulationActivity,
|
||||||
|
bundleOf(
|
||||||
|
"game" to appletGame,
|
||||||
|
"custom" to false
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -111,18 +111,10 @@ class SettingsActivity : AppCompatActivity() {
|
||||||
if (navHostFragment.childFragmentManager.backStackEntryCount > 0) {
|
if (navHostFragment.childFragmentManager.backStackEntryCount > 0) {
|
||||||
navHostFragment.navController.popBackStack()
|
navHostFragment.navController.popBackStack()
|
||||||
} else {
|
} 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() {
|
override fun onStart() {
|
||||||
super.onStart()
|
super.onStart()
|
||||||
if (!DirectoryInitialization.areDirectoriesReady) {
|
if (!DirectoryInitialization.areDirectoriesReady) {
|
||||||
|
|
@ -178,7 +170,7 @@ class SettingsActivity : AppCompatActivity() {
|
||||||
getString(R.string.settings_reset),
|
getString(R.string.settings_reset),
|
||||||
Toast.LENGTH_LONG
|
Toast.LENGTH_LONG
|
||||||
).show()
|
).show()
|
||||||
finishWithFragmentLikeAnimation()
|
finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setInsets() {
|
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.fragment.app.activityViewModels
|
||||||
import androidx.navigation.findNavController
|
import androidx.navigation.findNavController
|
||||||
import com.google.android.material.transition.MaterialSharedAxis
|
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.R
|
||||||
import org.yuzu.yuzu_emu.databinding.FragmentAboutBinding
|
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.model.HomeViewModel
|
||||||
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
|
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
|
||||||
import org.yuzu.yuzu_emu.NativeLibrary
|
import org.yuzu.yuzu_emu.NativeLibrary
|
||||||
|
|
@ -54,7 +55,7 @@ class AboutFragment : Fragment() {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
homeViewModel.setStatusBarShadeVisibility(visible = false)
|
homeViewModel.setStatusBarShadeVisibility(visible = false)
|
||||||
binding.toolbarAbout.setNavigationOnClickListener {
|
binding.toolbarAbout.setNavigationOnClickListener {
|
||||||
binding.root.findNavController().popBackStack()
|
requireActivity().onBackPressedDispatcher.onBackPressed()
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.imageLogo.setOnLongClickListener {
|
binding.imageLogo.setOnLongClickListener {
|
||||||
|
|
@ -72,8 +73,11 @@ class AboutFragment : Fragment() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
binding.buttonLicenses.setOnClickListener {
|
binding.buttonLicenses.setOnClickListener {
|
||||||
exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
|
val action = HomeNavigationDirections.actionGlobalSettingsSubscreenActivity(
|
||||||
binding.root.findNavController().navigate(R.id.action_aboutFragment_to_licensesFragment)
|
SettingsSubscreen.LICENSES,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
binding.root.findNavController().navigate(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
val buildName = getString(R.string.app_name_suffixed)
|
val buildName = getString(R.string.app_name_suffixed)
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ import androidx.core.view.updatePadding
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.navigation.findNavController
|
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.google.android.material.transition.MaterialSharedAxis
|
import com.google.android.material.transition.MaterialSharedAxis
|
||||||
|
|
@ -61,7 +60,7 @@ class AddonsFragment : Fragment() {
|
||||||
homeViewModel.setStatusBarShadeVisibility(false)
|
homeViewModel.setStatusBarShadeVisibility(false)
|
||||||
|
|
||||||
binding.toolbarAddons.setNavigationOnClickListener {
|
binding.toolbarAddons.setNavigationOnClickListener {
|
||||||
binding.root.findNavController().popBackStack()
|
requireActivity().onBackPressedDispatcher.onBackPressed()
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.toolbarAddons.title = getString(R.string.addons_game, args.game.title)
|
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
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
package org.yuzu.yuzu_emu.fragments
|
package org.yuzu.yuzu_emu.fragments
|
||||||
|
|
@ -12,7 +12,6 @@ import androidx.core.view.WindowInsetsCompat
|
||||||
import androidx.core.view.updatePadding
|
import androidx.core.view.updatePadding
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.navigation.findNavController
|
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import com.google.android.material.transition.MaterialSharedAxis
|
import com.google.android.material.transition.MaterialSharedAxis
|
||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
|
|
@ -50,7 +49,7 @@ class AppletLauncherFragment : Fragment() {
|
||||||
homeViewModel.setStatusBarShadeVisibility(visible = false)
|
homeViewModel.setStatusBarShadeVisibility(visible = false)
|
||||||
|
|
||||||
binding.toolbarApplets.setNavigationOnClickListener {
|
binding.toolbarApplets.setNavigationOnClickListener {
|
||||||
binding.root.findNavController().popBackStack()
|
requireActivity().onBackPressedDispatcher.onBackPressed()
|
||||||
}
|
}
|
||||||
|
|
||||||
val applets = listOf(
|
val applets = listOf(
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ import androidx.core.view.WindowInsetsCompat
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.core.view.updatePadding
|
import androidx.core.view.updatePadding
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.navigation.findNavController
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.fasterxml.jackson.databind.JsonNode
|
import com.fasterxml.jackson.databind.JsonNode
|
||||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||||
|
|
@ -142,7 +141,7 @@ class DriverFetcherFragment : Fragment() {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
homeViewModel.setStatusBarShadeVisibility(visible = false)
|
homeViewModel.setStatusBarShadeVisibility(visible = false)
|
||||||
binding.toolbarDrivers.setNavigationOnClickListener {
|
binding.toolbarDrivers.setNavigationOnClickListener {
|
||||||
binding.root.findNavController().popBackStack()
|
requireActivity().onBackPressedDispatcher.onBackPressed()
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.listDrivers.layoutManager = LinearLayoutManager(context)
|
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
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
package org.yuzu.yuzu_emu.fragments
|
package org.yuzu.yuzu_emu.fragments
|
||||||
|
|
@ -19,6 +19,7 @@ import androidx.navigation.fragment.navArgs
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import com.google.android.material.transition.MaterialSharedAxis
|
import com.google.android.material.transition.MaterialSharedAxis
|
||||||
|
import org.yuzu.yuzu_emu.HomeNavigationDirections
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
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.databinding.FragmentDriverManagerBinding
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
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.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.Driver.Companion.toDriver
|
||||||
import org.yuzu.yuzu_emu.model.DriverViewModel
|
import org.yuzu.yuzu_emu.model.DriverViewModel
|
||||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||||
|
|
@ -105,7 +107,7 @@ class DriverManagerFragment : Fragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.toolbarDrivers.setNavigationOnClickListener {
|
binding.toolbarDrivers.setNavigationOnClickListener {
|
||||||
binding.root.findNavController().popBackStack()
|
requireActivity().onBackPressedDispatcher.onBackPressed()
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.buttonInstall.setOnClickListener {
|
binding.buttonInstall.setOnClickListener {
|
||||||
|
|
@ -113,9 +115,11 @@ class DriverManagerFragment : Fragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.buttonFetch.setOnClickListener {
|
binding.buttonFetch.setOnClickListener {
|
||||||
binding.root.findNavController().navigate(
|
val action = HomeNavigationDirections.actionGlobalSettingsSubscreenActivity(
|
||||||
R.id.action_driverManagerFragment_to_driverFetcherFragment
|
SettingsSubscreen.DRIVER_FETCHER,
|
||||||
|
null
|
||||||
)
|
)
|
||||||
|
binding.root.findNavController().navigate(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.listDrivers.apply {
|
binding.listDrivers.apply {
|
||||||
|
|
|
||||||
|
|
@ -4,20 +4,21 @@
|
||||||
package org.yuzu.yuzu_emu.fragments
|
package org.yuzu.yuzu_emu.fragments
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.core.view.WindowInsetsCompat
|
||||||
import androidx.core.view.updatePadding
|
import androidx.core.view.updatePadding
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.navigation.findNavController
|
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.google.android.material.transition.MaterialSharedAxis
|
import com.google.android.material.transition.MaterialSharedAxis
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
import org.yuzu.yuzu_emu.adapters.FolderAdapter
|
import org.yuzu.yuzu_emu.adapters.FolderAdapter
|
||||||
import org.yuzu.yuzu_emu.databinding.FragmentFoldersBinding
|
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.GameDir
|
||||||
import org.yuzu.yuzu_emu.model.GamesViewModel
|
import org.yuzu.yuzu_emu.model.GamesViewModel
|
||||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||||
import org.yuzu.yuzu_emu.ui.main.MainActivity
|
|
||||||
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
|
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
|
||||||
import org.yuzu.yuzu_emu.utils.collect
|
import org.yuzu.yuzu_emu.utils.collect
|
||||||
|
|
||||||
|
|
@ -36,6 +36,20 @@ class GameFoldersFragment : Fragment() {
|
||||||
private val homeViewModel: HomeViewModel by activityViewModels()
|
private val homeViewModel: HomeViewModel by activityViewModels()
|
||||||
private val gamesViewModel: GamesViewModel 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?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
|
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
|
||||||
|
|
@ -59,7 +73,7 @@ class GameFoldersFragment : Fragment() {
|
||||||
homeViewModel.setStatusBarShadeVisibility(visible = false)
|
homeViewModel.setStatusBarShadeVisibility(visible = false)
|
||||||
|
|
||||||
binding.toolbarFolders.setNavigationOnClickListener {
|
binding.toolbarFolders.setNavigationOnClickListener {
|
||||||
binding.root.findNavController().popBackStack()
|
requireActivity().onBackPressedDispatcher.onBackPressed()
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.listFolders.apply {
|
binding.listFolders.apply {
|
||||||
|
|
@ -74,7 +88,6 @@ class GameFoldersFragment : Fragment() {
|
||||||
(binding.listFolders.adapter as FolderAdapter).submitList(it)
|
(binding.listFolders.adapter as FolderAdapter).submitList(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
val mainActivity = requireActivity() as MainActivity
|
|
||||||
binding.buttonAdd.setOnClickListener {
|
binding.buttonAdd.setOnClickListener {
|
||||||
// Show a model to choose between Game and External Content
|
// Show a model to choose between Game and External Content
|
||||||
val options = arrayOf(
|
val options = arrayOf(
|
||||||
|
|
@ -87,10 +100,10 @@ class GameFoldersFragment : Fragment() {
|
||||||
.setItems(options) { _, which ->
|
.setItems(options) { _, which ->
|
||||||
when (which) {
|
when (which) {
|
||||||
0 -> { // Game Folder
|
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
|
1 -> { // External Content Folder
|
||||||
mainActivity.getExternalContentDirectory.launch(null)
|
getExternalContentDirectory.launch(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -105,6 +118,50 @@ class GameFoldersFragment : Fragment() {
|
||||||
gamesViewModel.onCloseGameFoldersFragment()
|
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() =
|
private fun setInsets() =
|
||||||
ViewCompat.setOnApplyWindowInsetsListener(
|
ViewCompat.setOnApplyWindowInsetsListener(
|
||||||
binding.root
|
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
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
package org.yuzu.yuzu_emu.fragments
|
package org.yuzu.yuzu_emu.fragments
|
||||||
|
|
@ -18,7 +18,6 @@ import androidx.core.view.WindowInsetsCompat
|
||||||
import androidx.core.view.updatePadding
|
import androidx.core.view.updatePadding
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.navigation.findNavController
|
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
import com.google.android.material.transition.MaterialSharedAxis
|
import com.google.android.material.transition.MaterialSharedAxis
|
||||||
import org.yuzu.yuzu_emu.NativeLibrary
|
import org.yuzu.yuzu_emu.NativeLibrary
|
||||||
|
|
@ -64,7 +63,7 @@ class GameInfoFragment : Fragment() {
|
||||||
binding.apply {
|
binding.apply {
|
||||||
toolbarInfo.title = args.game.title
|
toolbarInfo.title = args.game.title
|
||||||
toolbarInfo.setNavigationOnClickListener {
|
toolbarInfo.setNavigationOnClickListener {
|
||||||
view.findNavController().popBackStack()
|
requireActivity().onBackPressedDispatcher.onBackPressed()
|
||||||
}
|
}
|
||||||
|
|
||||||
val pathString = Uri.parse(args.game.path).path ?: ""
|
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.databinding.FragmentGamePropertiesBinding
|
||||||
import org.yuzu.yuzu_emu.features.DocumentProvider
|
import org.yuzu.yuzu_emu.features.DocumentProvider
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
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.DriverViewModel
|
||||||
import org.yuzu.yuzu_emu.model.GameProperty
|
import org.yuzu.yuzu_emu.model.GameProperty
|
||||||
import org.yuzu.yuzu_emu.model.GamesViewModel
|
import org.yuzu.yuzu_emu.model.GamesViewModel
|
||||||
|
|
@ -250,8 +251,10 @@ class GamePropertiesFragment : Fragment() {
|
||||||
R.string.info_description,
|
R.string.info_description,
|
||||||
R.drawable.ic_info_outline,
|
R.drawable.ic_info_outline,
|
||||||
action = {
|
action = {
|
||||||
val action = GamePropertiesFragmentDirections
|
val action = HomeNavigationDirections.actionGlobalSettingsSubscreenActivity(
|
||||||
.actionPerGamePropertiesFragmentToGameInfoFragment(args.game)
|
SettingsSubscreen.GAME_INFO,
|
||||||
|
args.game
|
||||||
|
)
|
||||||
binding.root.findNavController().navigate(action)
|
binding.root.findNavController().navigate(action)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -317,8 +320,11 @@ class GamePropertiesFragment : Fragment() {
|
||||||
R.string.add_ons_description,
|
R.string.add_ons_description,
|
||||||
R.drawable.ic_edit,
|
R.drawable.ic_edit,
|
||||||
action = {
|
action = {
|
||||||
val action = GamePropertiesFragmentDirections
|
val action =
|
||||||
.actionPerGamePropertiesFragmentToAddonsFragment(args.game)
|
HomeNavigationDirections.actionGlobalSettingsSubscreenActivity(
|
||||||
|
SettingsSubscreen.ADDONS,
|
||||||
|
args.game
|
||||||
|
)
|
||||||
binding.root.findNavController().navigate(action)
|
binding.root.findNavController().navigate(action)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -333,8 +339,11 @@ class GamePropertiesFragment : Fragment() {
|
||||||
R.drawable.ic_build,
|
R.drawable.ic_build,
|
||||||
detailsFlow = driverViewModel.selectedDriverTitle,
|
detailsFlow = driverViewModel.selectedDriverTitle,
|
||||||
action = {
|
action = {
|
||||||
val action = GamePropertiesFragmentDirections
|
val action =
|
||||||
.actionPerGamePropertiesFragmentToDriverManagerFragment(args.game)
|
HomeNavigationDirections.actionGlobalSettingsSubscreenActivity(
|
||||||
|
SettingsSubscreen.DRIVER_MANAGER,
|
||||||
|
args.game
|
||||||
|
)
|
||||||
binding.root.findNavController().navigate(action)
|
binding.root.findNavController().navigate(action)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -347,8 +356,11 @@ class GamePropertiesFragment : Fragment() {
|
||||||
R.string.freedreno_per_game_description,
|
R.string.freedreno_per_game_description,
|
||||||
R.drawable.ic_graphics,
|
R.drawable.ic_graphics,
|
||||||
action = {
|
action = {
|
||||||
val action = GamePropertiesFragmentDirections
|
val action =
|
||||||
.actionPerGamePropertiesFragmentToFreedrenoSettingsFragment(args.game)
|
HomeNavigationDirections.actionGlobalSettingsSubscreenActivity(
|
||||||
|
SettingsSubscreen.FREEDRENO_SETTINGS,
|
||||||
|
args.game
|
||||||
|
)
|
||||||
binding.root.findNavController().navigate(action)
|
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.DocumentProvider
|
||||||
import org.yuzu.yuzu_emu.features.fetcher.SpacingItemDecoration
|
import org.yuzu.yuzu_emu.features.fetcher.SpacingItemDecoration
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
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.DriverViewModel
|
||||||
import org.yuzu.yuzu_emu.model.HomeSetting
|
import org.yuzu.yuzu_emu.model.HomeSetting
|
||||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||||
|
|
@ -126,8 +127,11 @@ class HomeSettingsFragment : Fragment() {
|
||||||
R.string.profile_manager_description,
|
R.string.profile_manager_description,
|
||||||
R.drawable.ic_account_circle,
|
R.drawable.ic_account_circle,
|
||||||
{
|
{
|
||||||
binding.root.findNavController()
|
val action = HomeNavigationDirections.actionGlobalSettingsSubscreenActivity(
|
||||||
.navigate(R.id.action_homeSettingsFragment_to_profileManagerFragment)
|
SettingsSubscreen.PROFILE_MANAGER,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
binding.root.findNavController().navigate(action)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
@ -137,8 +141,10 @@ class HomeSettingsFragment : Fragment() {
|
||||||
R.string.install_gpu_driver_description,
|
R.string.install_gpu_driver_description,
|
||||||
R.drawable.ic_build,
|
R.drawable.ic_build,
|
||||||
{
|
{
|
||||||
val action = HomeSettingsFragmentDirections
|
val action = HomeNavigationDirections.actionGlobalSettingsSubscreenActivity(
|
||||||
.actionHomeSettingsFragmentToDriverManagerFragment(null)
|
SettingsSubscreen.DRIVER_MANAGER,
|
||||||
|
null
|
||||||
|
)
|
||||||
binding.root.findNavController().navigate(action)
|
binding.root.findNavController().navigate(action)
|
||||||
},
|
},
|
||||||
{ true },
|
{ true },
|
||||||
|
|
@ -154,7 +160,12 @@ class HomeSettingsFragment : Fragment() {
|
||||||
R.string.gpu_driver_settings,
|
R.string.gpu_driver_settings,
|
||||||
R.drawable.ic_graphics,
|
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.string.applets_description,
|
||||||
R.drawable.ic_applet,
|
R.drawable.ic_applet,
|
||||||
{
|
{
|
||||||
binding.root.findNavController()
|
val action = HomeNavigationDirections.actionGlobalSettingsSubscreenActivity(
|
||||||
.navigate(R.id.action_homeSettingsFragment_to_appletLauncherFragment)
|
SettingsSubscreen.APPLET_LAUNCHER,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
binding.root.findNavController().navigate(action)
|
||||||
},
|
},
|
||||||
{ NativeLibrary.isFirmwareAvailable() },
|
{ NativeLibrary.isFirmwareAvailable() },
|
||||||
R.string.applets_error_firmware,
|
R.string.applets_error_firmware,
|
||||||
|
|
@ -189,8 +203,11 @@ class HomeSettingsFragment : Fragment() {
|
||||||
R.string.manage_yuzu_data_description,
|
R.string.manage_yuzu_data_description,
|
||||||
R.drawable.ic_install,
|
R.drawable.ic_install,
|
||||||
{
|
{
|
||||||
binding.root.findNavController()
|
val action = HomeNavigationDirections.actionGlobalSettingsSubscreenActivity(
|
||||||
.navigate(R.id.action_homeSettingsFragment_to_installableFragment)
|
SettingsSubscreen.INSTALLABLE,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
binding.root.findNavController().navigate(action)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
@ -200,8 +217,11 @@ class HomeSettingsFragment : Fragment() {
|
||||||
R.string.select_games_folder_description,
|
R.string.select_games_folder_description,
|
||||||
R.drawable.ic_add,
|
R.drawable.ic_add,
|
||||||
{
|
{
|
||||||
binding.root.findNavController()
|
val action = HomeNavigationDirections.actionGlobalSettingsSubscreenActivity(
|
||||||
.navigate(R.id.action_homeSettingsFragment_to_gameFoldersFragment)
|
SettingsSubscreen.GAME_FOLDERS,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
binding.root.findNavController().navigate(action)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
@ -284,9 +304,11 @@ class HomeSettingsFragment : Fragment() {
|
||||||
R.string.about_description,
|
R.string.about_description,
|
||||||
R.drawable.ic_info_outline,
|
R.drawable.ic_info_outline,
|
||||||
{
|
{
|
||||||
exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
|
val action = HomeNavigationDirections.actionGlobalSettingsSubscreenActivity(
|
||||||
parentFragmentManager.primaryNavigationFragment?.findNavController()
|
SettingsSubscreen.ABOUT,
|
||||||
?.navigate(R.id.action_homeSettingsFragment_to_aboutFragment)
|
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
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
package org.yuzu.yuzu_emu.fragments
|
package org.yuzu.yuzu_emu.fragments
|
||||||
|
|
@ -14,23 +14,23 @@ import androidx.core.view.WindowInsetsCompat
|
||||||
import androidx.core.view.updatePadding
|
import androidx.core.view.updatePadding
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.navigation.findNavController
|
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import com.google.android.material.transition.MaterialSharedAxis
|
import com.google.android.material.transition.MaterialSharedAxis
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.yuzu.yuzu_emu.NativeLibrary
|
import org.yuzu.yuzu_emu.NativeLibrary
|
||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
import org.yuzu.yuzu_emu.YuzuApplication
|
import org.yuzu.yuzu_emu.YuzuApplication
|
||||||
import org.yuzu.yuzu_emu.adapters.InstallableAdapter
|
import org.yuzu.yuzu_emu.adapters.InstallableAdapter
|
||||||
import org.yuzu.yuzu_emu.databinding.FragmentInstallablesBinding
|
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.HomeViewModel
|
||||||
import org.yuzu.yuzu_emu.model.Installable
|
import org.yuzu.yuzu_emu.model.Installable
|
||||||
import org.yuzu.yuzu_emu.model.TaskState
|
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.FileUtil
|
||||||
|
import org.yuzu.yuzu_emu.utils.InstallableActions
|
||||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
import org.yuzu.yuzu_emu.utils.NativeConfig
|
||||||
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
|
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
|
||||||
import org.yuzu.yuzu_emu.utils.collect
|
import org.yuzu.yuzu_emu.utils.collect
|
||||||
|
|
@ -45,6 +45,9 @@ class InstallableFragment : Fragment() {
|
||||||
private val binding get() = _binding!!
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
private val homeViewModel: HomeViewModel by activityViewModels()
|
private val homeViewModel: HomeViewModel by activityViewModels()
|
||||||
|
private val gamesViewModel: GamesViewModel by activityViewModels()
|
||||||
|
private val addonViewModel: AddonViewModel by activityViewModels()
|
||||||
|
private val driverViewModel: DriverViewModel by activityViewModels()
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
@ -65,12 +68,10 @@ class InstallableFragment : Fragment() {
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
val mainActivity = requireActivity() as MainActivity
|
|
||||||
|
|
||||||
homeViewModel.setStatusBarShadeVisibility(visible = false)
|
homeViewModel.setStatusBarShadeVisibility(visible = false)
|
||||||
|
|
||||||
binding.toolbarInstallables.setNavigationOnClickListener {
|
binding.toolbarInstallables.setNavigationOnClickListener {
|
||||||
binding.root.findNavController().popBackStack()
|
requireActivity().onBackPressedDispatcher.onBackPressed()
|
||||||
}
|
}
|
||||||
|
|
||||||
homeViewModel.openImportSaves.collect(viewLifecycleOwner) {
|
homeViewModel.openImportSaves.collect(viewLifecycleOwner) {
|
||||||
|
|
@ -84,8 +85,8 @@ class InstallableFragment : Fragment() {
|
||||||
Installable(
|
Installable(
|
||||||
R.string.user_data,
|
R.string.user_data,
|
||||||
R.string.user_data_description,
|
R.string.user_data_description,
|
||||||
install = { mainActivity.importUserData.launch(arrayOf("application/zip")) },
|
install = { importUserDataLauncher.launch(arrayOf("application/zip")) },
|
||||||
export = { mainActivity.exportUserData.launch("export.zip") }
|
export = { exportUserDataLauncher.launch("export.zip") }
|
||||||
),
|
),
|
||||||
Installable(
|
Installable(
|
||||||
R.string.manage_save_data,
|
R.string.manage_save_data,
|
||||||
|
|
@ -127,27 +128,33 @@ class InstallableFragment : Fragment() {
|
||||||
Installable(
|
Installable(
|
||||||
R.string.install_game_content,
|
R.string.install_game_content,
|
||||||
R.string.install_game_content_description,
|
R.string.install_game_content_description,
|
||||||
install = { mainActivity.installGameUpdate.launch(arrayOf("*/*")) }
|
install = { installGameUpdateLauncher.launch(arrayOf("*/*")) }
|
||||||
),
|
),
|
||||||
Installable(
|
Installable(
|
||||||
R.string.install_firmware,
|
R.string.install_firmware,
|
||||||
R.string.install_firmware_description,
|
R.string.install_firmware_description,
|
||||||
install = { mainActivity.getFirmware.launch(arrayOf("application/zip")) }
|
install = { getFirmwareLauncher.launch(arrayOf("application/zip")) }
|
||||||
),
|
),
|
||||||
Installable(
|
Installable(
|
||||||
R.string.uninstall_firmware,
|
R.string.uninstall_firmware,
|
||||||
R.string.uninstall_firmware_description,
|
R.string.uninstall_firmware_description,
|
||||||
install = { mainActivity.uninstallFirmware() }
|
install = {
|
||||||
|
InstallableActions.uninstallFirmware(
|
||||||
|
activity = requireActivity(),
|
||||||
|
fragmentManager = parentFragmentManager,
|
||||||
|
homeViewModel = homeViewModel
|
||||||
|
)
|
||||||
|
}
|
||||||
),
|
),
|
||||||
Installable(
|
Installable(
|
||||||
R.string.install_prod_keys,
|
R.string.install_prod_keys,
|
||||||
R.string.install_prod_keys_description,
|
R.string.install_prod_keys_description,
|
||||||
install = { mainActivity.getProdKey.launch(arrayOf("*/*")) }
|
install = { getProdKeyLauncher.launch(arrayOf("*/*")) }
|
||||||
),
|
),
|
||||||
Installable(
|
Installable(
|
||||||
R.string.install_amiibo_keys,
|
R.string.install_amiibo_keys,
|
||||||
R.string.install_amiibo_keys_description,
|
R.string.install_amiibo_keys_description,
|
||||||
install = { mainActivity.getAmiiboKey.launch(arrayOf("*/*")) }
|
install = { getAmiiboKeyLauncher.launch(arrayOf("*/*")) }
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -180,6 +187,132 @@ class InstallableFragment : Fragment() {
|
||||||
windowInsets
|
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 =
|
private val importSaves =
|
||||||
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
|
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
|
||||||
if (result == null) {
|
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
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
package org.yuzu.yuzu_emu.fragments
|
package org.yuzu.yuzu_emu.fragments
|
||||||
|
|
@ -13,7 +13,6 @@ import androidx.core.view.WindowInsetsCompat
|
||||||
import androidx.core.view.updatePadding
|
import androidx.core.view.updatePadding
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.navigation.findNavController
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.google.android.material.transition.MaterialSharedAxis
|
import com.google.android.material.transition.MaterialSharedAxis
|
||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
|
|
@ -48,7 +47,7 @@ class LicensesFragment : Fragment() {
|
||||||
homeViewModel.setStatusBarShadeVisibility(visible = false)
|
homeViewModel.setStatusBarShadeVisibility(visible = false)
|
||||||
|
|
||||||
binding.toolbarLicenses.setNavigationOnClickListener {
|
binding.toolbarLicenses.setNavigationOnClickListener {
|
||||||
binding.root.findNavController().popBackStack()
|
requireActivity().onBackPressedDispatcher.onBackPressed()
|
||||||
}
|
}
|
||||||
|
|
||||||
val licenses = listOf(
|
val licenses = listOf(
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ class ProfileManagerFragment : Fragment() {
|
||||||
homeViewModel.setStatusBarShadeVisibility(visible = false)
|
homeViewModel.setStatusBarShadeVisibility(visible = false)
|
||||||
|
|
||||||
binding.toolbarProfiles.setNavigationOnClickListener {
|
binding.toolbarProfiles.setNavigationOnClickListener {
|
||||||
findNavController().popBackStack()
|
requireActivity().onBackPressedDispatcher.onBackPressed()
|
||||||
}
|
}
|
||||||
|
|
||||||
setupRecyclerView()
|
setupRecyclerView()
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ import androidx.preference.PreferenceManager
|
||||||
import com.google.android.material.color.MaterialColors
|
import com.google.android.material.color.MaterialColors
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FilenameFilter
|
|
||||||
import org.yuzu.yuzu_emu.NativeLibrary
|
import org.yuzu.yuzu_emu.NativeLibrary
|
||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
|
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.DriverViewModel
|
||||||
import org.yuzu.yuzu_emu.model.GamesViewModel
|
import org.yuzu.yuzu_emu.model.GamesViewModel
|
||||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||||
import org.yuzu.yuzu_emu.model.InstallResult
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import org.yuzu.yuzu_emu.model.TaskState
|
|
||||||
import org.yuzu.yuzu_emu.model.TaskViewModel
|
import org.yuzu.yuzu_emu.model.TaskViewModel
|
||||||
import org.yuzu.yuzu_emu.utils.*
|
import org.yuzu.yuzu_emu.utils.*
|
||||||
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
|
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 androidx.core.content.edit
|
||||||
import org.yuzu.yuzu_emu.activities.EmulationActivity
|
import org.yuzu.yuzu_emu.activities.EmulationActivity
|
||||||
import kotlin.text.compareTo
|
import kotlin.text.compareTo
|
||||||
|
|
@ -453,35 +446,13 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun processKey(result: Uri, extension: String = "keys") {
|
fun processKey(result: Uri, extension: String = "keys") {
|
||||||
contentResolver.takePersistableUriPermission(
|
InstallableActions.processKey(
|
||||||
result,
|
activity = this,
|
||||||
Intent.FLAG_GRANT_READ_URI_PERMISSION
|
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 ->
|
val getFirmware = registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
|
||||||
|
|
@ -491,75 +462,21 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun processFirmware(result: Uri, onComplete: (() -> Unit)? = null) {
|
fun processFirmware(result: Uri, onComplete: (() -> Unit)? = null) {
|
||||||
val filterNCA = FilenameFilter { _, dirName -> dirName.endsWith(".nca") }
|
InstallableActions.processFirmware(
|
||||||
|
activity = this,
|
||||||
val firmwarePath =
|
fragmentManager = supportFragmentManager,
|
||||||
File(NativeConfig.getNandDir() + "/system/Contents/registered/")
|
homeViewModel = homeViewModel,
|
||||||
val cacheFirmwareDir = File("${cacheDir.path}/registered/")
|
result = result,
|
||||||
|
onComplete = onComplete
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun uninstallFirmware() {
|
fun uninstallFirmware() {
|
||||||
val firmwarePath =
|
InstallableActions.uninstallFirmware(
|
||||||
File(NativeConfig.getNandDir() + "/system/Contents/registered/")
|
activity = this,
|
||||||
ProgressDialogFragment.newInstance(
|
fragmentManager = supportFragmentManager,
|
||||||
this,
|
homeViewModel = homeViewModel
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val installGameUpdate = registerForActivityResult(
|
val installGameUpdate = registerForActivityResult(
|
||||||
|
|
@ -606,101 +523,12 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun installContent(documents: List<Uri>) {
|
private fun installContent(documents: List<Uri>) {
|
||||||
ProgressDialogFragment.newInstance(
|
InstallableActions.installContent(
|
||||||
this@MainActivity,
|
activity = this,
|
||||||
R.string.installing_game_content
|
fragmentManager = supportFragmentManager,
|
||||||
) { progressCallback, messageCallback ->
|
addonViewModel = addonViewModel,
|
||||||
var installSuccess = 0
|
documents = documents
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val exportUserData = registerForActivityResult(
|
val exportUserData = registerForActivityResult(
|
||||||
|
|
@ -709,25 +537,11 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
return@registerForActivityResult
|
return@registerForActivityResult
|
||||||
}
|
}
|
||||||
|
InstallableActions.exportUserData(
|
||||||
ProgressDialogFragment.newInstance(
|
activity = this,
|
||||||
this,
|
fragmentManager = supportFragmentManager,
|
||||||
R.string.exporting_user_data,
|
result = result
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val importUserData =
|
val importUserData =
|
||||||
|
|
@ -735,58 +549,12 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
return@registerForActivityResult
|
return@registerForActivityResult
|
||||||
}
|
}
|
||||||
|
InstallableActions.importUserData(
|
||||||
ProgressDialogFragment.newInstance(
|
activity = this,
|
||||||
this,
|
fragmentManager = supportFragmentManager,
|
||||||
R.string.importing_user_data
|
gamesViewModel = gamesViewModel,
|
||||||
) { progressCallback, _ ->
|
driverViewModel = driverViewModel,
|
||||||
val checkStream =
|
result = result
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:id="@+id/appbar_about"
|
android:id="@+id/appbar_about"
|
||||||
|
style="@style/Widget.Eden.TransparentTopAppBarLayout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:fitsSystemWindows="true"
|
android:fitsSystemWindows="true"
|
||||||
android:touchscreenBlocksFocus="false"
|
android:touchscreenBlocksFocus="false">
|
||||||
android:background="@android:color/transparent"
|
|
||||||
app:elevation="0dp">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.MaterialToolbar
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
android:id="@+id/toolbar_about"
|
android:id="@+id/toolbar_about"
|
||||||
|
|
@ -41,15 +40,41 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:padding="24dp">
|
android:paddingBottom="24dp"
|
||||||
|
android:paddingStart="24dp"
|
||||||
|
android:paddingTop="0dp"
|
||||||
|
android:paddingEnd="24dp">
|
||||||
|
|
||||||
<ImageView
|
<LinearLayout
|
||||||
android:id="@+id/image_logo"
|
android:layout_width="wrap_content"
|
||||||
android:layout_width="200dp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_height="200dp"
|
|
||||||
android:layout_gravity="center_vertical"
|
|
||||||
android:layout_marginEnd="32dp"
|
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
|
<LinearLayout
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
|
|
@ -57,39 +82,6 @@
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:orientation="vertical">
|
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
|
<com.google.android.material.card.MaterialCardView
|
||||||
android:id="@+id/button_contributors"
|
android:id="@+id/button_contributors"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
@ -205,7 +197,7 @@
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="24dp"
|
android:layout_marginTop="12dp"
|
||||||
android:gravity="start"
|
android:gravity="start"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,11 @@
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:id="@+id/appbar_about"
|
android:id="@+id/appbar_about"
|
||||||
|
style="@style/Widget.Eden.TransparentTopAppBarLayout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:fitsSystemWindows="true"
|
android:fitsSystemWindows="true"
|
||||||
android:touchscreenBlocksFocus="false"
|
android:touchscreenBlocksFocus="false">
|
||||||
android:background="@android:color/transparent"
|
|
||||||
app:elevation="0dp">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.MaterialToolbar
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
android:id="@+id/toolbar_about"
|
android:id="@+id/toolbar_about"
|
||||||
|
|
@ -43,48 +42,35 @@
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:paddingBottom="24dp">
|
android:paddingBottom="24dp">
|
||||||
|
|
||||||
<ImageView
|
<LinearLayout
|
||||||
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
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginHorizontal="16dp"
|
android:orientation="vertical"
|
||||||
android:layout_marginTop="16dp"
|
android:gravity="center_horizontal">
|
||||||
app:cardBackgroundColor="@android:color/transparent"
|
|
||||||
app:cardCornerRadius="16dp"
|
<ImageView
|
||||||
app:cardElevation="0dp"
|
android:id="@+id/image_logo"
|
||||||
app:strokeColor="?attr/colorOutline"
|
android:layout_width="160dp"
|
||||||
app:strokeWidth="1dp">
|
android:layout_height="160dp"
|
||||||
<LinearLayout
|
android:layout_marginBottom="8dp"
|
||||||
android:layout_width="match_parent"
|
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:layout_height="wrap_content"
|
||||||
android:paddingVertical="20dp"
|
android:text="@string/app_name"
|
||||||
android:paddingHorizontal="20dp"
|
android:textAlignment="center" />
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<com.google.android.material.textview.MaterialTextView
|
<com.google.android.material.textview.MaterialTextView
|
||||||
style="@style/TextAppearance.Material3.TitleMedium"
|
style="@style/SynthwaveText.Body"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:textAlignment="viewStart"
|
android:layout_marginTop="4dp"
|
||||||
android:text="@string/about" />
|
android:textAlignment="center"
|
||||||
|
android:text="@string/about_app_description" />
|
||||||
|
|
||||||
<com.google.android.material.textview.MaterialTextView
|
</LinearLayout>
|
||||||
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>
|
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
<com.google.android.material.card.MaterialCardView
|
||||||
android:id="@+id/button_contributors"
|
android:id="@+id/button_contributors"
|
||||||
|
|
@ -206,7 +192,7 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:gravity="center_horizontal"
|
android:gravity="center_horizontal"
|
||||||
android:layout_marginTop="24dp"
|
android:layout_marginTop="12dp"
|
||||||
android:layout_marginBottom="16dp"
|
android:layout_marginBottom="16dp"
|
||||||
android:layout_marginHorizontal="40dp">
|
android:layout_marginHorizontal="40dp">
|
||||||
|
|
||||||
|
|
@ -220,7 +206,9 @@
|
||||||
app:icon="@drawable/ic_discord"
|
app:icon="@drawable/ic_discord"
|
||||||
app:iconSize="24dp"
|
app:iconSize="24dp"
|
||||||
app:iconGravity="textStart"
|
app:iconGravity="textStart"
|
||||||
app:iconPadding="0dp" />
|
app:iconPadding="0dp"
|
||||||
|
app:strokeColor="?attr/colorOutline"
|
||||||
|
app:strokeWidth="1dp" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
style="@style/EdenButton.Secondary"
|
style="@style/EdenButton.Secondary"
|
||||||
|
|
@ -232,7 +220,9 @@
|
||||||
app:icon="@drawable/ic_stoat"
|
app:icon="@drawable/ic_stoat"
|
||||||
app:iconSize="24dp"
|
app:iconSize="24dp"
|
||||||
app:iconGravity="textStart"
|
app:iconGravity="textStart"
|
||||||
app:iconPadding="0dp" />
|
app:iconPadding="0dp"
|
||||||
|
app:strokeColor="?attr/colorOutline"
|
||||||
|
app:strokeWidth="1dp" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
style="@style/EdenButton.Secondary"
|
style="@style/EdenButton.Secondary"
|
||||||
|
|
@ -244,7 +234,9 @@
|
||||||
app:icon="@drawable/ic_x"
|
app:icon="@drawable/ic_x"
|
||||||
app:iconSize="24dp"
|
app:iconSize="24dp"
|
||||||
app:iconGravity="textStart"
|
app:iconGravity="textStart"
|
||||||
app:iconPadding="0dp" />
|
app:iconPadding="0dp"
|
||||||
|
app:strokeColor="?attr/colorOutline"
|
||||||
|
app:strokeWidth="1dp" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
style="@style/EdenButton.Secondary"
|
style="@style/EdenButton.Secondary"
|
||||||
|
|
@ -256,7 +248,9 @@
|
||||||
app:icon="@drawable/ic_website"
|
app:icon="@drawable/ic_website"
|
||||||
app:iconSize="24dp"
|
app:iconSize="24dp"
|
||||||
app:iconGravity="textStart"
|
app:iconGravity="textStart"
|
||||||
app:iconPadding="0dp" />
|
app:iconPadding="0dp"
|
||||||
|
app:strokeColor="?attr/colorOutline"
|
||||||
|
app:strokeWidth="1dp" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/button_github"
|
android:id="@+id/button_github"
|
||||||
|
|
@ -268,7 +262,9 @@
|
||||||
app:icon="@drawable/ic_github"
|
app:icon="@drawable/ic_github"
|
||||||
app:iconSize="24dp"
|
app:iconSize="24dp"
|
||||||
app:iconGravity="textStart"
|
app:iconGravity="textStart"
|
||||||
app:iconPadding="0dp" />
|
app:iconPadding="0dp"
|
||||||
|
app:strokeColor="?attr/colorOutline"
|
||||||
|
app:strokeWidth="1dp" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,10 +40,6 @@
|
||||||
|
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_global_settingsActivity"
|
android:id="@+id/action_global_settingsActivity"
|
||||||
app:destination="@id/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" />
|
|
||||||
|
|
||||||
</navigation>
|
</navigation>
|
||||||
|
|
|
||||||
|
|
@ -20,26 +20,7 @@
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/homeSettingsFragment"
|
android:id="@+id/homeSettingsFragment"
|
||||||
android:name="org.yuzu.yuzu_emu.fragments.HomeSettingsFragment"
|
android:name="org.yuzu.yuzu_emu.fragments.HomeSettingsFragment"
|
||||||
android:label="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>
|
|
||||||
|
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/firstTimeSetupFragment"
|
android:id="@+id/firstTimeSetupFragment"
|
||||||
|
|
@ -55,11 +36,7 @@
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/aboutFragment"
|
android:id="@+id/aboutFragment"
|
||||||
android:name="org.yuzu.yuzu_emu.fragments.AboutFragment"
|
android:name="org.yuzu.yuzu_emu.fragments.AboutFragment"
|
||||||
android:label="AboutFragment" >
|
android:label="AboutFragment" />
|
||||||
<action
|
|
||||||
android:id="@+id/action_aboutFragment_to_licensesFragment"
|
|
||||||
app:destination="@id/licensesFragment" />
|
|
||||||
</fragment>
|
|
||||||
|
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/licensesFragment"
|
android:id="@+id/licensesFragment"
|
||||||
|
|
@ -101,11 +78,23 @@
|
||||||
|
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_global_settingsActivity"
|
android:id="@+id/action_global_settingsActivity"
|
||||||
app:destination="@id/settingsActivity"
|
app:destination="@id/settingsActivity" />
|
||||||
app:enterAnim="@anim/nav_default_enter_anim"
|
<activity
|
||||||
app:exitAnim="@anim/nav_default_exit_anim"
|
android:id="@+id/settingsSubscreenActivity"
|
||||||
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
|
android:name="org.yuzu.yuzu_emu.features.settings.ui.SettingsSubscreenActivity"
|
||||||
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
|
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
|
<fragment
|
||||||
android:id="@+id/installableFragment"
|
android:id="@+id/installableFragment"
|
||||||
android:name="org.yuzu.yuzu_emu.fragments.InstallableFragment"
|
android:name="org.yuzu.yuzu_emu.fragments.InstallableFragment"
|
||||||
|
|
@ -119,9 +108,6 @@
|
||||||
app:argType="org.yuzu.yuzu_emu.model.Game"
|
app:argType="org.yuzu.yuzu_emu.model.Game"
|
||||||
app:nullable="true"
|
app:nullable="true"
|
||||||
android:defaultValue="@null" />
|
android:defaultValue="@null" />
|
||||||
<action
|
|
||||||
android:id="@+id/action_driverManagerFragment_to_driverFetcherFragment"
|
|
||||||
app:destination="@id/driverFetcherFragment" />
|
|
||||||
</fragment>
|
</fragment>
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/appletLauncherFragment"
|
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="copied_to_clipboard">Copied to clipboard</string>
|
||||||
<string name="about_app_description">An open-source Switch emulator</string>
|
<string name="about_app_description">An open-source Switch emulator</string>
|
||||||
<string name="contributors">Contributors</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="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="licenses_description">Projects that make Eden for Android possible</string>
|
||||||
<string name="build">Build</string>
|
<string name="build">Build</string>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue