diff options
Diffstat (limited to 'src')
3 files changed, 33 insertions, 72 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 ec116ab62..6940fc757 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 | |||
| 21 | import org.yuzu.yuzu_emu.model.HomeViewModel | 21 | import org.yuzu.yuzu_emu.model.HomeViewModel |
| 22 | import org.yuzu.yuzu_emu.model.Installable | 22 | import org.yuzu.yuzu_emu.model.Installable |
| 23 | import org.yuzu.yuzu_emu.ui.main.MainActivity | 23 | import org.yuzu.yuzu_emu.ui.main.MainActivity |
| 24 | import java.time.LocalDateTime | ||
| 25 | import java.time.format.DateTimeFormatter | ||
| 24 | 26 | ||
| 25 | class InstallableFragment : Fragment() { | 27 | class InstallableFragment : Fragment() { |
| 26 | private var _binding: FragmentInstallablesBinding? = null | 28 | private var _binding: FragmentInstallablesBinding? = null |
| @@ -78,7 +80,15 @@ class InstallableFragment : Fragment() { | |||
| 78 | R.string.manage_save_data, | 80 | R.string.manage_save_data, |
| 79 | R.string.import_export_saves_description, | 81 | R.string.import_export_saves_description, |
| 80 | install = { mainActivity.importSaves.launch(arrayOf("application/zip")) }, | 82 | install = { mainActivity.importSaves.launch(arrayOf("application/zip")) }, |
| 81 | export = { mainActivity.exportSave() } | 83 | export = { |
| 84 | mainActivity.exportSaves.launch( | ||
| 85 | "yuzu saves - ${ | ||
| 86 | LocalDateTime.now().format( | ||
| 87 | DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm") | ||
| 88 | ) | ||
| 89 | }.zip" | ||
| 90 | ) | ||
| 91 | } | ||
| 82 | ) | 92 | ) |
| 83 | } else { | 93 | } else { |
| 84 | Installable( | 94 | 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 211b7cf69..ace5dddea 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 | |||
| 6 | import android.content.Intent | 6 | import android.content.Intent |
| 7 | import android.net.Uri | 7 | import android.net.Uri |
| 8 | import android.os.Bundle | 8 | import android.os.Bundle |
| 9 | import android.provider.DocumentsContract | ||
| 10 | import android.view.View | 9 | import android.view.View |
| 11 | import android.view.ViewGroup.MarginLayoutParams | 10 | import android.view.ViewGroup.MarginLayoutParams |
| 12 | import android.view.WindowManager | 11 | import android.view.WindowManager |
| @@ -20,7 +19,6 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen | |||
| 20 | import androidx.core.view.ViewCompat | 19 | import androidx.core.view.ViewCompat |
| 21 | import androidx.core.view.WindowCompat | 20 | import androidx.core.view.WindowCompat |
| 22 | import androidx.core.view.WindowInsetsCompat | 21 | import androidx.core.view.WindowInsetsCompat |
| 23 | import androidx.documentfile.provider.DocumentFile | ||
| 24 | import androidx.lifecycle.Lifecycle | 22 | import androidx.lifecycle.Lifecycle |
| 25 | import androidx.lifecycle.lifecycleScope | 23 | import androidx.lifecycle.lifecycleScope |
| 26 | import androidx.lifecycle.repeatOnLifecycle | 24 | import androidx.lifecycle.repeatOnLifecycle |
| @@ -41,7 +39,6 @@ import org.yuzu.yuzu_emu.NativeLibrary | |||
| 41 | import org.yuzu.yuzu_emu.R | 39 | import org.yuzu.yuzu_emu.R |
| 42 | import org.yuzu.yuzu_emu.activities.EmulationActivity | 40 | import org.yuzu.yuzu_emu.activities.EmulationActivity |
| 43 | import org.yuzu.yuzu_emu.databinding.ActivityMainBinding | 41 | import org.yuzu.yuzu_emu.databinding.ActivityMainBinding |
| 44 | import org.yuzu.yuzu_emu.features.DocumentProvider | ||
| 45 | import org.yuzu.yuzu_emu.features.settings.model.Settings | 42 | import org.yuzu.yuzu_emu.features.settings.model.Settings |
| 46 | import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment | 43 | import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment |
| 47 | import org.yuzu.yuzu_emu.fragments.MessageDialogFragment | 44 | import org.yuzu.yuzu_emu.fragments.MessageDialogFragment |
| @@ -53,9 +50,6 @@ import org.yuzu.yuzu_emu.model.TaskViewModel | |||
| 53 | import org.yuzu.yuzu_emu.utils.* | 50 | import org.yuzu.yuzu_emu.utils.* |
| 54 | import java.io.BufferedInputStream | 51 | import java.io.BufferedInputStream |
| 55 | import java.io.BufferedOutputStream | 52 | import java.io.BufferedOutputStream |
| 56 | import java.io.FileOutputStream | ||
| 57 | import java.time.LocalDateTime | ||
| 58 | import java.time.format.DateTimeFormatter | ||
| 59 | import java.util.zip.ZipEntry | 53 | import java.util.zip.ZipEntry |
| 60 | import java.util.zip.ZipInputStream | 54 | import java.util.zip.ZipInputStream |
| 61 | 55 | ||
| @@ -73,7 +67,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 73 | 67 | ||
| 74 | // Get first subfolder in saves folder (should be the user folder) | 68 | // Get first subfolder in saves folder (should be the user folder) |
| 75 | val savesFolderRoot get() = File(savesFolder).listFiles()?.firstOrNull()?.canonicalPath ?: "" | 69 | val savesFolderRoot get() = File(savesFolder).listFiles()?.firstOrNull()?.canonicalPath ?: "" |
| 76 | private var lastZipCreated: File? = null | ||
| 77 | 70 | ||
| 78 | override fun onCreate(savedInstanceState: Bundle?) { | 71 | override fun onCreate(savedInstanceState: Bundle?) { |
| 79 | val splashScreen = installSplashScreen() | 72 | val splashScreen = installSplashScreen() |
| @@ -657,74 +650,30 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 657 | } | 650 | } |
| 658 | 651 | ||
| 659 | /** | 652 | /** |
| 660 | * Zips the save files located in the given folder path and creates a new zip file with the current date and time. | ||
| 661 | * @return true if the zip file is successfully created, false otherwise. | ||
| 662 | */ | ||
| 663 | private fun zipSave(): Boolean { | ||
| 664 | try { | ||
| 665 | val tempFolder = File(getPublicFilesDir().canonicalPath, "temp") | ||
| 666 | tempFolder.mkdirs() | ||
| 667 | val saveFolder = File(savesFolderRoot) | ||
| 668 | val outputZipFile = File( | ||
| 669 | tempFolder, | ||
| 670 | "yuzu saves - ${ | ||
| 671 | LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) | ||
| 672 | }.zip" | ||
| 673 | ) | ||
| 674 | outputZipFile.createNewFile() | ||
| 675 | val result = FileUtil.zipFromInternalStorage( | ||
| 676 | saveFolder, | ||
| 677 | savesFolderRoot, | ||
| 678 | BufferedOutputStream(FileOutputStream(outputZipFile)) | ||
| 679 | ) | ||
| 680 | if (result == TaskState.Failed) { | ||
| 681 | return false | ||
| 682 | } | ||
| 683 | lastZipCreated = outputZipFile | ||
| 684 | } catch (e: Exception) { | ||
| 685 | return false | ||
| 686 | } | ||
| 687 | return true | ||
| 688 | } | ||
| 689 | |||
| 690 | /** | ||
| 691 | * Exports the save file located in the given folder path by creating a zip file and sharing it via intent. | 653 | * Exports the save file located in the given folder path by creating a zip file and sharing it via intent. |
| 692 | */ | 654 | */ |
| 693 | fun exportSave() { | 655 | val exportSaves = registerForActivityResult( |
| 694 | CoroutineScope(Dispatchers.IO).launch { | 656 | ActivityResultContracts.CreateDocument("application/zip") |
| 695 | val wasZipCreated = zipSave() | 657 | ) { result -> |
| 696 | val lastZipFile = lastZipCreated | 658 | if (result == null) { |
| 697 | if (!wasZipCreated || lastZipFile == null) { | 659 | return@registerForActivityResult |
| 698 | withContext(Dispatchers.Main) { | 660 | } |
| 699 | Toast.makeText( | ||
| 700 | this@MainActivity, | ||
| 701 | getString(R.string.export_save_failed), | ||
| 702 | Toast.LENGTH_LONG | ||
| 703 | ).show() | ||
| 704 | } | ||
| 705 | return@launch | ||
| 706 | } | ||
| 707 | 661 | ||
| 708 | withContext(Dispatchers.Main) { | 662 | IndeterminateProgressDialogFragment.newInstance( |
| 709 | val file = DocumentFile.fromSingleUri( | 663 | this, |
| 710 | this@MainActivity, | 664 | R.string.save_files_exporting, |
| 711 | DocumentsContract.buildDocumentUri( | 665 | false |
| 712 | DocumentProvider.AUTHORITY, | 666 | ) { |
| 713 | "${DocumentProvider.ROOT_ID}/temp/${lastZipFile.name}" | 667 | val zipResult = FileUtil.zipFromInternalStorage( |
| 714 | ) | 668 | File(savesFolderRoot), |
| 715 | )!! | 669 | savesFolderRoot, |
| 716 | val intent = Intent(Intent.ACTION_SEND) | 670 | BufferedOutputStream(contentResolver.openOutputStream(result)) |
| 717 | .setDataAndType(file.uri, "application/zip") | 671 | ) |
| 718 | .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) | 672 | return@newInstance when (zipResult) { |
| 719 | .putExtra(Intent.EXTRA_STREAM, file.uri) | 673 | TaskState.Completed -> getString(R.string.export_success) |
| 720 | startForResultExportSave.launch( | 674 | TaskState.Cancelled, TaskState.Failed -> getString(R.string.export_failed) |
| 721 | Intent.createChooser( | ||
| 722 | intent, | ||
| 723 | getString(R.string.share_save_file) | ||
| 724 | ) | ||
| 725 | ) | ||
| 726 | } | 675 | } |
| 727 | } | 676 | }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG) |
| 728 | } | 677 | } |
| 729 | 678 | ||
| 730 | private val startForResultExportSave = | 679 | 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 98c3f20f8..471af8795 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 @@ | |||
| 91 | <string name="manage_save_data">Manage save data</string> | 91 | <string name="manage_save_data">Manage save data</string> |
| 92 | <string name="manage_save_data_description">Save data found. Please select an option below.</string> | 92 | <string name="manage_save_data_description">Save data found. Please select an option below.</string> |
| 93 | <string name="import_export_saves_description">Import or export save files</string> | 93 | <string name="import_export_saves_description">Import or export save files</string> |
| 94 | <string name="save_files_exporting">Exporting save files…</string> | ||
| 94 | <string name="save_file_imported_success">Imported successfully</string> | 95 | <string name="save_file_imported_success">Imported successfully</string> |
| 95 | <string name="save_file_invalid_zip_structure">Invalid save directory structure</string> | 96 | <string name="save_file_invalid_zip_structure">Invalid save directory structure</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="save_file_invalid_zip_structure_description">The first subfolder name must be the title ID of the game.</string> |
| @@ -256,6 +257,7 @@ | |||
| 256 | <string name="cancelling">Cancelling</string> | 257 | <string name="cancelling">Cancelling</string> |
| 257 | <string name="install">Install</string> | 258 | <string name="install">Install</string> |
| 258 | <string name="delete">Delete</string> | 259 | <string name="delete">Delete</string> |
| 260 | <string name="export_success">Exported successfully</string> | ||
| 259 | 261 | ||
| 260 | <!-- GPU driver installation --> | 262 | <!-- GPU driver installation --> |
| 261 | <string name="select_gpu_driver">Select GPU driver</string> | 263 | <string name="select_gpu_driver">Select GPU driver</string> |