From 19674ec78d0c8b831083de3e7cf2fa29aacf9d4d Mon Sep 17 00:00:00 2001 From: PabloG02 Date: Sat, 3 Jun 2023 14:13:20 +0200 Subject: [PATCH 1/7] android: move unzip function to FileUtil and use SecurityException --- .../fragments/ImportExportSavesFragment.kt | 34 ++----------------- .../java/org/yuzu/yuzu_emu/utils/FileUtil.kt | 32 +++++++++++++++++ 2 files changed, 34 insertions(+), 32 deletions(-) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ImportExportSavesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ImportExportSavesFragment.kt index 5f107b37d..36e63bb9e 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ImportExportSavesFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ImportExportSavesFragment.kt @@ -23,17 +23,14 @@ import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.features.DocumentProvider import org.yuzu.yuzu_emu.getPublicFilesDir -import java.io.BufferedInputStream +import org.yuzu.yuzu_emu.utils.FileUtil import java.io.BufferedOutputStream import java.io.File import java.io.FileOutputStream import java.io.FilenameFilter -import java.io.IOException -import java.io.InputStream import java.time.LocalDateTime import java.time.format.DateTimeFormatter import java.util.zip.ZipEntry -import java.util.zip.ZipInputStream import java.util.zip.ZipOutputStream class ImportExportSavesFragment : DialogFragment() { @@ -124,33 +121,6 @@ class ImportExportSavesFragment : DialogFragment() { return true } - /** - * Extracts the save files located in the given zip file and copies them to the saves folder. - * @exception IOException if the file was being created outside of the target directory - */ - private fun unzip(zipStream: InputStream, destDir: File): Boolean { - val zis = ZipInputStream(BufferedInputStream(zipStream)) - var entry: ZipEntry? = zis.nextEntry - while (entry != null) { - val entryName = entry.name - val entryFile = File(destDir, entryName) - if (!entryFile.canonicalPath.startsWith(destDir.canonicalPath + File.separator)) { - zis.close() - throw IOException("Entry is outside of the target dir: " + entryFile.name) - } - if (entry.isDirectory) { - entryFile.mkdirs() - } else { - entryFile.parentFile?.mkdirs() - entryFile.createNewFile() - entryFile.outputStream().use { fos -> zis.copyTo(fos) } - } - entry = zis.nextEntry - } - zis.close() - return true - } - /** * Exports the save file located in the given folder path by creating a zip file and sharing it via intent. */ @@ -204,7 +174,7 @@ class ImportExportSavesFragment : DialogFragment() { try { CoroutineScope(Dispatchers.IO).launch { - unzip(inputZip, cacheSaveDir) + FileUtil.unzip(inputZip, cacheSaveDir) cacheSaveDir.list(filterTitleId)?.forEach { savePath -> File(savesFolder, savePath).deleteRecursively() File(cacheSaveDir, savePath).copyRecursively(File(savesFolder, savePath), true) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt index 0a7b323b1..593dad8d3 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt @@ -9,10 +9,14 @@ import android.net.Uri import android.provider.DocumentsContract import androidx.documentfile.provider.DocumentFile import org.yuzu.yuzu_emu.model.MinimalDocumentFile +import java.io.BufferedInputStream +import java.io.File import java.io.FileOutputStream import java.io.IOException import java.io.InputStream import java.net.URLDecoder +import java.util.zip.ZipEntry +import java.util.zip.ZipInputStream object FileUtil { const val PATH_TREE = "tree" @@ -276,6 +280,34 @@ object FileUtil { return false } + /** + * Extracts the given zip file into the given directory. + * @exception IOException if the file was being created outside of the target directory + */ + @Throws(SecurityException::class) + fun unzip(zipStream: InputStream, destDir: File): Boolean { + ZipInputStream(BufferedInputStream(zipStream)).use { zis -> + var entry: ZipEntry? = zis.nextEntry + while (entry != null) { + val entryName = entry.name + val entryFile = File(destDir, entryName) + if (!entryFile.canonicalPath.startsWith(destDir.canonicalPath + File.separator)) { + throw SecurityException("Entry is outside of the target dir: " + entryFile.name) + } + if (entry.isDirectory) { + entryFile.mkdirs() + } else { + entryFile.parentFile?.mkdirs() + entryFile.createNewFile() + entryFile.outputStream().use { fos -> zis.copyTo(fos) } + } + entry = zis.nextEntry + } + } + + return true + } + fun isRootTreeUri(uri: Uri): Boolean { val paths = uri.pathSegments return paths.size == 2 && PATH_TREE == paths[0] From 5435f0be5e4d81da5140cea79904252403f108c2 Mon Sep 17 00:00:00 2001 From: PabloG02 Date: Sat, 3 Jun 2023 14:14:05 +0200 Subject: [PATCH 2/7] android: add option to install firmware --- .../fragments/HomeSettingsFragment.kt | 8 ++- .../IndeterminateProgressDialogFragment.kt | 36 ++++++++++ .../org/yuzu/yuzu_emu/ui/main/MainActivity.kt | 65 +++++++++++++++++++ .../app/src/main/res/drawable/ic_firmware.xml | 10 +++ .../app/src/main/res/values/strings.xml | 6 ++ 5 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt create mode 100644 src/android/app/src/main/res/drawable/ic_firmware.xml 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 import androidx.core.app.ActivityCompat import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat -import androidx.core.content.ContextCompat import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.updatePadding +import androidx.documentfile.provider.DocumentFile import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.navigation.fragment.findNavController @@ -40,6 +40,7 @@ import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile import org.yuzu.yuzu_emu.model.HomeSetting import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.ui.main.MainActivity +import org.yuzu.yuzu_emu.utils.FileUtil import org.yuzu.yuzu_emu.utils.GpuDriverHelper class HomeSettingsFragment : Fragment() { @@ -108,6 +109,11 @@ class HomeSettingsFragment : Fragment() { R.string.install_prod_keys_description, R.drawable.ic_unlock ) { mainActivity.getProdKey.launch(arrayOf("*/*")) }, + HomeSetting( + R.string.install_firmware, + R.string.install_firmware_description, + R.drawable.ic_firmware + ) { mainActivity.getFirmware.launch(arrayOf("application/zip")) }, HomeSetting( R.string.about, R.string.about_description, 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 @@ +package org.yuzu.yuzu_emu.fragments + +import android.app.Dialog +import android.os.Bundle +import androidx.fragment.app.DialogFragment +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding + +class IndeterminateProgressDialogFragment : DialogFragment() { + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val titleId = requireArguments().getInt(TITLE) + + val progressBinding = DialogProgressBarBinding.inflate(layoutInflater) + progressBinding.progressBar.isIndeterminate = true + return MaterialAlertDialogBuilder(requireContext()) + .setTitle(titleId) + .setView(progressBinding.root) + .show() + } + + companion object { + const val TAG = "IndeterminateProgressDialogFragment" + + private const val TITLE = "Title" + + fun newInstance( + titleId: Int, + ): IndeterminateProgressDialogFragment { + val dialog = IndeterminateProgressDialogFragment() + val args = Bundle() + args.putInt(TITLE, titleId) + dialog.arguments = args + return dialog + } + } +} \ 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 import com.google.android.material.color.MaterialColors import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.navigation.NavigationBarView +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -37,10 +38,13 @@ import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile +import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment import org.yuzu.yuzu_emu.fragments.MessageDialogFragment import org.yuzu.yuzu_emu.model.GamesViewModel import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.utils.* +import java.io.File +import java.io.FilenameFilter import java.io.IOException class MainActivity : AppCompatActivity(), ThemeProvider { @@ -315,6 +319,67 @@ class MainActivity : AppCompatActivity(), ThemeProvider { } } + val getFirmware = + registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> + if (result == null) + return@registerForActivityResult + + val inputZip = contentResolver.openInputStream(result) + if (inputZip == null) { + Toast.makeText( + applicationContext, + getString(R.string.fatal_error), + Toast.LENGTH_LONG + ).show() + return@registerForActivityResult + } + + val filterNCA = FilenameFilter { _, dirName -> dirName.endsWith(".nca") } + + val firmwarePath = + File(DirectoryInitialization.userDirectory + "/nand/system/Contents/registered/") + val cacheFirmwareDir = File("${cacheDir.path}/registered/") + + val installingFirmwareDialog = IndeterminateProgressDialogFragment.newInstance( + R.string.firmware_installing + ) + installingFirmwareDialog.isCancelable = false + installingFirmwareDialog.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG) + + lifecycleScope.launch(Dispatchers.IO) { + try { + FileUtil.unzip(inputZip, cacheFirmwareDir) + val unfilteredNumOfFiles = cacheFirmwareDir.list()?.size ?: -1 + val filteredNumOfFiles = cacheFirmwareDir.list(filterNCA)?.size ?: -2 + if (unfilteredNumOfFiles != filteredNumOfFiles) { + withContext(Dispatchers.Main) { + installingFirmwareDialog.dismiss() + MessageDialogFragment.newInstance( + R.string.firmware_installed_failure, + R.string.firmware_installed_failure_description + ).show(supportFragmentManager, MessageDialogFragment.TAG) + } + } else { + firmwarePath.deleteRecursively() + cacheFirmwareDir.copyRecursively(firmwarePath, true) + withContext(Dispatchers.Main) { + installingFirmwareDialog.dismiss() + Toast.makeText( + applicationContext, + getString(R.string.save_file_imported_success), + Toast.LENGTH_LONG + ).show() + } + } + } catch (e: Exception) { + Toast.makeText(applicationContext, getString(R.string.fatal_error), Toast.LENGTH_LONG) + .show() + } finally { + cacheFirmwareDir.deleteRecursively() + } + } + } + val getAmiiboKey = registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> 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 @@ + + + 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 @@ The first subfolder name must be the title ID of the game. Import Export + Install firmware + Required to boot some games + Installing firmware + Firmware installed successfully + Firmware installation failed. + Check that the ZIP contains a firmware. Gaia isn\'t real From 8713c442e9e5408f8d8a4e937e2c8e5c9c335430 Mon Sep 17 00:00:00 2001 From: PabloG02 Date: Sat, 3 Jun 2023 14:15:15 +0200 Subject: [PATCH 3/7] android: add option to share log --- .../fragments/HomeSettingsFragment.kt | 23 +++++++++++++++++++ .../app/src/main/res/drawable/ic_log.xml | 10 ++++++++ .../app/src/main/res/values/strings.xml | 3 +++ 3 files changed, 36 insertions(+) create mode 100644 src/android/app/src/main/res/drawable/ic_log.xml 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 cc4b0157b..0bdbabe79 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 @@ -114,6 +114,11 @@ class HomeSettingsFragment : Fragment() { R.string.install_firmware_description, R.drawable.ic_firmware ) { mainActivity.getFirmware.launch(arrayOf("application/zip")) }, + HomeSetting( + R.string.share_log, + R.string.share_log_description, + R.drawable.ic_log + ) { shareLog() }, HomeSetting( R.string.about, R.string.about_description, @@ -268,6 +273,24 @@ class HomeSettingsFragment : Fragment() { .show() } + private fun shareLog() { + val file = DocumentFile.fromSingleUri( + mainActivity, DocumentsContract.buildDocumentUri( + DocumentProvider.AUTHORITY, + "${DocumentProvider.ROOT_ID}/log/yuzu_log.txt" + ) + )!! + if (file.exists()) { + val intent = Intent(Intent.ACTION_SEND) + .setDataAndType(file.uri, FileUtil.TEXT_PLAIN) + .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + .putExtra(Intent.EXTRA_STREAM, file.uri) + startActivity(Intent.createChooser(intent, "Share log")) + } else { + Toast.makeText(requireContext(), getText(R.string.share_log_missing), Toast.LENGTH_SHORT).show() + } + } + private fun setInsets() = ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view: View, windowInsets: WindowInsetsCompat -> val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) diff --git a/src/android/app/src/main/res/drawable/ic_log.xml b/src/android/app/src/main/res/drawable/ic_log.xml new file mode 100644 index 000000000..f55b9ad85 --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_log.xml @@ -0,0 +1,10 @@ + + + diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 4b3bfcf9d..5d42be5e6 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -102,6 +102,9 @@ Firmware installed successfully Firmware installation failed. Check that the ZIP contains a firmware. + Share log + Share the log file + No log file found Gaia isn\'t real From 72597b8ffea24de329366b2beda6b1cad0620fa0 Mon Sep 17 00:00:00 2001 From: PabloG02 Date: Sat, 3 Jun 2023 14:16:07 +0200 Subject: [PATCH 4/7] android: update strings --- src/android/app/src/main/res/values/strings.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 5d42be5e6..1646b90eb 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -97,13 +97,13 @@ Import Export Install firmware - Required to boot some games + Firmware must be in a ZIP archive and is needed to boot some games Installing firmware Firmware installed successfully - Firmware installation failed. - Check that the ZIP contains a firmware. - Share log - Share the log file + Firmware installation failed + Verify that the ZIP contains valid firmware and try again. + Share debug logs + Share yuzu\'s log file to debug issues No log file found From 3733187c147149c995df97d7ac72ab5e4aafd137 Mon Sep 17 00:00:00 2001 From: PabloG02 Date: Sun, 4 Jun 2023 02:24:14 +0200 Subject: [PATCH 5/7] Attempt to move the unzip coroutine to a ViewModel --- .../IndeterminateProgressDialogFragment.kt | 40 +++++++++++++++++- .../org/yuzu/yuzu_emu/model/TaskViewModel.kt | 42 +++++++++++++++++++ .../org/yuzu/yuzu_emu/ui/main/MainActivity.kt | 39 +++++++---------- 3 files changed, 94 insertions(+), 27 deletions(-) create mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/model/TaskViewModel.kt 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 index edf7b8a3c..10a897392 100644 --- 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 @@ -2,33 +2,69 @@ package org.yuzu.yuzu_emu.fragments import android.app.Dialog import android.os.Bundle +import android.widget.Toast import androidx.fragment.app.DialogFragment +import androidx.lifecycle.ViewModelProvider import com.google.android.material.dialog.MaterialAlertDialogBuilder import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding +import org.yuzu.yuzu_emu.model.TaskViewModel +import java.io.Serializable + class IndeterminateProgressDialogFragment : DialogFragment() { + private lateinit var taskViewModel: TaskViewModel + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + taskViewModel = ViewModelProvider(requireActivity())[TaskViewModel::class.java] + val titleId = requireArguments().getInt(TITLE) val progressBinding = DialogProgressBarBinding.inflate(layoutInflater) progressBinding.progressBar.isIndeterminate = true - return MaterialAlertDialogBuilder(requireContext()) + val dialog = MaterialAlertDialogBuilder(requireContext()) .setTitle(titleId) .setView(progressBinding.root) - .show() + .create() + dialog.setCanceledOnTouchOutside(false) + + taskViewModel.isComplete.observe(this) { complete -> + if (complete) { + dialog.dismiss() + when (val result = taskViewModel.result.value) { + is String -> Toast.makeText(requireContext(), result, Toast.LENGTH_LONG).show() + is MessageDialogFragment -> result.show( + parentFragmentManager, + MessageDialogFragment.TAG + ) + } + taskViewModel.clear() + } + } + + if (taskViewModel.isRunning.value == false) { + val task = requireArguments().getSerializable(TASK) as? () -> Any + if (task != null) { + taskViewModel.task = task + taskViewModel.runTask() + } + } + return dialog } companion object { const val TAG = "IndeterminateProgressDialogFragment" private const val TITLE = "Title" + private const val TASK = "Task" fun newInstance( titleId: Int, + task: () -> Any ): IndeterminateProgressDialogFragment { val dialog = IndeterminateProgressDialogFragment() val args = Bundle() args.putInt(TITLE, titleId) + args.putSerializable(TASK, task as Serializable) dialog.arguments = args return dialog } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/TaskViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/TaskViewModel.kt new file mode 100644 index 000000000..23723bceb --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/TaskViewModel.kt @@ -0,0 +1,42 @@ +package org.yuzu.yuzu_emu.model + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +class TaskViewModel : ViewModel() { + private val _result = MutableLiveData() + val result: LiveData = _result + + private val _isComplete = MutableLiveData() + val isComplete: LiveData = _isComplete + + private val _isRunning = MutableLiveData() + val isRunning: LiveData = _isRunning + + lateinit var task: () -> Any + + init { + clear() + } + + fun clear() { + _result.value = Any() + _isComplete.value = false + _isRunning.value = false + } + + fun runTask() { + if (_isRunning.value == true) return + _isRunning.value = true + + viewModelScope.launch(Dispatchers.IO) { + val res = task() + _result.postValue(res) + _isComplete.postValue(true) + } + } +} \ 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 bb8311023..2001ad704 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,7 +26,6 @@ import androidx.preference.PreferenceManager import com.google.android.material.color.MaterialColors import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.navigation.NavigationBarView -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -340,44 +339,34 @@ class MainActivity : AppCompatActivity(), ThemeProvider { File(DirectoryInitialization.userDirectory + "/nand/system/Contents/registered/") val cacheFirmwareDir = File("${cacheDir.path}/registered/") - val installingFirmwareDialog = IndeterminateProgressDialogFragment.newInstance( - R.string.firmware_installing - ) - installingFirmwareDialog.isCancelable = false - installingFirmwareDialog.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG) - - lifecycleScope.launch(Dispatchers.IO) { + val task: () -> Any = { + var messageToShow: Any try { FileUtil.unzip(inputZip, cacheFirmwareDir) val unfilteredNumOfFiles = cacheFirmwareDir.list()?.size ?: -1 val filteredNumOfFiles = cacheFirmwareDir.list(filterNCA)?.size ?: -2 if (unfilteredNumOfFiles != filteredNumOfFiles) { - withContext(Dispatchers.Main) { - installingFirmwareDialog.dismiss() - MessageDialogFragment.newInstance( - R.string.firmware_installed_failure, - R.string.firmware_installed_failure_description - ).show(supportFragmentManager, MessageDialogFragment.TAG) - } + messageToShow = MessageDialogFragment.newInstance( + R.string.firmware_installed_failure, + R.string.firmware_installed_failure_description + ) } else { firmwarePath.deleteRecursively() cacheFirmwareDir.copyRecursively(firmwarePath, true) - withContext(Dispatchers.Main) { - installingFirmwareDialog.dismiss() - Toast.makeText( - applicationContext, - getString(R.string.save_file_imported_success), - Toast.LENGTH_LONG - ).show() - } + messageToShow = getString(R.string.save_file_imported_success) } } catch (e: Exception) { - Toast.makeText(applicationContext, getString(R.string.fatal_error), Toast.LENGTH_LONG) - .show() + messageToShow = getString(R.string.fatal_error) } finally { cacheFirmwareDir.deleteRecursively() } + messageToShow } + + IndeterminateProgressDialogFragment.newInstance( + R.string.firmware_installing, + task + ).show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG) } val getAmiiboKey = From 409ff26f029861235b3f7b12400eea82c843244d Mon Sep 17 00:00:00 2001 From: PabloG02 Date: Mon, 5 Jun 2023 08:39:49 +0200 Subject: [PATCH 6/7] Address feedback --- .../fragments/HomeSettingsFragment.kt | 9 ++++++-- .../IndeterminateProgressDialogFragment.kt | 22 +++++++++---------- .../org/yuzu/yuzu_emu/model/TaskViewModel.kt | 9 ++++++-- .../org/yuzu/yuzu_emu/ui/main/MainActivity.kt | 7 +++--- 4 files changed, 28 insertions(+), 19 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 0bdbabe79..d2fa46323 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 @@ -275,7 +275,8 @@ class HomeSettingsFragment : Fragment() { private fun shareLog() { val file = DocumentFile.fromSingleUri( - mainActivity, DocumentsContract.buildDocumentUri( + mainActivity, + DocumentsContract.buildDocumentUri( DocumentProvider.AUTHORITY, "${DocumentProvider.ROOT_ID}/log/yuzu_log.txt" ) @@ -287,7 +288,11 @@ class HomeSettingsFragment : Fragment() { .putExtra(Intent.EXTRA_STREAM, file.uri) startActivity(Intent.createChooser(intent, "Share log")) } else { - Toast.makeText(requireContext(), getText(R.string.share_log_missing), Toast.LENGTH_SHORT).show() + Toast.makeText( + requireContext(), + getText(R.string.share_log_missing), + Toast.LENGTH_SHORT + ).show() } } 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 index 10a897392..c7880d8cc 100644 --- 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 @@ -1,22 +1,24 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + package org.yuzu.yuzu_emu.fragments import android.app.Dialog import android.os.Bundle import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.DialogFragment +import androidx.fragment.app.activityViewModels import androidx.lifecycle.ViewModelProvider import com.google.android.material.dialog.MaterialAlertDialogBuilder import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding import org.yuzu.yuzu_emu.model.TaskViewModel -import java.io.Serializable class IndeterminateProgressDialogFragment : DialogFragment() { - private lateinit var taskViewModel: TaskViewModel + private val taskViewModel: TaskViewModel by activityViewModels() override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - taskViewModel = ViewModelProvider(requireActivity())[TaskViewModel::class.java] - val titleId = requireArguments().getInt(TITLE) val progressBinding = DialogProgressBarBinding.inflate(layoutInflater) @@ -42,11 +44,7 @@ class IndeterminateProgressDialogFragment : DialogFragment() { } if (taskViewModel.isRunning.value == false) { - val task = requireArguments().getSerializable(TASK) as? () -> Any - if (task != null) { - taskViewModel.task = task - taskViewModel.runTask() - } + taskViewModel.runTask() } return dialog } @@ -55,18 +53,18 @@ class IndeterminateProgressDialogFragment : DialogFragment() { const val TAG = "IndeterminateProgressDialogFragment" private const val TITLE = "Title" - private const val TASK = "Task" fun newInstance( + activity: AppCompatActivity, titleId: Int, task: () -> Any ): IndeterminateProgressDialogFragment { val dialog = IndeterminateProgressDialogFragment() val args = Bundle() + ViewModelProvider(activity)[TaskViewModel::class.java].task = task args.putInt(TITLE, titleId) - args.putSerializable(TASK, task as Serializable) dialog.arguments = args return dialog } } -} \ No newline at end of file +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/TaskViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/TaskViewModel.kt index 23723bceb..27ea725a5 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/TaskViewModel.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/TaskViewModel.kt @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + package org.yuzu.yuzu_emu.model import androidx.lifecycle.LiveData @@ -30,7 +33,9 @@ class TaskViewModel : ViewModel() { } fun runTask() { - if (_isRunning.value == true) return + if (_isRunning.value == true) { + return + } _isRunning.value = true viewModelScope.launch(Dispatchers.IO) { @@ -39,4 +44,4 @@ class TaskViewModel : ViewModel() { _isComplete.postValue(true) } } -} \ 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 2001ad704..6805efb55 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 @@ -345,15 +345,15 @@ class MainActivity : AppCompatActivity(), ThemeProvider { FileUtil.unzip(inputZip, cacheFirmwareDir) val unfilteredNumOfFiles = cacheFirmwareDir.list()?.size ?: -1 val filteredNumOfFiles = cacheFirmwareDir.list(filterNCA)?.size ?: -2 - if (unfilteredNumOfFiles != filteredNumOfFiles) { - messageToShow = MessageDialogFragment.newInstance( + messageToShow = if (unfilteredNumOfFiles != filteredNumOfFiles) { + MessageDialogFragment.newInstance( R.string.firmware_installed_failure, R.string.firmware_installed_failure_description ) } else { firmwarePath.deleteRecursively() cacheFirmwareDir.copyRecursively(firmwarePath, true) - messageToShow = getString(R.string.save_file_imported_success) + getString(R.string.save_file_imported_success) } } catch (e: Exception) { messageToShow = getString(R.string.fatal_error) @@ -364,6 +364,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { } IndeterminateProgressDialogFragment.newInstance( + this, R.string.firmware_installing, task ).show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG) From e1078ec0f48186ea6947271fff0da881d91b7d1c Mon Sep 17 00:00:00 2001 From: bunnei Date: Mon, 5 Jun 2023 17:40:43 -0700 Subject: [PATCH 7/7] android: HomeSettingsFragment: Use string resource for "Share log". --- .../java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 d2fa46323..bdc337501 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 @@ -286,7 +286,7 @@ class HomeSettingsFragment : Fragment() { .setDataAndType(file.uri, FileUtil.TEXT_PLAIN) .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) .putExtra(Intent.EXTRA_STREAM, file.uri) - startActivity(Intent.createChooser(intent, "Share log")) + startActivity(Intent.createChooser(intent, getText(R.string.share_log))) } else { Toast.makeText( requireContext(),