From 82a4a67f6bc65d1b3d85b4acdf0e059d095db6a5 Mon Sep 17 00:00:00 2001 From: t895 Date: Sun, 19 Nov 2023 17:22:53 -0500 Subject: [PATCH] android: Use file picker intent for save exporter --- .../yuzu_emu/fragments/InstallableFragment.kt | 12 ++- .../org/yuzu/yuzu_emu/ui/main/MainActivity.kt | 93 +++++-------------- .../app/src/main/res/values/strings.xml | 2 + 3 files changed, 34 insertions(+), 73 deletions(-) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt index ec116ab62b..6940fc7573 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt @@ -21,6 +21,8 @@ import org.yuzu.yuzu_emu.databinding.FragmentInstallablesBinding import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.Installable import org.yuzu.yuzu_emu.ui.main.MainActivity +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter class InstallableFragment : Fragment() { private var _binding: FragmentInstallablesBinding? = null @@ -78,7 +80,15 @@ class InstallableFragment : Fragment() { R.string.manage_save_data, R.string.import_export_saves_description, install = { mainActivity.importSaves.launch(arrayOf("application/zip")) }, - export = { mainActivity.exportSave() } + export = { + mainActivity.exportSaves.launch( + "yuzu saves - ${ + LocalDateTime.now().format( + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm") + ) + }.zip" + ) + } ) } else { Installable( 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 211b7cf69d..ace5dddea6 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 @@ -6,7 +6,6 @@ package org.yuzu.yuzu_emu.ui.main import android.content.Intent import android.net.Uri import android.os.Bundle -import android.provider.DocumentsContract import android.view.View import android.view.ViewGroup.MarginLayoutParams import android.view.WindowManager @@ -20,7 +19,6 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.core.view.ViewCompat import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat -import androidx.documentfile.provider.DocumentFile import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle @@ -41,7 +39,6 @@ import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.activities.EmulationActivity import org.yuzu.yuzu_emu.databinding.ActivityMainBinding -import org.yuzu.yuzu_emu.features.DocumentProvider import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment import org.yuzu.yuzu_emu.fragments.MessageDialogFragment @@ -53,9 +50,6 @@ import org.yuzu.yuzu_emu.model.TaskViewModel import org.yuzu.yuzu_emu.utils.* import java.io.BufferedInputStream import java.io.BufferedOutputStream -import java.io.FileOutputStream -import java.time.LocalDateTime -import java.time.format.DateTimeFormatter import java.util.zip.ZipEntry import java.util.zip.ZipInputStream @@ -73,7 +67,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider { // Get first subfolder in saves folder (should be the user folder) val savesFolderRoot get() = File(savesFolder).listFiles()?.firstOrNull()?.canonicalPath ?: "" - private var lastZipCreated: File? = null override fun onCreate(savedInstanceState: Bundle?) { val splashScreen = installSplashScreen() @@ -656,75 +649,31 @@ class MainActivity : AppCompatActivity(), ThemeProvider { }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG) } - /** - * Zips the save files located in the given folder path and creates a new zip file with the current date and time. - * @return true if the zip file is successfully created, false otherwise. - */ - private fun zipSave(): Boolean { - try { - val tempFolder = File(getPublicFilesDir().canonicalPath, "temp") - tempFolder.mkdirs() - val saveFolder = File(savesFolderRoot) - val outputZipFile = File( - tempFolder, - "yuzu saves - ${ - LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) - }.zip" - ) - outputZipFile.createNewFile() - val result = FileUtil.zipFromInternalStorage( - saveFolder, - savesFolderRoot, - BufferedOutputStream(FileOutputStream(outputZipFile)) - ) - if (result == TaskState.Failed) { - return false - } - lastZipCreated = outputZipFile - } catch (e: Exception) { - return false - } - return true - } - /** * Exports the save file located in the given folder path by creating a zip file and sharing it via intent. */ - fun exportSave() { - CoroutineScope(Dispatchers.IO).launch { - val wasZipCreated = zipSave() - val lastZipFile = lastZipCreated - if (!wasZipCreated || lastZipFile == null) { - withContext(Dispatchers.Main) { - Toast.makeText( - this@MainActivity, - getString(R.string.export_save_failed), - Toast.LENGTH_LONG - ).show() - } - return@launch - } - - withContext(Dispatchers.Main) { - val file = DocumentFile.fromSingleUri( - this@MainActivity, - DocumentsContract.buildDocumentUri( - DocumentProvider.AUTHORITY, - "${DocumentProvider.ROOT_ID}/temp/${lastZipFile.name}" - ) - )!! - val intent = Intent(Intent.ACTION_SEND) - .setDataAndType(file.uri, "application/zip") - .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - .putExtra(Intent.EXTRA_STREAM, file.uri) - startForResultExportSave.launch( - Intent.createChooser( - intent, - getString(R.string.share_save_file) - ) - ) - } + val exportSaves = registerForActivityResult( + ActivityResultContracts.CreateDocument("application/zip") + ) { result -> + if (result == null) { + return@registerForActivityResult } + + IndeterminateProgressDialogFragment.newInstance( + this, + R.string.save_files_exporting, + false + ) { + val zipResult = FileUtil.zipFromInternalStorage( + File(savesFolderRoot), + savesFolderRoot, + BufferedOutputStream(contentResolver.openOutputStream(result)) + ) + return@newInstance when (zipResult) { + TaskState.Completed -> getString(R.string.export_success) + TaskState.Cancelled, TaskState.Failed -> getString(R.string.export_failed) + } + }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG) } private val startForResultExportSave = diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 98c3f20f87..471af87959 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -91,6 +91,7 @@ Manage save data Save data found. Please select an option below. Import or export save files + Exporting save files… Imported successfully Invalid save directory structure The first subfolder name must be the title ID of the game. @@ -256,6 +257,7 @@ Cancelling Install Delete + Exported successfully Select GPU driver