From 246b6fe0a7779105c7805e0b6088331fdb00c986 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sun, 5 Oct 2025 19:44:51 +0200 Subject: [PATCH] Android: Replace deprecated startActivityForResult --- .../activities/EmulationActivity.kt | 165 ++++++++++-------- .../dolphinemu/activities/UserDataActivity.kt | 78 ++++----- .../infinitybase/ui/FigureSlotAdapter.kt | 31 +--- .../settings/model/view/DirectoryPicker.kt | 19 ++ .../settings/model/view/FilePicker.kt | 6 +- .../settings/model/view/SettingsItem.kt | 9 +- .../features/settings/ui/SettingsActivity.kt | 45 ----- .../ui/SettingsActivityResultLaunchers.kt | 60 +++++++ .../features/settings/ui/SettingsAdapter.kt | 9 +- .../features/settings/ui/SettingsFragment.kt | 42 ++--- .../settings/ui/SettingsFragmentPresenter.kt | 30 ++-- .../settings/ui/SettingsFragmentView.kt | 5 + .../ui/viewholder/FilePickerViewHolder.kt | 3 +- .../skylanders/ui/SkylanderSlotAdapter.kt | 31 +--- .../dolphinemu/fragments/ConvertFragment.kt | 21 ++- .../dolphinemu/ui/main/MainActivity.kt | 66 +------ .../dolphinemu/ui/main/MainPresenter.kt | 98 +++++++++-- .../dolphinemu/dolphinemu/ui/main/MainView.kt | 4 - .../dolphinemu/ui/main/TvMainActivity.kt | 59 ------- .../dolphinemu/utils/FileBrowserHelper.java | 6 +- 20 files changed, 374 insertions(+), 413 deletions(-) create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/DirectoryPicker.kt create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivityResultLaunchers.kt diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.kt index 5e6c8af9e95..184392f75f6 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.kt @@ -2,10 +2,10 @@ package org.dolphinemu.dolphinemu.activities -import android.annotation.SuppressLint import android.content.DialogInterface import android.content.Intent import android.graphics.Rect +import android.net.Uri import android.os.Build import android.os.Bundle import android.util.SparseIntArray @@ -16,6 +16,7 @@ import android.view.View import android.view.ViewGroup.MarginLayoutParams import android.view.WindowManager import android.widget.Toast +import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.PopupMenu import androidx.core.view.ViewCompat @@ -87,6 +88,92 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider { private lateinit var binding: ActivityEmulationBinding + private val requestChangeDisc = registerForActivityResult( + ActivityResultContracts.GetContent() + ) { uri: Uri? -> + if (uri != null) { + NativeLibrary.ChangeDisc(uri.toString()) + } + } + + val requestSkylanderFile = registerForActivityResult( + ActivityResultContracts.GetContent() + ) { uri: Uri? -> + if (uri != null) { + val slot = SkylanderConfig.loadSkylander( + skylanderSlots[skylanderSlot].portalSlot, + uri.toString() + )!! + clearSkylander(skylanderSlot) + skylanderSlots[skylanderSlot].portalSlot = slot.first!! + skylanderSlots[skylanderSlot].label = slot.second!! + skylandersBinding.figureManager.adapter!!.notifyItemChanged(skylanderSlot) + skylanderSlot = -1 + skylanderData = Skylander.BLANK_SKYLANDER + } + } + + val requestCreateSkylander = registerForActivityResult( + ActivityResultContracts.CreateDocument("*/*") + ) { uri: Uri? -> + if (uri != null) { + if (skylanderData.id != -1 && skylanderData.variant != -1) { + val slot = SkylanderConfig.createSkylander( + skylanderData.id, + skylanderData.variant, + uri.toString(), + skylanderSlots[skylanderSlot].portalSlot + ) + clearSkylander(skylanderSlot) + skylanderSlots[skylanderSlot].portalSlot = slot.first + skylanderSlots[skylanderSlot].label = slot.second + skylandersBinding.figureManager.adapter?.notifyItemChanged(skylanderSlot) + skylanderSlot = -1 + skylanderData = Skylander.BLANK_SKYLANDER + } + } + } + + val requestInfinityFigureFile = registerForActivityResult( + ActivityResultContracts.GetContent() + ) { uri: Uri? -> + if (uri != null) { + val label = InfinityConfig.loadFigure(infinityPosition, uri.toString()) + if (label != null && label != "Unknown Figure") { + clearInfinityFigure(infinityPosition) + infinityFigures[infinityPosition].label = label + infinityBinding.figureManager.adapter?.notifyItemChanged(infinityPosition) + infinityPosition = -1 + infinityFigureData = Figure.BLANK_FIGURE + } else { + MaterialAlertDialogBuilder(this) + .setTitle(R.string.incompatible_figure_selected) + .setMessage(R.string.select_compatible_figure) + .setPositiveButton(android.R.string.ok, null) + .show() + } + } + } + + val requestCreateInfinityFigure = registerForActivityResult( + ActivityResultContracts.CreateDocument("*/*") + ) { uri: Uri? -> + if (uri != null) { + if (infinityFigureData.number != -1L) { + val label = InfinityConfig.createFigure( + infinityFigureData.number, + uri.toString(), + infinityPosition + ) + clearInfinityFigure(infinityPosition) + infinityFigures[infinityPosition].label = label!! + infinityBinding.figureManager.adapter?.notifyItemChanged(infinityPosition) + infinityPosition = -1 + infinityFigureData = Figure.BLANK_FIGURE + } + } + } + override fun onCreate(savedInstanceState: Bundle?) { ThemeHelper.setTheme(this) @@ -263,70 +350,6 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider { return super.onKeyLongPress(keyCode, event) } - override fun onActivityResult(requestCode: Int, resultCode: Int, result: Intent?) { - super.onActivityResult(requestCode, resultCode, result) - // If the user picked a file, as opposed to just backing out. - if (resultCode == RESULT_OK) { - if (requestCode == REQUEST_CHANGE_DISC) { - NativeLibrary.ChangeDisc(result!!.data.toString()) - } else if (requestCode == REQUEST_SKYLANDER_FILE) { - val slot = SkylanderConfig.loadSkylander( - skylanderSlots[skylanderSlot].portalSlot, - result!!.data.toString() - )!! - clearSkylander(skylanderSlot) - skylanderSlots[skylanderSlot].portalSlot = slot.first!! - skylanderSlots[skylanderSlot].label = slot.second!! - skylandersBinding.figureManager.adapter!!.notifyItemChanged(skylanderSlot) - skylanderSlot = -1 - skylanderData = Skylander.BLANK_SKYLANDER - } else if (requestCode == REQUEST_CREATE_SKYLANDER) { - if (skylanderData.id != -1 && skylanderData.variant != -1) { - val slot = SkylanderConfig.createSkylander( - skylanderData.id, - skylanderData.variant, - result!!.data.toString(), - skylanderSlots[skylanderSlot].portalSlot - ) - clearSkylander(skylanderSlot) - skylanderSlots[skylanderSlot].portalSlot = slot.first - skylanderSlots[skylanderSlot].label = slot.second - skylandersBinding.figureManager.adapter?.notifyItemChanged(skylanderSlot) - skylanderSlot = -1 - skylanderData = Skylander.BLANK_SKYLANDER - } - } else if (requestCode == REQUEST_INFINITY_FIGURE_FILE) { - val label = InfinityConfig.loadFigure(infinityPosition, result!!.data.toString()) - if (label != null && label != "Unknown Figure") { - clearInfinityFigure(infinityPosition) - infinityFigures[infinityPosition].label = label - infinityBinding.figureManager.adapter?.notifyItemChanged(infinityPosition) - infinityPosition = -1 - infinityFigureData = Figure.BLANK_FIGURE - } else { - MaterialAlertDialogBuilder(this) - .setTitle(R.string.incompatible_figure_selected) - .setMessage(R.string.select_compatible_figure) - .setPositiveButton(android.R.string.ok, null) - .show() - } - } else if (requestCode == REQUEST_CREATE_INFINITY_FIGURE) { - if (infinityFigureData.number != -1L) { - val label = InfinityConfig.createFigure( - infinityFigureData.number, - result!!.data.toString(), - infinityPosition - ) - clearInfinityFigure(infinityPosition) - infinityFigures[infinityPosition].label = label!! - infinityBinding.figureManager.adapter?.notifyItemChanged(infinityPosition) - infinityPosition = -1 - infinityFigureData = Figure.BLANK_FIGURE - } - } - } - } - private fun enableFullscreenImmersive() { WindowCompat.setDecorFitsSystemWindows(window, false) WindowInsetsControllerCompat(window, window.decorView).let { controller -> @@ -480,12 +503,7 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider { MENU_ACTION_LOAD_SLOT4 -> NativeLibrary.LoadState(3) MENU_ACTION_LOAD_SLOT5 -> NativeLibrary.LoadState(4) MENU_ACTION_LOAD_SLOT6 -> NativeLibrary.LoadState(5) - MENU_ACTION_CHANGE_DISC -> { - val intent = Intent(Intent.ACTION_OPEN_DOCUMENT) - intent.addCategory(Intent.CATEGORY_OPENABLE) - intent.type = "*/*" - startActivityForResult(intent, REQUEST_CHANGE_DISC) - } + MENU_ACTION_CHANGE_DISC -> requestChangeDisc.launch("*/*") MENU_SET_IR_MODE -> setIRMode() MENU_ACTION_CHOOSE_DOUBLETAP -> chooseDoubleTapButton() @@ -997,11 +1015,6 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider { companion object { private const val BACKSTACK_NAME_MENU = "menu" private const val BACKSTACK_NAME_SUBMENU = "submenu" - const val REQUEST_CHANGE_DISC = 1 - const val REQUEST_SKYLANDER_FILE = 2 - const val REQUEST_CREATE_SKYLANDER = 3 - const val REQUEST_INFINITY_FIGURE_FILE = 4 - const val REQUEST_CREATE_INFINITY_FIGURE = 5 private var ignoreLaunchRequests = false 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 4639da28399..c111c99adf1 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 @@ -11,6 +11,8 @@ import android.os.Bundle import android.provider.DocumentsContract import android.view.View import androidx.activity.enableEdgeToEdge +import androidx.activity.result.ActivityResult +import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat @@ -44,6 +46,39 @@ class UserDataActivity : AppCompatActivity(), ThemeProvider { private lateinit var mBinding: ActivityUserDataBinding override var themeId: Int = 0 + private val requestImport = registerForActivityResult( + ActivityResultContracts.OpenDocument() + ) { uri: Uri? -> + if (uri != null) { + val arguments = Bundle() + arguments.putString( + UserDataImportWarningDialog.KEY_URI_RESULT, + uri.toString() + ) + + val dialog = UserDataImportWarningDialog() + dialog.arguments = arguments + dialog.show(supportFragmentManager, UserDataImportWarningDialog.TAG) + } + } + + private val requestExport = registerForActivityResult( + ActivityResultContracts.CreateDocument("application/zip") + ) { uri: Uri? -> + if (uri != null) { + taskViewModel.clear() + taskViewModel.task = { exportUserData(uri) } + + 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) + } + } override fun onCreate(savedInstanceState: Bundle?) { taskViewModel = ViewModelProvider(this)[TaskViewModel::class.java] @@ -108,34 +143,6 @@ class UserDataActivity : AppCompatActivity(), ThemeProvider { this.themeId = themeId } - public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - - if (requestCode == REQUEST_CODE_IMPORT && resultCode == RESULT_OK) { - val arguments = Bundle() - arguments.putString( - UserDataImportWarningDialog.KEY_URI_RESULT, - data!!.data!!.toString() - ) - - val dialog = UserDataImportWarningDialog() - dialog.arguments = arguments - dialog.show(supportFragmentManager, UserDataImportWarningDialog.TAG) - } else if (requestCode == REQUEST_CODE_EXPORT && resultCode == RESULT_OK) { - taskViewModel.clear() - taskViewModel.task = { exportUserData(data!!.data!!) } - - 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) - } - } - private fun confirmResetSettings() { MaterialAlertDialogBuilder(this) .setTitle(R.string.reset_dolphin_settings) @@ -216,15 +223,14 @@ class UserDataActivity : AppCompatActivity(), ThemeProvider { } private fun importUserData() { - val intent = Intent(Intent.ACTION_OPEN_DOCUMENT) - intent.type = "application/zip" - startActivityForResult(intent, REQUEST_CODE_IMPORT) + requestImport.launch(arrayOf("application/zip")) } fun importUserData(source: Uri): Int { try { - if (!isDolphinUserDataBackup(source)) + if (!isDolphinUserDataBackup(source)) { return R.string.user_data_import_invalid_file + } taskViewModel.onResultDismiss = { // Restart the app to apply the imported user data. @@ -319,10 +325,7 @@ class UserDataActivity : AppCompatActivity(), ThemeProvider { } private fun exportUserData() { - val intent = Intent(Intent.ACTION_CREATE_DOCUMENT) - intent.type = "application/zip" - intent.putExtra(Intent.EXTRA_TITLE, "dolphin-emu.zip") - startActivityForResult(intent, REQUEST_CODE_EXPORT) + requestExport.launch("dolphin-emu.zip") } private fun exportUserData(destination: Uri): Int { @@ -385,9 +388,6 @@ class UserDataActivity : AppCompatActivity(), ThemeProvider { } companion object { - private const val REQUEST_CODE_IMPORT = 0 - private const val REQUEST_CODE_EXPORT = 1 - private const val BUFFER_SIZE = 64 * 1024 @JvmStatic diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/infinitybase/ui/FigureSlotAdapter.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/infinitybase/ui/FigureSlotAdapter.kt index 512019388cd..0613274c30e 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/infinitybase/ui/FigureSlotAdapter.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/infinitybase/ui/FigureSlotAdapter.kt @@ -50,14 +50,8 @@ class FigureSlotAdapter( } holder.binding.buttonLoadFigure.setOnClickListener { - val loadFigure = Intent(Intent.ACTION_OPEN_DOCUMENT) - loadFigure.addCategory(Intent.CATEGORY_OPENABLE) - loadFigure.type = "*/*" activity.setInfinityFigureData(0, "", figure.position, position) - activity.startActivityForResult( - loadFigure, - EmulationActivity.REQUEST_INFINITY_FIGURE_FILE - ) + activity.requestInfinityFigureFile.launch("*/*") } val inflater = LayoutInflater.from(activity) @@ -110,28 +104,11 @@ class FigureSlotAdapter( .show() createDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener { if (binding.infinityNum.text.toString().isNotBlank()) { - val createFigure = Intent(Intent.ACTION_CREATE_DOCUMENT) - createFigure.addCategory(Intent.CATEGORY_OPENABLE) - createFigure.type = "*/*" val num = binding.infinityNum.text.toString().toLong() val name = InfinityConfig.LIST_FIGURES[num] - if (name != null) { - createFigure.putExtra( - Intent.EXTRA_TITLE, - "$name.bin" - ) - activity.setInfinityFigureData(num, name, figure.position, position) - } else { - createFigure.putExtra( - Intent.EXTRA_TITLE, - "Unknown(Number: $num).bin" - ) - activity.setInfinityFigureData(num, "Unknown", figure.position, position) - } - activity.startActivityForResult( - createFigure, - EmulationActivity.REQUEST_CREATE_INFINITY_FIGURE - ) + val title = if (name != null) "$name.bin" else "Unknown(Number: $num).bin" + activity.setInfinityFigureData(num, name ?: "Unknown", figure.position, position) + activity.requestCreateInfinityFigure.launch(title) createDialog.dismiss() } else { Toast.makeText( diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/DirectoryPicker.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/DirectoryPicker.kt new file mode 100644 index 00000000000..415241e38cb --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/DirectoryPicker.kt @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.model.view + +import android.content.Context +import android.content.Intent +import androidx.activity.result.ActivityResultLauncher +import org.dolphinemu.dolphinemu.features.settings.model.AbstractStringSetting + +class DirectoryPicker( + context: Context, + setting: AbstractStringSetting, + titleId: Int, + descriptionId: Int, + launcher: ActivityResultLauncher, + defaultPathRelativeToUserDirectory: String? +) : FilePicker(context, setting, titleId, descriptionId, launcher, defaultPathRelativeToUserDirectory) { + override val type: Int = TYPE_DIRECTORY_PICKER +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/FilePicker.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/FilePicker.kt index 2b5bd70162e..e85cc7dc0ba 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/FilePicker.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/FilePicker.kt @@ -3,15 +3,17 @@ package org.dolphinemu.dolphinemu.features.settings.model.view import android.content.Context +import android.content.Intent +import androidx.activity.result.ActivityResultLauncher import org.dolphinemu.dolphinemu.features.settings.model.AbstractStringSetting import org.dolphinemu.dolphinemu.features.settings.model.Settings -class FilePicker( +open class FilePicker( context: Context, override var setting: AbstractStringSetting, titleId: Int, descriptionId: Int, - val requestType: Int, + val launcher: ActivityResultLauncher, val defaultPathRelativeToUserDirectory: String? ) : SettingsItem(context, titleId, descriptionId) { override val type: Int = TYPE_FILE_PICKER diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SettingsItem.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SettingsItem.kt index 01c4796e683..4d41676a144 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SettingsItem.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SettingsItem.kt @@ -81,9 +81,10 @@ abstract class SettingsItem { const val TYPE_STRING_SINGLE_CHOICE = 6 const val TYPE_SINGLE_CHOICE_DYNAMIC_DESCRIPTIONS = 8 const val TYPE_FILE_PICKER = 9 - const val TYPE_RUN_RUNNABLE = 10 - const val TYPE_STRING = 11 - const val TYPE_HYPERLINK_HEADER = 12 - const val TYPE_DATETIME_CHOICE = 13 + const val TYPE_DIRECTORY_PICKER = 10 + const val TYPE_RUN_RUNNABLE = 11 + const val TYPE_STRING = 12 + const val TYPE_HYPERLINK_HEADER = 13 + const val TYPE_DATETIME_CHOICE = 14 } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivity.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivity.kt index 9b252b5cdd5..68f957a6266 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivity.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivity.kt @@ -1,15 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-or-later -// GPU driver implementation partially based on: -// SPDX-FileCopyrightText: 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - package org.dolphinemu.dolphinemu.features.settings.ui import android.content.Context import android.content.DialogInterface import android.content.Intent -import android.net.Uri import android.os.Bundle import android.view.KeyEvent import android.view.Menu @@ -33,7 +28,6 @@ import org.dolphinemu.dolphinemu.features.settings.model.Settings import org.dolphinemu.dolphinemu.features.settings.ui.SettingsFragment.Companion.newInstance import org.dolphinemu.dolphinemu.ui.main.MainPresenter import org.dolphinemu.dolphinemu.ui.main.ThemeProvider -import org.dolphinemu.dolphinemu.utils.FileBrowserHelper import org.dolphinemu.dolphinemu.utils.InsetsHelper import org.dolphinemu.dolphinemu.utils.SerializableHelper.serializable import org.dolphinemu.dolphinemu.utils.ThemeHelper.enableScrollTint @@ -177,40 +171,6 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView, ThemeProvide return duration != 0f && transition != 0f } - override fun onActivityResult(requestCode: Int, resultCode: Int, result: Intent?) { - super.onActivityResult(requestCode, resultCode, result) - - // If the user picked a file, as opposed to just backing out. - if (resultCode != RESULT_OK) { - return - } - - when (requestCode) { - MainPresenter.REQUEST_DIRECTORY -> { - val path = FileBrowserHelper.getSelectedPath(result) - fragment!!.adapter!!.onFilePickerConfirmation(path!!) - } - - MainPresenter.REQUEST_GAME_FILE, - MainPresenter.REQUEST_SD_FILE, - MainPresenter.REQUEST_WAD_FILE, - MainPresenter.REQUEST_WII_SAVE_FILE, - MainPresenter.REQUEST_NAND_BIN_FILE -> { - val uri = canonicalizeIfPossible(result!!.data!!) - val validExtensions: Set = - if (requestCode == MainPresenter.REQUEST_GAME_FILE) FileBrowserHelper.GAME_EXTENSIONS else FileBrowserHelper.RAW_EXTENSION - var flags = Intent.FLAG_GRANT_READ_URI_PERMISSION - if (requestCode != MainPresenter.REQUEST_GAME_FILE) flags = - flags or Intent.FLAG_GRANT_WRITE_URI_PERMISSION - val takeFlags = flags and result.flags - FileBrowserHelper.runAfterExtensionCheck(this, uri, validExtensions) { - contentResolver.takePersistableUriPermission(uri, takeFlags) - fragment!!.adapter!!.onFilePickerConfirmation(uri.toString()) - } - } - } - } - override fun dispatchKeyEvent(event: KeyEvent): Boolean { ControllerInterface.dispatchKeyEvent(event) return super.dispatchKeyEvent(event) @@ -221,11 +181,6 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView, ThemeProvide return super.dispatchGenericMotionEvent(event) } - private fun canonicalizeIfPossible(uri: Uri): Uri { - val canonicalizedUri = contentResolver.canonicalize(uri) - return canonicalizedUri ?: uri - } - override fun showLoading() { if (dialog == null) { dialog = MaterialAlertDialogBuilder(this) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivityResultLaunchers.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivityResultLaunchers.kt new file mode 100644 index 00000000000..9c13be1b19a --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivityResultLaunchers.kt @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.settings.ui + +import android.app.Activity +import android.content.Intent +import androidx.activity.result.ActivityResult +import androidx.activity.result.contract.ActivityResultContracts +import androidx.fragment.app.Fragment +import org.dolphinemu.dolphinemu.utils.FileBrowserHelper + +class SettingsActivityResultLaunchers( + private val fragment: Fragment, private val getAdapter: () -> SettingsAdapter? +) { + val requestDirectory = fragment.registerForActivityResult( + ActivityResultContracts.StartActivityForResult() + ) { result: ActivityResult -> + val intent = result.data + if (result.resultCode == Activity.RESULT_OK && intent != null) { + val path = FileBrowserHelper.getSelectedPath(intent) + if (path != null) { + getAdapter()?.onFilePickerConfirmation(path) + } + } + } + + val requestGameFile = fragment.registerForActivityResult( + ActivityResultContracts.StartActivityForResult() + ) { result: ActivityResult -> + onFileResult( + result, + FileBrowserHelper.GAME_EXTENSIONS, + Intent.FLAG_GRANT_READ_URI_PERMISSION + ) + } + + val requestRawFile = fragment.registerForActivityResult( + ActivityResultContracts.StartActivityForResult() + ) { result: ActivityResult -> + onFileResult( + result, + FileBrowserHelper.RAW_EXTENSION, + Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION + ) + } + + private fun onFileResult(result: ActivityResult, validExtensions: Set, flags: Int) { + val intent = result.data + val uri = intent?.data + val context = fragment.requireContext() + if (result.resultCode == Activity.RESULT_OK && uri != null) { + val canonicalizedUri = context.contentResolver.canonicalize(uri) ?: uri + val takeFlags = flags and intent.flags + FileBrowserHelper.runAfterExtensionCheck(context, canonicalizedUri, validExtensions) { + context.contentResolver.takePersistableUriPermission(canonicalizedUri, takeFlags) + getAdapter()?.onFilePickerConfirmation(canonicalizedUri.toString()) + } + } + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.kt index 3ce937e6ab8..ae236d37642 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.kt @@ -94,7 +94,8 @@ class SettingsAdapter( ListItemMappingBinding.inflate(inflater, parent, false), this ) - SettingsItem.TYPE_FILE_PICKER -> FilePickerViewHolder( + SettingsItem.TYPE_FILE_PICKER, + SettingsItem.TYPE_DIRECTORY_PICKER -> FilePickerViewHolder( ListItemSettingBinding.inflate(inflater, parent, false), this ) @@ -357,6 +358,7 @@ class SettingsAdapter( fun onFilePickerDirectoryClick(item: SettingsItem, position: Int) { clickedItem = item clickedPosition = position + val directoryPicker = item as DirectoryPicker if (!PermissionsHandler.isExternalStorageLegacy()) { MaterialAlertDialogBuilder(context) @@ -364,10 +366,11 @@ class SettingsAdapter( .setPositiveButton(R.string.ok) { dialog: DialogInterface, _: Int -> dialog.dismiss() } .show() } else { - FileBrowserHelper.openDirectoryPicker( + val intent = FileBrowserHelper.createDirectoryPickerIntent( fragmentView.fragmentActivity, FileBrowserHelper.GAME_EXTENSIONS ) + directoryPicker.launcher.launch(intent) } } @@ -383,7 +386,7 @@ class SettingsAdapter( intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, filePicker.getSelectedValue()) } - fragmentView.fragmentActivity.startActivityForResult(intent, filePicker.requestType) + filePicker.launcher.launch(intent) } fun onDateTimeClick(item: DateTimeChoiceSetting, position: Int) { diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragment.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragment.kt index 991d3c3c2b9..2992dd32113 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragment.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragment.kt @@ -1,17 +1,22 @@ // SPDX-License-Identifier: GPL-2.0-or-later +// GPU driver implementation partially based on: +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + package org.dolphinemu.dolphinemu.features.settings.ui +import android.app.Activity import android.content.Context import android.content.DialogInterface import android.content.Intent -import android.net.Uri import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.Toast -import androidx.appcompat.app.AppCompatActivity +import androidx.activity.result.ActivityResult +import androidx.activity.result.contract.ActivityResultContracts import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.updatePadding @@ -26,7 +31,6 @@ import org.dolphinemu.dolphinemu.R import org.dolphinemu.dolphinemu.databinding.FragmentSettingsBinding import org.dolphinemu.dolphinemu.features.settings.model.Settings import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem -import org.dolphinemu.dolphinemu.ui.main.MainPresenter import org.dolphinemu.dolphinemu.utils.GpuDriverInstallResult import org.dolphinemu.dolphinemu.utils.SerializableHelper.serializable import java.util.* @@ -43,10 +47,22 @@ class SettingsFragment : Fragment(), SettingsFragmentView { override var adapter: SettingsAdapter? = null + override val activityResultLaunchers: SettingsActivityResultLaunchers = + SettingsActivityResultLaunchers(this) { adapter } + private var oldControllerSettingsWarningHeight = 0 private var binding: FragmentSettingsBinding? = null + private val requestGpuDriver = registerForActivityResult( + ActivityResultContracts.StartActivityForResult() + ) { result: ActivityResult -> + val uri = result.data?.data + if (result.resultCode == Activity.RESULT_OK && uri != null) { + presenter.installDriver(uri) + } + } + override fun onAttach(context: Context) { super.onAttach(context) @@ -185,7 +201,7 @@ class SettingsFragment : Fragment(), SettingsFragmentView { if (presenter.gpuDriver == null) { return } - val msg = "${presenter!!.gpuDriver!!.name} ${presenter!!.gpuDriver!!.driverVersion}" + val msg = "${presenter.gpuDriver!!.name} ${presenter.gpuDriver!!.driverVersion}" MaterialAlertDialogBuilder(requireContext()) .setTitle(getString(R.string.gpu_driver_dialog_title)) @@ -209,23 +225,7 @@ class SettingsFragment : Fragment(), SettingsFragmentView { addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) type = "application/zip" } - startActivityForResult(intent, MainPresenter.REQUEST_GPU_DRIVER) - } - - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - - // If the user picked a file, as opposed to just backing out. - if (resultCode != AppCompatActivity.RESULT_OK) { - return - } - - if (requestCode != MainPresenter.REQUEST_GPU_DRIVER) { - return - } - - val uri = data?.data ?: return - presenter.installDriver(uri) + requestGpuDriver.launch(intent) } override fun onDriverInstallDone(result: GpuDriverInstallResult) { diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.kt index 25acccb2fc8..977f2aeff68 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.kt @@ -32,9 +32,7 @@ import org.dolphinemu.dolphinemu.features.input.ui.ProfileDialogPresenter import org.dolphinemu.dolphinemu.features.settings.model.* import org.dolphinemu.dolphinemu.features.settings.model.view.* import org.dolphinemu.dolphinemu.model.GpuDriverMetadata -import org.dolphinemu.dolphinemu.ui.main.MainPresenter import org.dolphinemu.dolphinemu.utils.* -import java.util.* import kotlin.collections.ArrayList import kotlin.math.ceil import kotlin.math.floor @@ -584,57 +582,57 @@ class SettingsFragmentPresenter( StringSetting.MAIN_DEFAULT_ISO, R.string.default_ISO, 0, - MainPresenter.REQUEST_GAME_FILE, + fragmentView.activityResultLaunchers.requestGameFile, null ) ) sl.add( - FilePicker( + DirectoryPicker( context, StringSetting.MAIN_FS_PATH, R.string.wii_NAND_root, 0, - MainPresenter.REQUEST_DIRECTORY, + fragmentView.activityResultLaunchers.requestDirectory, "/Wii" ) ) sl.add( - FilePicker( + DirectoryPicker( context, StringSetting.MAIN_DUMP_PATH, R.string.dump_path, 0, - MainPresenter.REQUEST_DIRECTORY, + fragmentView.activityResultLaunchers.requestDirectory, "/Dump" ) ) sl.add( - FilePicker( + DirectoryPicker( context, StringSetting.MAIN_LOAD_PATH, R.string.load_path, 0, - MainPresenter.REQUEST_DIRECTORY, + fragmentView.activityResultLaunchers.requestDirectory, "/Load" ) ) sl.add( - FilePicker( + DirectoryPicker( context, StringSetting.MAIN_RESOURCEPACK_PATH, R.string.resource_pack_path, 0, - MainPresenter.REQUEST_DIRECTORY, + fragmentView.activityResultLaunchers.requestDirectory, "/ResourcePacks" ) ) sl.add( - FilePicker( + DirectoryPicker( context, StringSetting.MAIN_WFS_PATH, R.string.wfs_path, 0, - MainPresenter.REQUEST_DIRECTORY, + fragmentView.activityResultLaunchers.requestDirectory, "/WFS" ) ) @@ -784,17 +782,17 @@ class SettingsFragmentPresenter( StringSetting.MAIN_WII_SD_CARD_IMAGE_PATH, R.string.wii_sd_card_path, 0, - MainPresenter.REQUEST_SD_FILE, + fragmentView.activityResultLaunchers.requestRawFile, "/Load/WiiSD.raw" ) ) sl.add( - FilePicker( + DirectoryPicker( context, StringSetting.MAIN_WII_SD_CARD_SYNC_FOLDER_PATH, R.string.wii_sd_sync_folder, 0, - MainPresenter.REQUEST_DIRECTORY, + fragmentView.activityResultLaunchers.requestDirectory, "/Load/WiiSDSync/" ) ) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentView.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentView.kt index b0ad3e28b13..c0b39400a78 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentView.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentView.kt @@ -38,6 +38,11 @@ interface SettingsFragmentView { */ val adapter: SettingsAdapter? + /** + * The activity result launchers to use for asking the user to pick a directory or file. + */ + val activityResultLaunchers: SettingsActivityResultLaunchers + /** * Tell the Fragment to tell the containing Activity to show a new * Fragment containing a submenu of settings. diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/FilePickerViewHolder.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/FilePickerViewHolder.kt index ff6246d292b..e4bf3338315 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/FilePickerViewHolder.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/FilePickerViewHolder.kt @@ -9,7 +9,6 @@ import org.dolphinemu.dolphinemu.databinding.ListItemSettingBinding import org.dolphinemu.dolphinemu.features.settings.model.view.FilePicker import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter -import org.dolphinemu.dolphinemu.ui.main.MainPresenter import org.dolphinemu.dolphinemu.utils.DirectoryInitialization import org.dolphinemu.dolphinemu.utils.FileBrowserHelper @@ -57,7 +56,7 @@ class FilePickerViewHolder( } val position = bindingAdapterPosition - if (setting.requestType == MainPresenter.REQUEST_DIRECTORY) { + if (setting.type == SettingsItem.TYPE_DIRECTORY_PICKER) { adapter.onFilePickerDirectoryClick(setting, position) } else { adapter.onFilePickerFileClick(setting, position) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/skylanders/ui/SkylanderSlotAdapter.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/skylanders/ui/SkylanderSlotAdapter.kt index 295d1abf1ae..ed3dde3e400 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/skylanders/ui/SkylanderSlotAdapter.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/skylanders/ui/SkylanderSlotAdapter.kt @@ -46,14 +46,8 @@ class SkylanderSlotAdapter( } holder.binding.buttonLoadFigure.setOnClickListener { - val loadSkylander = Intent(Intent.ACTION_OPEN_DOCUMENT) - loadSkylander.addCategory(Intent.CATEGORY_OPENABLE) - loadSkylander.type = "*/*" activity.setSkylanderData(0, 0, "", position) - activity.startActivityForResult( - loadSkylander, - EmulationActivity.REQUEST_SKYLANDER_FILE - ) + activity.requestSkylanderFile.launch("*/*") } val inflater = LayoutInflater.from(activity) @@ -85,29 +79,12 @@ class SkylanderSlotAdapter( if (binding.skylanderId.text.toString().isNotBlank() && binding.skylanderVar.text.toString().isNotBlank() ) { - val createSkylander = Intent(Intent.ACTION_CREATE_DOCUMENT) - createSkylander.addCategory(Intent.CATEGORY_OPENABLE) - createSkylander.type = "*/*" val id = binding.skylanderId.text.toString().toInt() val variant = binding.skylanderVar.text.toString().toInt() val name = SkylanderConfig.LIST_SKYLANDERS[SkylanderPair(id, variant)] - if (name != null) { - createSkylander.putExtra( - Intent.EXTRA_TITLE, - "$name.sky" - ) - activity.setSkylanderData(id, variant, name, position) - } else { - createSkylander.putExtra( - Intent.EXTRA_TITLE, - "Unknown(ID: " + id + "Variant: " + variant + ").sky" - ) - activity.setSkylanderData(id, variant, "Unknown", position) - } - activity.startActivityForResult( - createSkylander, - EmulationActivity.REQUEST_CREATE_SKYLANDER - ) + val title = if (name != null) "$name.sky" else "Unknown(ID: $id Variant: $variant).sky" + activity.setSkylanderData(id, variant, name ?: "Unknown", position) + activity.requestCreateSkylander.launch(title) createDialog.dismiss() } else { Toast.makeText( diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/ConvertFragment.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/ConvertFragment.kt index 0f5760a210a..5f171a872b6 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/ConvertFragment.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/ConvertFragment.kt @@ -15,6 +15,8 @@ import android.view.ViewGroup import android.widget.AdapterView import android.widget.AdapterView.OnItemClickListener import android.widget.ArrayAdapter +import androidx.activity.result.ActivityResult +import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.StringRes import androidx.fragment.app.Fragment import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -79,6 +81,15 @@ class ConvertFragment : Fragment(), View.OnClickListener { private var _binding: FragmentConvertBinding? = null val binding get() = _binding!! + private val requestSaveFile = registerForActivityResult( + ActivityResultContracts.StartActivityForResult() + ) { result: ActivityResult -> + val uri = result.data?.data + if (result.resultCode == Activity.RESULT_OK && uri != null) { + convert(uri.toString()) + } + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) gameFile = GameFileCacheManager.addOrGet(requireArguments().getString(ARG_GAME_PATH)) @@ -429,13 +440,7 @@ class ConvertFragment : Fragment(), View.OnClickListener { DocumentsContract.EXTRA_INITIAL_URI, originalPath ) - startActivityForResult(intent, REQUEST_CODE_SAVE_FILE) - } - - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (requestCode == REQUEST_CODE_SAVE_FILE && resultCode == Activity.RESULT_OK) { - convert(data!!.data.toString()) - } + requestSaveFile.launch(intent) } private fun convert(outPath: String) { @@ -515,8 +520,6 @@ class ConvertFragment : Fragment(), View.OnClickListener { private const val KEY_COMPRESSION_LEVEL = "convert_compression_level" private const val KEY_REMOVE_JUNK_DATA = "remove_junk_data" - private const val REQUEST_CODE_SAVE_FILE = 0 - private const val BLOB_TYPE_ISO = 0 private const val BLOB_TYPE_GCZ = 3 private const val BLOB_TYPE_WIA = 7 diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainActivity.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainActivity.kt index a759ad2d146..761c6dbfef0 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainActivity.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainActivity.kt @@ -2,7 +2,6 @@ package org.dolphinemu.dolphinemu.ui.main -import android.content.Intent import android.content.pm.PackageManager import android.os.Bundle import android.view.Menu @@ -18,7 +17,6 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener import com.google.android.material.appbar.AppBarLayout import com.google.android.material.tabs.TabLayout import org.dolphinemu.dolphinemu.R -import org.dolphinemu.dolphinemu.activities.EmulationActivity import org.dolphinemu.dolphinemu.adapters.PlatformPagerAdapter import org.dolphinemu.dolphinemu.databinding.ActivityMainBinding import org.dolphinemu.dolphinemu.features.settings.model.IntSetting @@ -31,7 +29,6 @@ import org.dolphinemu.dolphinemu.ui.platform.PlatformGamesView import org.dolphinemu.dolphinemu.ui.platform.PlatformTab import org.dolphinemu.dolphinemu.utils.AfterDirectoryInitializationRunner import org.dolphinemu.dolphinemu.utils.DirectoryInitialization -import org.dolphinemu.dolphinemu.utils.FileBrowserHelper import org.dolphinemu.dolphinemu.utils.InsetsHelper import org.dolphinemu.dolphinemu.utils.PermissionsHandler import org.dolphinemu.dolphinemu.utils.StartupHandler @@ -65,7 +62,11 @@ class MainActivity : AppCompatActivity(), MainView, OnRefreshListener, ThemeProv setSupportActionBar(binding.toolbarMain) // Set up the FAB. - binding.buttonAddDirectory.setOnClickListener { presenter.onFabClick() } + binding.buttonAddDirectory.setOnClickListener { + AfterDirectoryInitializationRunner().runWithLifecycle(this) { + presenter.launchFileListActivity() + } + } binding.appbarMain.addOnOffsetChangedListener { appBarLayout: AppBarLayout, verticalOffset: Int -> if (verticalOffset == 0) { binding.buttonAddDirectory.extend() @@ -145,63 +146,6 @@ class MainActivity : AppCompatActivity(), MainView, OnRefreshListener, ThemeProv SettingsActivity.launch(this, menuTag) } - override fun launchFileListActivity() { - if (DirectoryInitialization.preferOldFolderPicker(this)) { - FileBrowserHelper.openDirectoryPicker(this, FileBrowserHelper.GAME_EXTENSIONS) - } else { - val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) - startActivityForResult(intent, MainPresenter.REQUEST_DIRECTORY) - } - } - - override fun launchOpenFileActivity(requestCode: Int) { - val intent = Intent(Intent.ACTION_OPEN_DOCUMENT) - intent.addCategory(Intent.CATEGORY_OPENABLE) - intent.type = "*/*" - startActivityForResult(intent, requestCode) - } - - /** - * @param requestCode An int describing whether the Activity that is returning did so successfully. - * @param resultCode An int describing what Activity is giving us this callback. - * @param result The information the returning Activity is providing us. - */ - override fun onActivityResult(requestCode: Int, resultCode: Int, result: Intent?) { - super.onActivityResult(requestCode, resultCode, result) - - // If the user picked a file, as opposed to just backing out. - if (resultCode == RESULT_OK) { - val uri = result!!.data - when (requestCode) { - MainPresenter.REQUEST_DIRECTORY -> { - if (DirectoryInitialization.preferOldFolderPicker(this)) { - presenter.onDirectorySelected(FileBrowserHelper.getSelectedPath(result)) - } else { - presenter.onDirectorySelected(result) - } - } - - MainPresenter.REQUEST_GAME_FILE -> FileBrowserHelper.runAfterExtensionCheck( - this, uri, FileBrowserHelper.GAME_LIKE_EXTENSIONS - ) { EmulationActivity.launch(this, result.data.toString(), false) } - - MainPresenter.REQUEST_WAD_FILE -> FileBrowserHelper.runAfterExtensionCheck( - this, uri, FileBrowserHelper.WAD_EXTENSION - ) { presenter.installWAD(result.data.toString()) } - - MainPresenter.REQUEST_WII_SAVE_FILE -> FileBrowserHelper.runAfterExtensionCheck( - this, uri, FileBrowserHelper.BIN_EXTENSION - ) { presenter.importWiiSave(result.data.toString()) } - - MainPresenter.REQUEST_NAND_BIN_FILE -> FileBrowserHelper.runAfterExtensionCheck( - this, uri, FileBrowserHelper.BIN_EXTENSION - ) { presenter.importNANDBin(result.data.toString()) } - } - } else { - MainPresenter.skipRescanningLibrary() - } - } - override fun onRequestPermissionsResult( requestCode: Int, permissions: Array, diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainPresenter.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainPresenter.kt index e77b6940967..cea17578d40 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainPresenter.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainPresenter.kt @@ -2,9 +2,13 @@ package org.dolphinemu.dolphinemu.ui.main +import android.app.Activity import android.content.DialogInterface import android.content.Intent +import android.net.Uri import androidx.activity.ComponentActivity +import androidx.activity.result.ActivityResult +import androidx.activity.result.contract.ActivityResultContracts import androidx.fragment.app.FragmentActivity import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProvider @@ -35,6 +39,69 @@ import java.util.concurrent.ExecutionException class MainPresenter(private val mainView: MainView, private val activity: FragmentActivity) { private var dirToAdd: String? = null + private val requestDirectory = activity.registerForActivityResult( + ActivityResultContracts.StartActivityForResult() + ) { result: ActivityResult -> + val intent = result.data + if (result.resultCode == Activity.RESULT_OK && intent != null) { + if (DirectoryInitialization.preferOldFolderPicker(activity)) { + onDirectorySelected(FileBrowserHelper.getSelectedPath(intent)) + } else { + onDirectorySelected(intent) + } + } else { + skipRescanningLibrary() + } + } + + private val requestGameFile = activity.registerForActivityResult( + ActivityResultContracts.GetContent() + ) { uri: Uri? -> + if (uri != null) { + FileBrowserHelper.runAfterExtensionCheck( + activity, uri, FileBrowserHelper.GAME_LIKE_EXTENSIONS + ) { EmulationActivity.launch(activity, uri.toString(), false) } + } else { + skipRescanningLibrary() + } + } + + private val requestWadFile = activity.registerForActivityResult( + ActivityResultContracts.GetContent() + ) { uri: Uri? -> + if (uri != null) { + FileBrowserHelper.runAfterExtensionCheck( + activity, uri, FileBrowserHelper.WAD_EXTENSION + ) { installWAD(uri.toString()) } + } else { + skipRescanningLibrary() + } + } + + private val requestWiiSaveFile = activity.registerForActivityResult( + ActivityResultContracts.GetContent() + ) { uri: Uri? -> + if (uri != null) { + FileBrowserHelper.runAfterExtensionCheck( + activity, uri, FileBrowserHelper.BIN_EXTENSION + ) { importWiiSave(uri.toString()) } + } else { + skipRescanningLibrary() + } + } + + private val requestNandBinFile = activity.registerForActivityResult( + ActivityResultContracts.GetContent() + ) { uri: Uri? -> + if (uri != null) { + FileBrowserHelper.runAfterExtensionCheck( + activity, uri, FileBrowserHelper.BIN_EXTENSION + ) { importNANDBin(uri.toString()) } + } else { + skipRescanningLibrary() + } + } + fun onCreate() { // Ask the user to grant write permission if relevant and not already granted if (DirectoryInitialization.isWaitingForWriteAccess(activity)) @@ -50,8 +117,17 @@ class MainPresenter(private val mainView: MainView, private val activity: Fragme GameFileCacheManager.isRescanning().observe(activity, refreshObserver) } - fun onFabClick() { - AfterDirectoryInitializationRunner().runWithLifecycle(activity) { mainView.launchFileListActivity() } + fun launchFileListActivity() { + val intent = + if (DirectoryInitialization.preferOldFolderPicker(activity)) { + FileBrowserHelper.createDirectoryPickerIntent( + activity, + FileBrowserHelper.GAME_EXTENSIONS, + ) + } else { + Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) + } + requestDirectory.launch(intent) } fun handleOptionSelection(itemId: Int, activity: ComponentActivity): Boolean = @@ -73,12 +149,12 @@ class MainPresenter(private val mainView: MainView, private val activity: Fragme } R.id.button_add_directory -> { - AfterDirectoryInitializationRunner().runWithLifecycle(activity) { mainView.launchFileListActivity() } + AfterDirectoryInitializationRunner().runWithLifecycle(activity) { launchFileListActivity() } true } R.id.menu_open_file -> { - mainView.launchOpenFileActivity(REQUEST_GAME_FILE) + requestGameFile.launch("*/*") true } @@ -95,21 +171,21 @@ class MainPresenter(private val mainView: MainView, private val activity: Fragme R.id.menu_install_wad -> { AfterDirectoryInitializationRunner().runWithLifecycle( activity - ) { mainView.launchOpenFileActivity(REQUEST_WAD_FILE) } + ) { requestWadFile.launch("*/*") } true } R.id.menu_import_wii_save -> { AfterDirectoryInitializationRunner().runWithLifecycle( activity - ) { mainView.launchOpenFileActivity(REQUEST_WII_SAVE_FILE) } + ) { requestWiiSaveFile.launch("*/*") } true } R.id.menu_import_nand_backup -> { AfterDirectoryInitializationRunner().runWithLifecycle( activity - ) { mainView.launchOpenFileActivity(REQUEST_NAND_BIN_FILE) } + ) { requestNandBinFile.launch("*/*") } true } @@ -280,14 +356,6 @@ class MainPresenter(private val mainView: MainView, private val activity: Fragme } companion object { - const val REQUEST_DIRECTORY = 1 - const val REQUEST_GAME_FILE = 2 - const val REQUEST_SD_FILE = 3 - const val REQUEST_WAD_FILE = 4 - const val REQUEST_WII_SAVE_FILE = 5 - const val REQUEST_NAND_BIN_FILE = 6 - const val REQUEST_GPU_DRIVER = 7 - private var shouldRescanLibrary = true @JvmStatic diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainView.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainView.kt index 3892ee7599e..f43d2452fd9 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainView.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainView.kt @@ -20,10 +20,6 @@ interface MainView { fun launchSettingsActivity(menuTag: MenuTag?) - fun launchFileListActivity() - - fun launchOpenFileActivity(requestCode: Int) - /** * Shows or hides the loading indicator. */ diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/TvMainActivity.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/TvMainActivity.kt index 945d474d723..09bacd201b8 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/TvMainActivity.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/TvMainActivity.kt @@ -125,22 +125,6 @@ class TvMainActivity : FragmentActivity(), MainView, OnRefreshListener { SettingsActivity.launch(this, menuTag) } - override fun launchFileListActivity() { - if (DirectoryInitialization.preferOldFolderPicker(this)) { - FileBrowserHelper.openDirectoryPicker(this, FileBrowserHelper.GAME_EXTENSIONS) - } else { - val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) - startActivityForResult(intent, MainPresenter.REQUEST_DIRECTORY) - } - } - - override fun launchOpenFileActivity(requestCode: Int) { - val intent = Intent(Intent.ACTION_OPEN_DOCUMENT) - intent.addCategory(Intent.CATEGORY_OPENABLE) - intent.type = "*/*" - startActivityForResult(intent, requestCode) - } - /** * Shows or hides the loading indicator. */ @@ -165,49 +149,6 @@ class TvMainActivity : FragmentActivity(), MainView, OnRefreshListener { GridOptionDialogFragment().show(supportFragmentManager, "gridOptions") } - /** - * Callback from AddDirectoryActivity. Applies any changes necessary to the GameGridActivity. - * - * @param requestCode An int describing whether the Activity that is returning did so successfully. - * @param resultCode An int describing what Activity is giving us this callback. - * @param result The information the returning Activity is providing us. - */ - override fun onActivityResult(requestCode: Int, resultCode: Int, result: Intent?) { - super.onActivityResult(requestCode, resultCode, result) - - // If the user picked a file, as opposed to just backing out. - if (resultCode == RESULT_OK) { - val uri = result!!.data - when (requestCode) { - MainPresenter.REQUEST_DIRECTORY -> { - if (DirectoryInitialization.preferOldFolderPicker(this)) { - presenter.onDirectorySelected(FileBrowserHelper.getSelectedPath(result)) - } else { - presenter.onDirectorySelected(result) - } - } - - MainPresenter.REQUEST_GAME_FILE -> FileBrowserHelper.runAfterExtensionCheck( - this, uri, FileBrowserHelper.GAME_LIKE_EXTENSIONS - ) { EmulationActivity.launch(this, result.data.toString(), false) } - - MainPresenter.REQUEST_WAD_FILE -> FileBrowserHelper.runAfterExtensionCheck( - this, uri, FileBrowserHelper.WAD_EXTENSION - ) { presenter.installWAD(result.data.toString()) } - - MainPresenter.REQUEST_WII_SAVE_FILE -> FileBrowserHelper.runAfterExtensionCheck( - this, uri, FileBrowserHelper.BIN_EXTENSION - ) { presenter.importWiiSave(result.data.toString()) } - - MainPresenter.REQUEST_NAND_BIN_FILE -> FileBrowserHelper.runAfterExtensionCheck( - this, uri, FileBrowserHelper.BIN_EXTENSION - ) { presenter.importNANDBin(result.data.toString()) } - } - } else { - MainPresenter.skipRescanningLibrary() - } - } - override fun onRequestPermissionsResult( requestCode: Int, permissions: Array, diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/FileBrowserHelper.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/FileBrowserHelper.java index f13578d7aee..061d102be84 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/FileBrowserHelper.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/FileBrowserHelper.java @@ -17,7 +17,6 @@ import com.nononsenseapps.filepicker.Utils; import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.activities.CustomFilePickerActivity; import org.dolphinemu.dolphinemu.features.settings.model.StringSetting; -import org.dolphinemu.dolphinemu.ui.main.MainPresenter; import java.io.File; import java.util.ArrayList; @@ -49,7 +48,8 @@ public final class FileBrowserHelper public static final HashSet WAD_EXTENSION = new HashSet<>(Collections.singletonList( "wad")); - public static void openDirectoryPicker(FragmentActivity activity, HashSet extensions) + public static Intent createDirectoryPickerIntent(FragmentActivity activity, + HashSet extensions) { Intent i = new Intent(activity, CustomFilePickerActivity.class); @@ -60,7 +60,7 @@ public final class FileBrowserHelper Environment.getExternalStorageDirectory().getPath()); i.putExtra(CustomFilePickerActivity.EXTRA_EXTENSIONS, extensions); - activity.startActivityForResult(i, MainPresenter.REQUEST_DIRECTORY); + return i; } @Nullable