summaryrefslogtreecommitdiff
path: root/Source/Android/app/src/main/java
diff options
context:
space:
mode:
authorCharles Lombardo <clombardo169@gmail.com>2023-01-27 00:46:00 -0500
committerCharles Lombardo <clombardo169@gmail.com>2023-02-28 22:33:42 -0500
commitc904e068f0d52c3cdb8a659bb98a2c87da238d39 (patch)
treefc3df31f06b0daffcf03622009a73739b736f5f6 /Source/Android/app/src/main/java
parent8d1cf14565bc1a21951b1fece67d1a43790e6413 (diff)
Android: Use DialogFragments to direct UserData actions
Diffstat (limited to 'Source/Android/app/src/main/java')
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/UserDataActivity.kt81
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/NotificationDialog.kt22
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/TaskCompleteDialog.kt37
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/TaskDialog.kt70
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/UserDataImportWarningDialog.kt59
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/TaskViewModel.kt56
6 files changed, 291 insertions, 34 deletions
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/UserDataActivity.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/UserDataActivity.kt
index f110efe2eb..5046e7c7c0 100644
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/UserDataActivity.kt
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/UserDataActivity.kt
@@ -4,7 +4,6 @@ package org.dolphinemu.dolphinemu.activities
import android.content.ActivityNotFoundException
import android.content.Context
-import android.content.DialogInterface
import android.content.Intent
import android.net.Uri
import android.os.Build
@@ -14,10 +13,14 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
+import androidx.lifecycle.ViewModelProvider
import com.google.android.material.color.MaterialColors
-import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.dolphinemu.dolphinemu.R
import org.dolphinemu.dolphinemu.databinding.ActivityUserDataBinding
+import org.dolphinemu.dolphinemu.dialogs.NotificationDialog
+import org.dolphinemu.dolphinemu.dialogs.TaskDialog
+import org.dolphinemu.dolphinemu.dialogs.UserDataImportWarningDialog
+import org.dolphinemu.dolphinemu.model.TaskViewModel
import org.dolphinemu.dolphinemu.utils.*
import org.dolphinemu.dolphinemu.utils.ThemeHelper.enableScrollTint
import org.dolphinemu.dolphinemu.utils.ThemeHelper.setNavigationBarColor
@@ -28,10 +31,9 @@ import java.io.IOException
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
import java.util.zip.ZipOutputStream
-import kotlin.system.exitProcess
class UserDataActivity : AppCompatActivity() {
- private var sMustRestartApp = false
+ private lateinit var taskViewModel: TaskViewModel
private lateinit var mBinding: ActivityUserDataBinding
@@ -79,31 +81,32 @@ class UserDataActivity : AppCompatActivity() {
public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
+ taskViewModel = ViewModelProvider(this)[TaskViewModel::class.java]
if (requestCode == REQUEST_CODE_IMPORT && resultCode == RESULT_OK) {
+ val arguments = Bundle()
+ arguments.putString(
+ UserDataImportWarningDialog.KEY_URI_RESULT,
+ data!!.data!!.toString()
+ )
- MaterialAlertDialogBuilder(this)
- .setMessage(R.string.user_data_import_warning)
- .setNegativeButton(R.string.no) { dialog: DialogInterface, _: Int -> dialog.dismiss() }
- .setPositiveButton(R.string.yes) { dialog: DialogInterface, _: Int ->
- dialog.dismiss()
-
- ThreadUtil.runOnThreadAndShowResult(
- this,
- R.string.import_in_progress,
- R.string.do_not_close_app,
- { resources.getString(importUserData(data!!.data!!)) }) {
- if (sMustRestartApp) {
- exitProcess(0)
- }
- }
- }
- .show()
+ val dialog = UserDataImportWarningDialog()
+ dialog.arguments = arguments
+ dialog.show(supportFragmentManager, UserDataImportWarningDialog.TAG)
} else if (requestCode == REQUEST_CODE_EXPORT && resultCode == RESULT_OK) {
- ThreadUtil.runOnThreadAndShowResult(
- this,
- R.string.export_in_progress,
- 0
- ) { resources.getString(exportUserData(data!!.data!!)) }
+ taskViewModel.clear()
+ taskViewModel.task = {
+ val resultResource = exportUserData(data!!.data!!)
+ taskViewModel.setResult(resultResource)
+ }
+
+ val arguments = Bundle()
+ arguments.putInt(TaskDialog.KEY_TITLE, R.string.export_in_progress)
+ arguments.putInt(TaskDialog.KEY_MESSAGE, 0)
+ arguments.putBoolean(TaskDialog.KEY_CANCELLABLE, true)
+
+ val dialog = TaskDialog()
+ dialog.arguments = arguments
+ dialog.show(supportFragmentManager, TaskDialog.TAG)
}
}
@@ -118,10 +121,15 @@ class UserDataActivity : AppCompatActivity() {
} catch (e2: ActivityNotFoundException) {
// Activity not found. Perhaps it was removed by the OEM, or by some new Android version
// that didn't exist at the time of writing. Not much we can do other than tell the user.
- MaterialAlertDialogBuilder(this)
- .setMessage(R.string.user_data_open_system_file_manager_failed)
- .setPositiveButton(R.string.ok, null)
- .show()
+ val arguments = Bundle()
+ arguments.putInt(
+ NotificationDialog.KEY_MESSAGE,
+ R.string.user_data_open_system_file_manager_failed
+ )
+
+ val dialog = NotificationDialog()
+ dialog.arguments = arguments
+ dialog.show(supportFragmentManager, NotificationDialog.TAG)
}
}
}
@@ -140,17 +148,18 @@ class UserDataActivity : AppCompatActivity() {
startActivityForResult(intent, REQUEST_CODE_IMPORT)
}
- private fun importUserData(source: Uri): Int {
+ fun importUserData(source: Uri): Int {
try {
if (!isDolphinUserDataBackup(source))
return R.string.user_data_import_invalid_file
+ taskViewModel.mustRestartApp = true
+
contentResolver.openInputStream(source).use { `is` ->
ZipInputStream(`is`).use { zis ->
val userDirectory = File(DirectoryInitialization.getUserDirectory())
val userDirectoryCanonicalized = userDirectory.canonicalPath + '/'
- sMustRestartApp = true
deleteChildrenRecursively(userDirectory)
DirectoryInitialization.getGameListCache(this).delete()
@@ -262,8 +271,12 @@ class UserDataActivity : AppCompatActivity() {
private fun exportUserData(zos: ZipOutputStream, input: File, pathRelativeToRoot: File?) {
if (input.isDirectory) {
val children = input.listFiles() ?: throw IOException("Could not find directory $input")
- for (child in children) {
- exportUserData(zos, child, File(pathRelativeToRoot, child.name))
+
+ // Check if the coroutine was cancelled
+ if (!taskViewModel.cancelled) {
+ for (child in children) {
+ exportUserData(zos, child, File(pathRelativeToRoot, child.name))
+ }
}
if (children.isEmpty() && pathRelativeToRoot != null) {
zos.putNextEntry(ZipEntry(pathRelativeToRoot.path + '/'))
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/NotificationDialog.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/NotificationDialog.kt
new file mode 100644
index 0000000000..a8026a1a78
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/NotificationDialog.kt
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.dialogs
+
+import android.app.Dialog
+import android.os.Bundle
+import androidx.fragment.app.DialogFragment
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+
+class NotificationDialog : DialogFragment() {
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ val dialog = MaterialAlertDialogBuilder(requireContext())
+ .setMessage(requireArguments().getInt(KEY_MESSAGE))
+ .setPositiveButton(android.R.string.ok, null)
+ return dialog.create()
+ }
+
+ companion object {
+ const val TAG = "NotificationDialog"
+ const val KEY_MESSAGE = "message"
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/TaskCompleteDialog.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/TaskCompleteDialog.kt
new file mode 100644
index 0000000000..734921bf0f
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/TaskCompleteDialog.kt
@@ -0,0 +1,37 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.dialogs
+
+import android.app.Dialog
+import android.content.DialogInterface
+import android.os.Bundle
+import androidx.fragment.app.DialogFragment
+import androidx.lifecycle.ViewModelProvider
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import org.dolphinemu.dolphinemu.model.TaskViewModel
+
+class TaskCompleteDialog : DialogFragment() {
+ private lateinit var viewModel: TaskViewModel
+
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ viewModel = ViewModelProvider(requireActivity())[TaskViewModel::class.java]
+
+ val dialog = MaterialAlertDialogBuilder(requireContext())
+ .setMessage(requireArguments().getInt(KEY_MESSAGE))
+ .setPositiveButton(android.R.string.ok, null)
+ return dialog.create()
+ }
+
+ override fun onDismiss(dialog: DialogInterface) {
+ super.onDismiss(dialog)
+ if (viewModel.onResultDismiss != null)
+ viewModel.onResultDismiss!!.invoke()
+
+ viewModel.clear()
+ }
+
+ companion object {
+ const val TAG = "TaskCompleteDialog"
+ const val KEY_MESSAGE = "message"
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/TaskDialog.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/TaskDialog.kt
new file mode 100644
index 0000000000..f28ff70884
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/TaskDialog.kt
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.dialogs
+
+import android.app.Dialog
+import android.content.DialogInterface
+import android.os.Bundle
+import androidx.fragment.app.DialogFragment
+import androidx.lifecycle.ViewModelProvider
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import org.dolphinemu.dolphinemu.R
+import org.dolphinemu.dolphinemu.model.TaskViewModel
+
+class TaskDialog : DialogFragment() {
+ private lateinit var viewModel: TaskViewModel
+
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ viewModel = ViewModelProvider(requireActivity())[TaskViewModel::class.java]
+
+ val dialogBuilder = MaterialAlertDialogBuilder(requireContext())
+ .setTitle(requireArguments().getInt(KEY_TITLE))
+ .setView(R.layout.dialog_indeterminate_progress)
+ if (requireArguments().getBoolean(KEY_CANCELLABLE)) {
+ dialogBuilder.setCancelable(true)
+ .setNegativeButton(android.R.string.cancel) { dialog: DialogInterface, _: Int ->
+ viewModel.cancelled = true
+ dialog.dismiss()
+ }
+ }
+
+ val dialog = dialogBuilder.create()
+ dialog.setCanceledOnTouchOutside(false)
+
+ val progressMessage = requireArguments().getInt(KEY_MESSAGE)
+ if (progressMessage != 0) dialog.setMessage(resources.getString(progressMessage))
+
+ viewModel.isComplete.observe(this) { complete: Boolean ->
+ if (complete && viewModel.result.value != null) {
+ dialog.dismiss()
+ val notificationArguments = Bundle()
+ notificationArguments.putInt(
+ TaskCompleteDialog.KEY_MESSAGE,
+ viewModel.result.value!!
+ )
+
+ val taskCompleteDialog = TaskCompleteDialog()
+ taskCompleteDialog.arguments = notificationArguments
+ taskCompleteDialog.show(
+ requireActivity().supportFragmentManager,
+ TaskCompleteDialog.TAG
+ )
+ }
+ }
+
+ viewModel.runTask()
+ return dialog
+ }
+
+ override fun onCancel(dialog: DialogInterface) {
+ super.onCancel(dialog)
+ viewModel.cancelled = true
+ }
+
+ companion object {
+ const val TAG = "TaskDialog"
+ const val KEY_TITLE = "title"
+ const val KEY_MESSAGE = "message"
+ const val KEY_CANCELLABLE = "cancellable"
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/UserDataImportWarningDialog.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/UserDataImportWarningDialog.kt
new file mode 100644
index 0000000000..4ae469bb17
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/UserDataImportWarningDialog.kt
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.dialogs
+
+import android.app.Dialog
+import android.content.DialogInterface
+import android.os.Bundle
+import androidx.core.net.toUri
+import androidx.fragment.app.DialogFragment
+import androidx.lifecycle.ViewModelProvider
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import org.dolphinemu.dolphinemu.R
+import org.dolphinemu.dolphinemu.activities.UserDataActivity
+import org.dolphinemu.dolphinemu.model.TaskViewModel
+import kotlin.system.exitProcess
+
+class UserDataImportWarningDialog : DialogFragment() {
+ private lateinit var taskViewModel: TaskViewModel
+
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ taskViewModel = ViewModelProvider(requireActivity())[TaskViewModel::class.java]
+
+ val dialog = MaterialAlertDialogBuilder(requireContext())
+ .setMessage(R.string.user_data_import_warning)
+ .setNegativeButton(R.string.no) { dialog: DialogInterface, _: Int -> dialog.dismiss() }
+ .setPositiveButton(R.string.yes) { dialog: DialogInterface, _: Int ->
+ dialog.dismiss()
+
+ val taskArguments = Bundle()
+ taskArguments.putInt(TaskDialog.KEY_TITLE, R.string.import_in_progress)
+ taskArguments.putInt(TaskDialog.KEY_MESSAGE, R.string.do_not_close_app)
+ taskArguments.putBoolean(TaskDialog.KEY_CANCELLABLE, false)
+
+ taskViewModel.task = {
+ taskViewModel.setResult(
+ (requireActivity() as UserDataActivity).importUserData(
+ requireArguments().getString(KEY_URI_RESULT)!!.toUri()
+ )
+ )
+ }
+
+ taskViewModel.onResultDismiss = {
+ if (taskViewModel.mustRestartApp) {
+ exitProcess(0)
+ }
+ }
+
+ val taskDialog = TaskDialog()
+ taskDialog.arguments = taskArguments
+ taskDialog.show(requireActivity().supportFragmentManager, TaskDialog.TAG)
+ }
+ return dialog.create()
+ }
+
+ companion object {
+ const val TAG = "UserDataImportWarningDialog"
+ const val KEY_URI_RESULT = "uri"
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/TaskViewModel.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/TaskViewModel.kt
new file mode 100644
index 0000000000..084ee0d512
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/TaskViewModel.kt
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.model
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import kotlinx.coroutines.*
+
+class TaskViewModel : ViewModel() {
+ var cancelled = false
+ var mustRestartApp = false
+
+ private val _result = MutableLiveData<Int>()
+ val result: LiveData<Int> get() = _result
+
+ private val _isComplete = MutableLiveData<Boolean>()
+ val isComplete: LiveData<Boolean> get() = _isComplete
+
+ private val _isRunning = MutableLiveData<Boolean>()
+ val isRunning: LiveData<Boolean> get() = _isRunning
+
+ lateinit var task: () -> Unit
+ var onResultDismiss: (() -> Unit)? = null
+
+ init {
+ clear()
+ }
+
+ fun clear() {
+ _result.value = 0
+ _isComplete.value = false
+ cancelled = false
+ mustRestartApp = false
+ onResultDismiss = null
+ _isRunning.value = false
+ }
+
+ fun runTask() {
+ if (isRunning.value == true) return
+ _isRunning.value = true
+
+ viewModelScope.launch {
+ withContext(Dispatchers.IO) {
+ task.invoke()
+ _isRunning.postValue(false)
+ _isComplete.postValue(true)
+ }
+ }
+ }
+
+ fun setResult(result: Int) {
+ _result.postValue(result)
+ }
+}