Merge pull request #13992 from JosJuice/android-register-for-activity-result

Android: Replace deprecated startActivityForResult
This commit is contained in:
JosJuice 2025-11-16 09:43:04 +01:00 committed by GitHub
commit b74c3faa48
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 374 additions and 413 deletions

View File

@ -2,10 +2,10 @@
package org.dolphinemu.dolphinemu.activities package org.dolphinemu.dolphinemu.activities
import android.annotation.SuppressLint
import android.content.DialogInterface import android.content.DialogInterface
import android.content.Intent import android.content.Intent
import android.graphics.Rect import android.graphics.Rect
import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.util.SparseIntArray import android.util.SparseIntArray
@ -16,6 +16,7 @@ import android.view.View
import android.view.ViewGroup.MarginLayoutParams import android.view.ViewGroup.MarginLayoutParams
import android.view.WindowManager import android.view.WindowManager
import android.widget.Toast import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
@ -87,6 +88,92 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider {
private lateinit var binding: ActivityEmulationBinding 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?) { override fun onCreate(savedInstanceState: Bundle?) {
ThemeHelper.setTheme(this) ThemeHelper.setTheme(this)
@ -263,70 +350,6 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider {
return super.onKeyLongPress(keyCode, event) 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() { private fun enableFullscreenImmersive() {
WindowCompat.setDecorFitsSystemWindows(window, false) WindowCompat.setDecorFitsSystemWindows(window, false)
WindowInsetsControllerCompat(window, window.decorView).let { controller -> WindowInsetsControllerCompat(window, window.decorView).let { controller ->
@ -480,12 +503,7 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider {
MENU_ACTION_LOAD_SLOT4 -> NativeLibrary.LoadState(3) MENU_ACTION_LOAD_SLOT4 -> NativeLibrary.LoadState(3)
MENU_ACTION_LOAD_SLOT5 -> NativeLibrary.LoadState(4) MENU_ACTION_LOAD_SLOT5 -> NativeLibrary.LoadState(4)
MENU_ACTION_LOAD_SLOT6 -> NativeLibrary.LoadState(5) MENU_ACTION_LOAD_SLOT6 -> NativeLibrary.LoadState(5)
MENU_ACTION_CHANGE_DISC -> { MENU_ACTION_CHANGE_DISC -> requestChangeDisc.launch("*/*")
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "*/*"
startActivityForResult(intent, REQUEST_CHANGE_DISC)
}
MENU_SET_IR_MODE -> setIRMode() MENU_SET_IR_MODE -> setIRMode()
MENU_ACTION_CHOOSE_DOUBLETAP -> chooseDoubleTapButton() MENU_ACTION_CHOOSE_DOUBLETAP -> chooseDoubleTapButton()
@ -997,11 +1015,6 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider {
companion object { companion object {
private const val BACKSTACK_NAME_MENU = "menu" private const val BACKSTACK_NAME_MENU = "menu"
private const val BACKSTACK_NAME_SUBMENU = "submenu" 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 private var ignoreLaunchRequests = false

View File

@ -11,6 +11,8 @@ import android.os.Bundle
import android.provider.DocumentsContract import android.provider.DocumentsContract
import android.view.View import android.view.View
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.activity.result.ActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
@ -44,6 +46,39 @@ class UserDataActivity : AppCompatActivity(), ThemeProvider {
private lateinit var mBinding: ActivityUserDataBinding private lateinit var mBinding: ActivityUserDataBinding
override var themeId: Int = 0 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?) { override fun onCreate(savedInstanceState: Bundle?) {
taskViewModel = ViewModelProvider(this)[TaskViewModel::class.java] taskViewModel = ViewModelProvider(this)[TaskViewModel::class.java]
@ -108,34 +143,6 @@ class UserDataActivity : AppCompatActivity(), ThemeProvider {
this.themeId = themeId 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() { private fun confirmResetSettings() {
MaterialAlertDialogBuilder(this) MaterialAlertDialogBuilder(this)
.setTitle(R.string.reset_dolphin_settings) .setTitle(R.string.reset_dolphin_settings)
@ -216,15 +223,14 @@ class UserDataActivity : AppCompatActivity(), ThemeProvider {
} }
private fun importUserData() { private fun importUserData() {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT) requestImport.launch(arrayOf("application/zip"))
intent.type = "application/zip"
startActivityForResult(intent, REQUEST_CODE_IMPORT)
} }
fun importUserData(source: Uri): Int { fun importUserData(source: Uri): Int {
try { try {
if (!isDolphinUserDataBackup(source)) if (!isDolphinUserDataBackup(source)) {
return R.string.user_data_import_invalid_file return R.string.user_data_import_invalid_file
}
taskViewModel.onResultDismiss = { taskViewModel.onResultDismiss = {
// Restart the app to apply the imported user data. // Restart the app to apply the imported user data.
@ -319,10 +325,7 @@ class UserDataActivity : AppCompatActivity(), ThemeProvider {
} }
private fun exportUserData() { private fun exportUserData() {
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT) requestExport.launch("dolphin-emu.zip")
intent.type = "application/zip"
intent.putExtra(Intent.EXTRA_TITLE, "dolphin-emu.zip")
startActivityForResult(intent, REQUEST_CODE_EXPORT)
} }
private fun exportUserData(destination: Uri): Int { private fun exportUserData(destination: Uri): Int {
@ -385,9 +388,6 @@ class UserDataActivity : AppCompatActivity(), ThemeProvider {
} }
companion object { companion object {
private const val REQUEST_CODE_IMPORT = 0
private const val REQUEST_CODE_EXPORT = 1
private const val BUFFER_SIZE = 64 * 1024 private const val BUFFER_SIZE = 64 * 1024
@JvmStatic @JvmStatic

View File

@ -50,14 +50,8 @@ class FigureSlotAdapter(
} }
holder.binding.buttonLoadFigure.setOnClickListener { 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.setInfinityFigureData(0, "", figure.position, position)
activity.startActivityForResult( activity.requestInfinityFigureFile.launch("*/*")
loadFigure,
EmulationActivity.REQUEST_INFINITY_FIGURE_FILE
)
} }
val inflater = LayoutInflater.from(activity) val inflater = LayoutInflater.from(activity)
@ -110,28 +104,11 @@ class FigureSlotAdapter(
.show() .show()
createDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener { createDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
if (binding.infinityNum.text.toString().isNotBlank()) { 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 num = binding.infinityNum.text.toString().toLong()
val name = InfinityConfig.LIST_FIGURES[num] val name = InfinityConfig.LIST_FIGURES[num]
if (name != null) { val title = if (name != null) "$name.bin" else "Unknown(Number: $num).bin"
createFigure.putExtra( activity.setInfinityFigureData(num, name ?: "Unknown", figure.position, position)
Intent.EXTRA_TITLE, activity.requestCreateInfinityFigure.launch(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
)
createDialog.dismiss() createDialog.dismiss()
} else { } else {
Toast.makeText( Toast.makeText(

View File

@ -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<Intent>,
defaultPathRelativeToUserDirectory: String?
) : FilePicker(context, setting, titleId, descriptionId, launcher, defaultPathRelativeToUserDirectory) {
override val type: Int = TYPE_DIRECTORY_PICKER
}

View File

@ -3,15 +3,17 @@
package org.dolphinemu.dolphinemu.features.settings.model.view package org.dolphinemu.dolphinemu.features.settings.model.view
import android.content.Context 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.AbstractStringSetting
import org.dolphinemu.dolphinemu.features.settings.model.Settings import org.dolphinemu.dolphinemu.features.settings.model.Settings
class FilePicker( open class FilePicker(
context: Context, context: Context,
override var setting: AbstractStringSetting, override var setting: AbstractStringSetting,
titleId: Int, titleId: Int,
descriptionId: Int, descriptionId: Int,
val requestType: Int, val launcher: ActivityResultLauncher<Intent>,
val defaultPathRelativeToUserDirectory: String? val defaultPathRelativeToUserDirectory: String?
) : SettingsItem(context, titleId, descriptionId) { ) : SettingsItem(context, titleId, descriptionId) {
override val type: Int = TYPE_FILE_PICKER override val type: Int = TYPE_FILE_PICKER

View File

@ -81,9 +81,10 @@ abstract class SettingsItem {
const val TYPE_STRING_SINGLE_CHOICE = 6 const val TYPE_STRING_SINGLE_CHOICE = 6
const val TYPE_SINGLE_CHOICE_DYNAMIC_DESCRIPTIONS = 8 const val TYPE_SINGLE_CHOICE_DYNAMIC_DESCRIPTIONS = 8
const val TYPE_FILE_PICKER = 9 const val TYPE_FILE_PICKER = 9
const val TYPE_RUN_RUNNABLE = 10 const val TYPE_DIRECTORY_PICKER = 10
const val TYPE_STRING = 11 const val TYPE_RUN_RUNNABLE = 11
const val TYPE_HYPERLINK_HEADER = 12 const val TYPE_STRING = 12
const val TYPE_DATETIME_CHOICE = 13 const val TYPE_HYPERLINK_HEADER = 13
const val TYPE_DATETIME_CHOICE = 14
} }
} }

View File

@ -1,15 +1,10 @@
// SPDX-License-Identifier: GPL-2.0-or-later // 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 package org.dolphinemu.dolphinemu.features.settings.ui
import android.content.Context import android.content.Context
import android.content.DialogInterface import android.content.DialogInterface
import android.content.Intent import android.content.Intent
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.KeyEvent import android.view.KeyEvent
import android.view.Menu 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.features.settings.ui.SettingsFragment.Companion.newInstance
import org.dolphinemu.dolphinemu.ui.main.MainPresenter import org.dolphinemu.dolphinemu.ui.main.MainPresenter
import org.dolphinemu.dolphinemu.ui.main.ThemeProvider import org.dolphinemu.dolphinemu.ui.main.ThemeProvider
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper
import org.dolphinemu.dolphinemu.utils.InsetsHelper import org.dolphinemu.dolphinemu.utils.InsetsHelper
import org.dolphinemu.dolphinemu.utils.SerializableHelper.serializable import org.dolphinemu.dolphinemu.utils.SerializableHelper.serializable
import org.dolphinemu.dolphinemu.utils.ThemeHelper.enableScrollTint import org.dolphinemu.dolphinemu.utils.ThemeHelper.enableScrollTint
@ -177,40 +171,6 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView, ThemeProvide
return duration != 0f && transition != 0f 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<String> =
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 { override fun dispatchKeyEvent(event: KeyEvent): Boolean {
ControllerInterface.dispatchKeyEvent(event) ControllerInterface.dispatchKeyEvent(event)
return super.dispatchKeyEvent(event) return super.dispatchKeyEvent(event)
@ -221,11 +181,6 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView, ThemeProvide
return super.dispatchGenericMotionEvent(event) return super.dispatchGenericMotionEvent(event)
} }
private fun canonicalizeIfPossible(uri: Uri): Uri {
val canonicalizedUri = contentResolver.canonicalize(uri)
return canonicalizedUri ?: uri
}
override fun showLoading() { override fun showLoading() {
if (dialog == null) { if (dialog == null) {
dialog = MaterialAlertDialogBuilder(this) dialog = MaterialAlertDialogBuilder(this)

View File

@ -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<String>, 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())
}
}
}
}

View File

@ -94,7 +94,8 @@ class SettingsAdapter(
ListItemMappingBinding.inflate(inflater, parent, false), ListItemMappingBinding.inflate(inflater, parent, false),
this this
) )
SettingsItem.TYPE_FILE_PICKER -> FilePickerViewHolder( SettingsItem.TYPE_FILE_PICKER,
SettingsItem.TYPE_DIRECTORY_PICKER -> FilePickerViewHolder(
ListItemSettingBinding.inflate(inflater, parent, false), ListItemSettingBinding.inflate(inflater, parent, false),
this this
) )
@ -357,6 +358,7 @@ class SettingsAdapter(
fun onFilePickerDirectoryClick(item: SettingsItem, position: Int) { fun onFilePickerDirectoryClick(item: SettingsItem, position: Int) {
clickedItem = item clickedItem = item
clickedPosition = position clickedPosition = position
val directoryPicker = item as DirectoryPicker
if (!PermissionsHandler.isExternalStorageLegacy()) { if (!PermissionsHandler.isExternalStorageLegacy()) {
MaterialAlertDialogBuilder(context) MaterialAlertDialogBuilder(context)
@ -364,10 +366,11 @@ class SettingsAdapter(
.setPositiveButton(R.string.ok) { dialog: DialogInterface, _: Int -> dialog.dismiss() } .setPositiveButton(R.string.ok) { dialog: DialogInterface, _: Int -> dialog.dismiss() }
.show() .show()
} else { } else {
FileBrowserHelper.openDirectoryPicker( val intent = FileBrowserHelper.createDirectoryPickerIntent(
fragmentView.fragmentActivity, fragmentView.fragmentActivity,
FileBrowserHelper.GAME_EXTENSIONS FileBrowserHelper.GAME_EXTENSIONS
) )
directoryPicker.launcher.launch(intent)
} }
} }
@ -383,7 +386,7 @@ class SettingsAdapter(
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, filePicker.getSelectedValue()) intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, filePicker.getSelectedValue())
} }
fragmentView.fragmentActivity.startActivityForResult(intent, filePicker.requestType) filePicker.launcher.launch(intent)
} }
fun onDateTimeClick(item: DateTimeChoiceSetting, position: Int) { fun onDateTimeClick(item: DateTimeChoiceSetting, position: Int) {

View File

@ -1,17 +1,22 @@
// SPDX-License-Identifier: GPL-2.0-or-later // 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 package org.dolphinemu.dolphinemu.features.settings.ui
import android.app.Activity
import android.content.Context import android.content.Context
import android.content.DialogInterface import android.content.DialogInterface
import android.content.Intent import android.content.Intent
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Toast 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.ViewCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding import androidx.core.view.updatePadding
@ -26,7 +31,6 @@ import org.dolphinemu.dolphinemu.R
import org.dolphinemu.dolphinemu.databinding.FragmentSettingsBinding import org.dolphinemu.dolphinemu.databinding.FragmentSettingsBinding
import org.dolphinemu.dolphinemu.features.settings.model.Settings import org.dolphinemu.dolphinemu.features.settings.model.Settings
import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem 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.GpuDriverInstallResult
import org.dolphinemu.dolphinemu.utils.SerializableHelper.serializable import org.dolphinemu.dolphinemu.utils.SerializableHelper.serializable
import java.util.* import java.util.*
@ -43,10 +47,22 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
override var adapter: SettingsAdapter? = null override var adapter: SettingsAdapter? = null
override val activityResultLaunchers: SettingsActivityResultLaunchers =
SettingsActivityResultLaunchers(this) { adapter }
private var oldControllerSettingsWarningHeight = 0 private var oldControllerSettingsWarningHeight = 0
private var binding: FragmentSettingsBinding? = null 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) { override fun onAttach(context: Context) {
super.onAttach(context) super.onAttach(context)
@ -185,7 +201,7 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
if (presenter.gpuDriver == null) { if (presenter.gpuDriver == null) {
return return
} }
val msg = "${presenter!!.gpuDriver!!.name} ${presenter!!.gpuDriver!!.driverVersion}" val msg = "${presenter.gpuDriver!!.name} ${presenter.gpuDriver!!.driverVersion}"
MaterialAlertDialogBuilder(requireContext()) MaterialAlertDialogBuilder(requireContext())
.setTitle(getString(R.string.gpu_driver_dialog_title)) .setTitle(getString(R.string.gpu_driver_dialog_title))
@ -209,23 +225,7 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
type = "application/zip" type = "application/zip"
} }
startActivityForResult(intent, MainPresenter.REQUEST_GPU_DRIVER) requestGpuDriver.launch(intent)
}
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)
} }
override fun onDriverInstallDone(result: GpuDriverInstallResult) { override fun onDriverInstallDone(result: GpuDriverInstallResult) {

View File

@ -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.*
import org.dolphinemu.dolphinemu.features.settings.model.view.* import org.dolphinemu.dolphinemu.features.settings.model.view.*
import org.dolphinemu.dolphinemu.model.GpuDriverMetadata import org.dolphinemu.dolphinemu.model.GpuDriverMetadata
import org.dolphinemu.dolphinemu.ui.main.MainPresenter
import org.dolphinemu.dolphinemu.utils.* import org.dolphinemu.dolphinemu.utils.*
import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
import kotlin.math.ceil import kotlin.math.ceil
import kotlin.math.floor import kotlin.math.floor
@ -584,57 +582,57 @@ class SettingsFragmentPresenter(
StringSetting.MAIN_DEFAULT_ISO, StringSetting.MAIN_DEFAULT_ISO,
R.string.default_ISO, R.string.default_ISO,
0, 0,
MainPresenter.REQUEST_GAME_FILE, fragmentView.activityResultLaunchers.requestGameFile,
null null
) )
) )
sl.add( sl.add(
FilePicker( DirectoryPicker(
context, context,
StringSetting.MAIN_FS_PATH, StringSetting.MAIN_FS_PATH,
R.string.wii_NAND_root, R.string.wii_NAND_root,
0, 0,
MainPresenter.REQUEST_DIRECTORY, fragmentView.activityResultLaunchers.requestDirectory,
"/Wii" "/Wii"
) )
) )
sl.add( sl.add(
FilePicker( DirectoryPicker(
context, context,
StringSetting.MAIN_DUMP_PATH, StringSetting.MAIN_DUMP_PATH,
R.string.dump_path, R.string.dump_path,
0, 0,
MainPresenter.REQUEST_DIRECTORY, fragmentView.activityResultLaunchers.requestDirectory,
"/Dump" "/Dump"
) )
) )
sl.add( sl.add(
FilePicker( DirectoryPicker(
context, context,
StringSetting.MAIN_LOAD_PATH, StringSetting.MAIN_LOAD_PATH,
R.string.load_path, R.string.load_path,
0, 0,
MainPresenter.REQUEST_DIRECTORY, fragmentView.activityResultLaunchers.requestDirectory,
"/Load" "/Load"
) )
) )
sl.add( sl.add(
FilePicker( DirectoryPicker(
context, context,
StringSetting.MAIN_RESOURCEPACK_PATH, StringSetting.MAIN_RESOURCEPACK_PATH,
R.string.resource_pack_path, R.string.resource_pack_path,
0, 0,
MainPresenter.REQUEST_DIRECTORY, fragmentView.activityResultLaunchers.requestDirectory,
"/ResourcePacks" "/ResourcePacks"
) )
) )
sl.add( sl.add(
FilePicker( DirectoryPicker(
context, context,
StringSetting.MAIN_WFS_PATH, StringSetting.MAIN_WFS_PATH,
R.string.wfs_path, R.string.wfs_path,
0, 0,
MainPresenter.REQUEST_DIRECTORY, fragmentView.activityResultLaunchers.requestDirectory,
"/WFS" "/WFS"
) )
) )
@ -784,17 +782,17 @@ class SettingsFragmentPresenter(
StringSetting.MAIN_WII_SD_CARD_IMAGE_PATH, StringSetting.MAIN_WII_SD_CARD_IMAGE_PATH,
R.string.wii_sd_card_path, R.string.wii_sd_card_path,
0, 0,
MainPresenter.REQUEST_SD_FILE, fragmentView.activityResultLaunchers.requestRawFile,
"/Load/WiiSD.raw" "/Load/WiiSD.raw"
) )
) )
sl.add( sl.add(
FilePicker( DirectoryPicker(
context, context,
StringSetting.MAIN_WII_SD_CARD_SYNC_FOLDER_PATH, StringSetting.MAIN_WII_SD_CARD_SYNC_FOLDER_PATH,
R.string.wii_sd_sync_folder, R.string.wii_sd_sync_folder,
0, 0,
MainPresenter.REQUEST_DIRECTORY, fragmentView.activityResultLaunchers.requestDirectory,
"/Load/WiiSDSync/" "/Load/WiiSDSync/"
) )
) )

View File

@ -38,6 +38,11 @@ interface SettingsFragmentView {
*/ */
val adapter: SettingsAdapter? 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 * Tell the Fragment to tell the containing Activity to show a new
* Fragment containing a submenu of settings. * Fragment containing a submenu of settings.

View File

@ -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.FilePicker
import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter 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.DirectoryInitialization
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper import org.dolphinemu.dolphinemu.utils.FileBrowserHelper
@ -57,7 +56,7 @@ class FilePickerViewHolder(
} }
val position = bindingAdapterPosition val position = bindingAdapterPosition
if (setting.requestType == MainPresenter.REQUEST_DIRECTORY) { if (setting.type == SettingsItem.TYPE_DIRECTORY_PICKER) {
adapter.onFilePickerDirectoryClick(setting, position) adapter.onFilePickerDirectoryClick(setting, position)
} else { } else {
adapter.onFilePickerFileClick(setting, position) adapter.onFilePickerFileClick(setting, position)

View File

@ -46,14 +46,8 @@ class SkylanderSlotAdapter(
} }
holder.binding.buttonLoadFigure.setOnClickListener { holder.binding.buttonLoadFigure.setOnClickListener {
val loadSkylander = Intent(Intent.ACTION_OPEN_DOCUMENT)
loadSkylander.addCategory(Intent.CATEGORY_OPENABLE)
loadSkylander.type = "*/*"
activity.setSkylanderData(0, 0, "", position) activity.setSkylanderData(0, 0, "", position)
activity.startActivityForResult( activity.requestSkylanderFile.launch("*/*")
loadSkylander,
EmulationActivity.REQUEST_SKYLANDER_FILE
)
} }
val inflater = LayoutInflater.from(activity) val inflater = LayoutInflater.from(activity)
@ -85,29 +79,12 @@ class SkylanderSlotAdapter(
if (binding.skylanderId.text.toString().isNotBlank() && if (binding.skylanderId.text.toString().isNotBlank() &&
binding.skylanderVar.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 id = binding.skylanderId.text.toString().toInt()
val variant = binding.skylanderVar.text.toString().toInt() val variant = binding.skylanderVar.text.toString().toInt()
val name = SkylanderConfig.LIST_SKYLANDERS[SkylanderPair(id, variant)] val name = SkylanderConfig.LIST_SKYLANDERS[SkylanderPair(id, variant)]
if (name != null) { val title = if (name != null) "$name.sky" else "Unknown(ID: $id Variant: $variant).sky"
createSkylander.putExtra( activity.setSkylanderData(id, variant, name ?: "Unknown", position)
Intent.EXTRA_TITLE, activity.requestCreateSkylander.launch(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
)
createDialog.dismiss() createDialog.dismiss()
} else { } else {
Toast.makeText( Toast.makeText(

View File

@ -15,6 +15,8 @@ import android.view.ViewGroup
import android.widget.AdapterView import android.widget.AdapterView
import android.widget.AdapterView.OnItemClickListener import android.widget.AdapterView.OnItemClickListener
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import androidx.activity.result.ActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
@ -79,6 +81,15 @@ class ConvertFragment : Fragment(), View.OnClickListener {
private var _binding: FragmentConvertBinding? = null private var _binding: FragmentConvertBinding? = null
val binding get() = _binding!! 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?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
gameFile = GameFileCacheManager.addOrGet(requireArguments().getString(ARG_GAME_PATH)) gameFile = GameFileCacheManager.addOrGet(requireArguments().getString(ARG_GAME_PATH))
@ -429,13 +440,7 @@ class ConvertFragment : Fragment(), View.OnClickListener {
DocumentsContract.EXTRA_INITIAL_URI, DocumentsContract.EXTRA_INITIAL_URI,
originalPath originalPath
) )
startActivityForResult(intent, REQUEST_CODE_SAVE_FILE) requestSaveFile.launch(intent)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == REQUEST_CODE_SAVE_FILE && resultCode == Activity.RESULT_OK) {
convert(data!!.data.toString())
}
} }
private fun convert(outPath: String) { 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_COMPRESSION_LEVEL = "convert_compression_level"
private const val KEY_REMOVE_JUNK_DATA = "remove_junk_data" 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_ISO = 0
private const val BLOB_TYPE_GCZ = 3 private const val BLOB_TYPE_GCZ = 3
private const val BLOB_TYPE_WIA = 7 private const val BLOB_TYPE_WIA = 7

View File

@ -2,7 +2,6 @@
package org.dolphinemu.dolphinemu.ui.main package org.dolphinemu.dolphinemu.ui.main
import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Bundle import android.os.Bundle
import android.view.Menu 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.appbar.AppBarLayout
import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayout
import org.dolphinemu.dolphinemu.R import org.dolphinemu.dolphinemu.R
import org.dolphinemu.dolphinemu.activities.EmulationActivity
import org.dolphinemu.dolphinemu.adapters.PlatformPagerAdapter import org.dolphinemu.dolphinemu.adapters.PlatformPagerAdapter
import org.dolphinemu.dolphinemu.databinding.ActivityMainBinding import org.dolphinemu.dolphinemu.databinding.ActivityMainBinding
import org.dolphinemu.dolphinemu.features.settings.model.IntSetting 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.ui.platform.PlatformTab
import org.dolphinemu.dolphinemu.utils.AfterDirectoryInitializationRunner import org.dolphinemu.dolphinemu.utils.AfterDirectoryInitializationRunner
import org.dolphinemu.dolphinemu.utils.DirectoryInitialization import org.dolphinemu.dolphinemu.utils.DirectoryInitialization
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper
import org.dolphinemu.dolphinemu.utils.InsetsHelper import org.dolphinemu.dolphinemu.utils.InsetsHelper
import org.dolphinemu.dolphinemu.utils.PermissionsHandler import org.dolphinemu.dolphinemu.utils.PermissionsHandler
import org.dolphinemu.dolphinemu.utils.StartupHandler import org.dolphinemu.dolphinemu.utils.StartupHandler
@ -65,7 +62,11 @@ class MainActivity : AppCompatActivity(), MainView, OnRefreshListener, ThemeProv
setSupportActionBar(binding.toolbarMain) setSupportActionBar(binding.toolbarMain)
// Set up the FAB. // Set up the FAB.
binding.buttonAddDirectory.setOnClickListener { presenter.onFabClick() } binding.buttonAddDirectory.setOnClickListener {
AfterDirectoryInitializationRunner().runWithLifecycle(this) {
presenter.launchFileListActivity()
}
}
binding.appbarMain.addOnOffsetChangedListener { appBarLayout: AppBarLayout, verticalOffset: Int -> binding.appbarMain.addOnOffsetChangedListener { appBarLayout: AppBarLayout, verticalOffset: Int ->
if (verticalOffset == 0) { if (verticalOffset == 0) {
binding.buttonAddDirectory.extend() binding.buttonAddDirectory.extend()
@ -145,63 +146,6 @@ class MainActivity : AppCompatActivity(), MainView, OnRefreshListener, ThemeProv
SettingsActivity.launch(this, menuTag) 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( override fun onRequestPermissionsResult(
requestCode: Int, requestCode: Int,
permissions: Array<String>, permissions: Array<String>,

View File

@ -2,9 +2,13 @@
package org.dolphinemu.dolphinemu.ui.main package org.dolphinemu.dolphinemu.ui.main
import android.app.Activity
import android.content.DialogInterface import android.content.DialogInterface
import android.content.Intent import android.content.Intent
import android.net.Uri
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.result.ActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
@ -35,6 +39,69 @@ import java.util.concurrent.ExecutionException
class MainPresenter(private val mainView: MainView, private val activity: FragmentActivity) { class MainPresenter(private val mainView: MainView, private val activity: FragmentActivity) {
private var dirToAdd: String? = null 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() { fun onCreate() {
// Ask the user to grant write permission if relevant and not already granted // Ask the user to grant write permission if relevant and not already granted
if (DirectoryInitialization.isWaitingForWriteAccess(activity)) if (DirectoryInitialization.isWaitingForWriteAccess(activity))
@ -50,8 +117,17 @@ class MainPresenter(private val mainView: MainView, private val activity: Fragme
GameFileCacheManager.isRescanning().observe(activity, refreshObserver) GameFileCacheManager.isRescanning().observe(activity, refreshObserver)
} }
fun onFabClick() { fun launchFileListActivity() {
AfterDirectoryInitializationRunner().runWithLifecycle(activity) { mainView.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 = 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 -> { R.id.button_add_directory -> {
AfterDirectoryInitializationRunner().runWithLifecycle(activity) { mainView.launchFileListActivity() } AfterDirectoryInitializationRunner().runWithLifecycle(activity) { launchFileListActivity() }
true true
} }
R.id.menu_open_file -> { R.id.menu_open_file -> {
mainView.launchOpenFileActivity(REQUEST_GAME_FILE) requestGameFile.launch("*/*")
true true
} }
@ -95,21 +171,21 @@ class MainPresenter(private val mainView: MainView, private val activity: Fragme
R.id.menu_install_wad -> { R.id.menu_install_wad -> {
AfterDirectoryInitializationRunner().runWithLifecycle( AfterDirectoryInitializationRunner().runWithLifecycle(
activity activity
) { mainView.launchOpenFileActivity(REQUEST_WAD_FILE) } ) { requestWadFile.launch("*/*") }
true true
} }
R.id.menu_import_wii_save -> { R.id.menu_import_wii_save -> {
AfterDirectoryInitializationRunner().runWithLifecycle( AfterDirectoryInitializationRunner().runWithLifecycle(
activity activity
) { mainView.launchOpenFileActivity(REQUEST_WII_SAVE_FILE) } ) { requestWiiSaveFile.launch("*/*") }
true true
} }
R.id.menu_import_nand_backup -> { R.id.menu_import_nand_backup -> {
AfterDirectoryInitializationRunner().runWithLifecycle( AfterDirectoryInitializationRunner().runWithLifecycle(
activity activity
) { mainView.launchOpenFileActivity(REQUEST_NAND_BIN_FILE) } ) { requestNandBinFile.launch("*/*") }
true true
} }
@ -280,14 +356,6 @@ class MainPresenter(private val mainView: MainView, private val activity: Fragme
} }
companion object { 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 private var shouldRescanLibrary = true
@JvmStatic @JvmStatic

View File

@ -20,10 +20,6 @@ interface MainView {
fun launchSettingsActivity(menuTag: MenuTag?) fun launchSettingsActivity(menuTag: MenuTag?)
fun launchFileListActivity()
fun launchOpenFileActivity(requestCode: Int)
/** /**
* Shows or hides the loading indicator. * Shows or hides the loading indicator.
*/ */

View File

@ -125,22 +125,6 @@ class TvMainActivity : FragmentActivity(), MainView, OnRefreshListener {
SettingsActivity.launch(this, menuTag) 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. * Shows or hides the loading indicator.
*/ */
@ -165,49 +149,6 @@ class TvMainActivity : FragmentActivity(), MainView, OnRefreshListener {
GridOptionDialogFragment().show(supportFragmentManager, "gridOptions") 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( override fun onRequestPermissionsResult(
requestCode: Int, requestCode: Int,
permissions: Array<String>, permissions: Array<String>,

View File

@ -17,7 +17,6 @@ import com.nononsenseapps.filepicker.Utils;
import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.activities.CustomFilePickerActivity; import org.dolphinemu.dolphinemu.activities.CustomFilePickerActivity;
import org.dolphinemu.dolphinemu.features.settings.model.StringSetting; import org.dolphinemu.dolphinemu.features.settings.model.StringSetting;
import org.dolphinemu.dolphinemu.ui.main.MainPresenter;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
@ -49,7 +48,8 @@ public final class FileBrowserHelper
public static final HashSet<String> WAD_EXTENSION = new HashSet<>(Collections.singletonList( public static final HashSet<String> WAD_EXTENSION = new HashSet<>(Collections.singletonList(
"wad")); "wad"));
public static void openDirectoryPicker(FragmentActivity activity, HashSet<String> extensions) public static Intent createDirectoryPickerIntent(FragmentActivity activity,
HashSet<String> extensions)
{ {
Intent i = new Intent(activity, CustomFilePickerActivity.class); Intent i = new Intent(activity, CustomFilePickerActivity.class);
@ -60,7 +60,7 @@ public final class FileBrowserHelper
Environment.getExternalStorageDirectory().getPath()); Environment.getExternalStorageDirectory().getPath());
i.putExtra(CustomFilePickerActivity.EXTRA_EXTENSIONS, extensions); i.putExtra(CustomFilePickerActivity.EXTRA_EXTENSIONS, extensions);
activity.startActivityForResult(i, MainPresenter.REQUEST_DIRECTORY); return i;
} }
@Nullable @Nullable