summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar PabloG022023-06-03 14:14:05 +0200
committerGravatar PabloG022023-06-04 20:52:12 +0200
commit5435f0be5e4d81da5140cea79904252403f108c2 (patch)
tree0f45a42a0b81ab2f22bdf2a23607491916ddb4cd /src
parentandroid: move unzip function to FileUtil and use SecurityException (diff)
downloadyuzu-5435f0be5e4d81da5140cea79904252403f108c2.tar.gz
yuzu-5435f0be5e4d81da5140cea79904252403f108c2.tar.xz
yuzu-5435f0be5e4d81da5140cea79904252403f108c2.zip
android: add option to install firmware
Diffstat (limited to 'src')
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt36
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt65
-rw-r--r--src/android/app/src/main/res/drawable/ic_firmware.xml10
-rw-r--r--src/android/app/src/main/res/values/strings.xml6
5 files changed, 124 insertions, 1 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
index 67bcf8491..cc4b0157b 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
@@ -19,10 +19,10 @@ import androidx.appcompat.app.AppCompatActivity
19import androidx.core.app.ActivityCompat 19import androidx.core.app.ActivityCompat
20import androidx.core.app.NotificationCompat 20import androidx.core.app.NotificationCompat
21import androidx.core.app.NotificationManagerCompat 21import androidx.core.app.NotificationManagerCompat
22import androidx.core.content.ContextCompat
23import androidx.core.view.ViewCompat 22import androidx.core.view.ViewCompat
24import androidx.core.view.WindowInsetsCompat 23import androidx.core.view.WindowInsetsCompat
25import androidx.core.view.updatePadding 24import androidx.core.view.updatePadding
25import androidx.documentfile.provider.DocumentFile
26import androidx.fragment.app.Fragment 26import androidx.fragment.app.Fragment
27import androidx.fragment.app.activityViewModels 27import androidx.fragment.app.activityViewModels
28import androidx.navigation.fragment.findNavController 28import androidx.navigation.fragment.findNavController
@@ -40,6 +40,7 @@ import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
40import org.yuzu.yuzu_emu.model.HomeSetting 40import org.yuzu.yuzu_emu.model.HomeSetting
41import org.yuzu.yuzu_emu.model.HomeViewModel 41import org.yuzu.yuzu_emu.model.HomeViewModel
42import org.yuzu.yuzu_emu.ui.main.MainActivity 42import org.yuzu.yuzu_emu.ui.main.MainActivity
43import org.yuzu.yuzu_emu.utils.FileUtil
43import org.yuzu.yuzu_emu.utils.GpuDriverHelper 44import org.yuzu.yuzu_emu.utils.GpuDriverHelper
44 45
45class HomeSettingsFragment : Fragment() { 46class HomeSettingsFragment : Fragment() {
@@ -109,6 +110,11 @@ class HomeSettingsFragment : Fragment() {
109 R.drawable.ic_unlock 110 R.drawable.ic_unlock
110 ) { mainActivity.getProdKey.launch(arrayOf("*/*")) }, 111 ) { mainActivity.getProdKey.launch(arrayOf("*/*")) },
111 HomeSetting( 112 HomeSetting(
113 R.string.install_firmware,
114 R.string.install_firmware_description,
115 R.drawable.ic_firmware
116 ) { mainActivity.getFirmware.launch(arrayOf("application/zip")) },
117 HomeSetting(
112 R.string.about, 118 R.string.about,
113 R.string.about_description, 119 R.string.about_description,
114 R.drawable.ic_info_outline 120 R.drawable.ic_info_outline
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt
new file mode 100644
index 000000000..edf7b8a3c
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt
@@ -0,0 +1,36 @@
1package org.yuzu.yuzu_emu.fragments
2
3import android.app.Dialog
4import android.os.Bundle
5import androidx.fragment.app.DialogFragment
6import com.google.android.material.dialog.MaterialAlertDialogBuilder
7import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
8
9class IndeterminateProgressDialogFragment : DialogFragment() {
10 override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
11 val titleId = requireArguments().getInt(TITLE)
12
13 val progressBinding = DialogProgressBarBinding.inflate(layoutInflater)
14 progressBinding.progressBar.isIndeterminate = true
15 return MaterialAlertDialogBuilder(requireContext())
16 .setTitle(titleId)
17 .setView(progressBinding.root)
18 .show()
19 }
20
21 companion object {
22 const val TAG = "IndeterminateProgressDialogFragment"
23
24 private const val TITLE = "Title"
25
26 fun newInstance(
27 titleId: Int,
28 ): IndeterminateProgressDialogFragment {
29 val dialog = IndeterminateProgressDialogFragment()
30 val args = Bundle()
31 args.putInt(TITLE, titleId)
32 dialog.arguments = args
33 return dialog
34 }
35 }
36} \ No newline at end of file
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
index f8bca11bb..bb8311023 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
@@ -26,6 +26,7 @@ import androidx.preference.PreferenceManager
26import com.google.android.material.color.MaterialColors 26import com.google.android.material.color.MaterialColors
27import com.google.android.material.dialog.MaterialAlertDialogBuilder 27import com.google.android.material.dialog.MaterialAlertDialogBuilder
28import com.google.android.material.navigation.NavigationBarView 28import com.google.android.material.navigation.NavigationBarView
29import kotlinx.coroutines.CoroutineScope
29import kotlinx.coroutines.Dispatchers 30import kotlinx.coroutines.Dispatchers
30import kotlinx.coroutines.launch 31import kotlinx.coroutines.launch
31import kotlinx.coroutines.withContext 32import kotlinx.coroutines.withContext
@@ -37,10 +38,13 @@ import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
37import org.yuzu.yuzu_emu.features.settings.model.Settings 38import org.yuzu.yuzu_emu.features.settings.model.Settings
38import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity 39import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity
39import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile 40import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
41import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
40import org.yuzu.yuzu_emu.fragments.MessageDialogFragment 42import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
41import org.yuzu.yuzu_emu.model.GamesViewModel 43import org.yuzu.yuzu_emu.model.GamesViewModel
42import org.yuzu.yuzu_emu.model.HomeViewModel 44import org.yuzu.yuzu_emu.model.HomeViewModel
43import org.yuzu.yuzu_emu.utils.* 45import org.yuzu.yuzu_emu.utils.*
46import java.io.File
47import java.io.FilenameFilter
44import java.io.IOException 48import java.io.IOException
45 49
46class MainActivity : AppCompatActivity(), ThemeProvider { 50class MainActivity : AppCompatActivity(), ThemeProvider {
@@ -315,6 +319,67 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
315 } 319 }
316 } 320 }
317 321
322 val getFirmware =
323 registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
324 if (result == null)
325 return@registerForActivityResult
326
327 val inputZip = contentResolver.openInputStream(result)
328 if (inputZip == null) {
329 Toast.makeText(
330 applicationContext,
331 getString(R.string.fatal_error),
332 Toast.LENGTH_LONG
333 ).show()
334 return@registerForActivityResult
335 }
336
337 val filterNCA = FilenameFilter { _, dirName -> dirName.endsWith(".nca") }
338
339 val firmwarePath =
340 File(DirectoryInitialization.userDirectory + "/nand/system/Contents/registered/")
341 val cacheFirmwareDir = File("${cacheDir.path}/registered/")
342
343 val installingFirmwareDialog = IndeterminateProgressDialogFragment.newInstance(
344 R.string.firmware_installing
345 )
346 installingFirmwareDialog.isCancelable = false
347 installingFirmwareDialog.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
348
349 lifecycleScope.launch(Dispatchers.IO) {
350 try {
351 FileUtil.unzip(inputZip, cacheFirmwareDir)
352 val unfilteredNumOfFiles = cacheFirmwareDir.list()?.size ?: -1
353 val filteredNumOfFiles = cacheFirmwareDir.list(filterNCA)?.size ?: -2
354 if (unfilteredNumOfFiles != filteredNumOfFiles) {
355 withContext(Dispatchers.Main) {
356 installingFirmwareDialog.dismiss()
357 MessageDialogFragment.newInstance(
358 R.string.firmware_installed_failure,
359 R.string.firmware_installed_failure_description
360 ).show(supportFragmentManager, MessageDialogFragment.TAG)
361 }
362 } else {
363 firmwarePath.deleteRecursively()
364 cacheFirmwareDir.copyRecursively(firmwarePath, true)
365 withContext(Dispatchers.Main) {
366 installingFirmwareDialog.dismiss()
367 Toast.makeText(
368 applicationContext,
369 getString(R.string.save_file_imported_success),
370 Toast.LENGTH_LONG
371 ).show()
372 }
373 }
374 } catch (e: Exception) {
375 Toast.makeText(applicationContext, getString(R.string.fatal_error), Toast.LENGTH_LONG)
376 .show()
377 } finally {
378 cacheFirmwareDir.deleteRecursively()
379 }
380 }
381 }
382
318 val getAmiiboKey = 383 val getAmiiboKey =
319 registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> 384 registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
320 if (result == null) 385 if (result == null)
diff --git a/src/android/app/src/main/res/drawable/ic_firmware.xml b/src/android/app/src/main/res/drawable/ic_firmware.xml
new file mode 100644
index 000000000..61f3485e4
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_firmware.xml
@@ -0,0 +1,10 @@
1<vector xmlns:android="http://schemas.android.com/apk/res/android"
2 android:width="24dp"
3 android:height="24dp"
4 android:viewportWidth="960"
5 android:viewportHeight="960"
6 android:tint="?attr/colorControlNormal">
7 <path
8 android:fillColor="@android:color/white"
9 android:pathData="M160,840Q127,840 103.5,816.5Q80,793 80,760L80,200Q80,167 103.5,143.5Q127,120 160,120L720,120Q753,120 776.5,143.5Q800,167 800,200L800,280L840,280Q857,280 868.5,291.5Q880,303 880,320Q880,337 868.5,348.5Q857,360 840,360L800,360L800,440L840,440Q857,440 868.5,451.5Q880,463 880,480Q880,497 868.5,508.5Q857,520 840,520L800,520L800,600L840,600Q857,600 868.5,611.5Q880,623 880,640Q880,657 868.5,668.5Q857,680 840,680L800,680L800,760Q800,793 776.5,816.5Q753,840 720,840L160,840ZM160,760L720,760Q720,760 720,760Q720,760 720,760L720,200Q720,200 720,200Q720,200 720,200L160,200Q160,200 160,200Q160,200 160,200L160,760Q160,760 160,760Q160,760 160,760ZM280,680L400,680Q417,680 428.5,668.5Q440,657 440,640L440,560Q440,543 428.5,531.5Q417,520 400,520L280,520Q263,520 251.5,531.5Q240,543 240,560L240,640Q240,657 251.5,668.5Q263,680 280,680ZM520,400L600,400Q617,400 628.5,388.5Q640,377 640,360L640,320Q640,303 628.5,291.5Q617,280 600,280L520,280Q503,280 491.5,291.5Q480,303 480,320L480,360Q480,377 491.5,388.5Q503,400 520,400ZM280,480L400,480Q417,480 428.5,468.5Q440,457 440,440L440,320Q440,303 428.5,291.5Q417,280 400,280L280,280Q263,280 251.5,291.5Q240,303 240,320L240,440Q240,457 251.5,468.5Q263,480 280,480ZM520,680L600,680Q617,680 628.5,668.5Q640,657 640,640L640,480Q640,463 628.5,451.5Q617,440 600,440L520,440Q503,440 491.5,451.5Q480,463 480,480L480,640Q480,657 491.5,668.5Q503,680 520,680ZM160,200L160,200Q160,200 160,200Q160,200 160,200L160,760Q160,760 160,760Q160,760 160,760L160,760Q160,760 160,760Q160,760 160,760L160,200Q160,200 160,200Q160,200 160,200Z"/>
10</vector>
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index fc24e27f5..4b3bfcf9d 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -96,6 +96,12 @@
96 <string name="save_file_invalid_zip_structure_description">The first subfolder name must be the title ID of the game.</string> 96 <string name="save_file_invalid_zip_structure_description">The first subfolder name must be the title ID of the game.</string>
97 <string name="import_saves">Import</string> 97 <string name="import_saves">Import</string>
98 <string name="export_saves">Export</string> 98 <string name="export_saves">Export</string>
99 <string name="install_firmware">Install firmware</string>
100 <string name="install_firmware_description">Required to boot some games</string>
101 <string name="firmware_installing">Installing firmware</string>
102 <string name="firmware_installed_success">Firmware installed successfully</string>
103 <string name="firmware_installed_failure">Firmware installation failed.</string>
104 <string name="firmware_installed_failure_description">Check that the ZIP contains a firmware.</string>
99 105
100 <!-- About screen strings --> 106 <!-- About screen strings -->
101 <string name="gaia_is_not_real">Gaia isn\'t real</string> 107 <string name="gaia_is_not_real">Gaia isn\'t real</string>