From dd5f8312627a5bf4b5a0f311437baa681ecdfbbd Mon Sep 17 00:00:00 2001 From: Simonx22 Date: Fri, 21 Nov 2025 21:47:09 -0500 Subject: [PATCH 1/2] Android: Remove CompletableFuture We only use this class in one in one single function since its introduction with 12aa1071cbb30269cd6526cfbbcd3c4143d584ac in 2021. If we do need it elsewhere we can always bring it back. --- .../dolphinemu/ui/main/MainPresenter.kt | 19 ++-- .../dolphinemu/utils/CompletableFuture.java | 97 ------------------- 2 files changed, 10 insertions(+), 106 deletions(-) delete mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/CompletableFuture.java 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 cea17578d4..77a7dcaa8a 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 @@ -26,7 +26,6 @@ import org.dolphinemu.dolphinemu.model.GameFileCache import org.dolphinemu.dolphinemu.services.GameFileCacheManager import org.dolphinemu.dolphinemu.utils.AfterDirectoryInitializationRunner import org.dolphinemu.dolphinemu.utils.BooleanSupplier -import org.dolphinemu.dolphinemu.utils.CompletableFuture import org.dolphinemu.dolphinemu.utils.ContentHandler import org.dolphinemu.dolphinemu.utils.DirectoryInitialization import org.dolphinemu.dolphinemu.utils.FileBrowserHelper @@ -34,7 +33,8 @@ import org.dolphinemu.dolphinemu.utils.PermissionsHandler import org.dolphinemu.dolphinemu.utils.ThreadUtil import org.dolphinemu.dolphinemu.utils.WiiUtils import java.util.Arrays -import java.util.concurrent.ExecutionException +import java.util.concurrent.CountDownLatch +import java.util.concurrent.atomic.AtomicReference class MainPresenter(private val mainView: MainView, private val activity: FragmentActivity) { private var dirToAdd: String? = null @@ -265,33 +265,34 @@ class MainPresenter(private val mainView: MainView, private val activity: Fragme } fun importWiiSave(path: String?) { - val canOverwriteFuture = CompletableFuture() ThreadUtil.runOnThreadAndShowResult( activity, R.string.import_in_progress, 0, { val canOverwrite = BooleanSupplier { + val latch = CountDownLatch(1) + val decision = AtomicReference() activity.runOnUiThread { MaterialAlertDialogBuilder(activity) .setMessage(R.string.wii_save_exists) .setCancelable(false) .setPositiveButton(R.string.yes) { _: DialogInterface?, _: Int -> - canOverwriteFuture.complete(true) + decision.set(true) + latch.countDown() } .setNegativeButton(R.string.no) { _: DialogInterface?, _: Int -> - canOverwriteFuture.complete(false) + decision.set(false) + latch.countDown() } .show() } try { - return@BooleanSupplier canOverwriteFuture.get() - } catch (e: ExecutionException) { - // Shouldn't happen - throw RuntimeException(e) + latch.await() } catch (e: InterruptedException) { throw RuntimeException(e) } + decision.get() ?: false } val message: Int = when (WiiUtils.importWiiSave(path!!, canOverwrite)) { diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/CompletableFuture.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/CompletableFuture.java deleted file mode 100644 index 1b3c450534..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/CompletableFuture.java +++ /dev/null @@ -1,97 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.utils; - -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.locks.Condition; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - -/** - * Simplified re-implementation of a subset of {@link java.util.concurrent.CompletableFuture}. - * Replace this class with that class once we have full Java 8 support (once we require API 24). - */ -public class CompletableFuture implements Future -{ - private final Lock lock = new ReentrantLock(); - private final Condition done = lock.newCondition(); - - private boolean isDone = false; - private T result = null; - - @Override - public boolean cancel(boolean mayInterruptIfRunning) - { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isCancelled() - { - return false; - } - - @Override - public boolean isDone() - { - return isDone; - } - - @Override - public T get() throws ExecutionException, InterruptedException - { - lock.lock(); - try - { - while (!isDone) - done.await(); - - return result; - } - finally - { - lock.unlock(); - } - } - - @Override - public T get(long timeout, TimeUnit unit) - throws ExecutionException, InterruptedException, TimeoutException - { - lock.lock(); - try - { - while (!isDone) - { - if (!done.await(timeout, unit)) - throw new TimeoutException(); - } - - return result; - } - finally - { - lock.unlock(); - } - } - - public boolean complete(T value) - { - lock.lock(); - try - { - boolean wasDone = isDone; - result = value; - isDone = true; - done.signalAll(); - return !wasDone; - } - finally - { - lock.unlock(); - } - } -} From e78ece126e4d586f1816a8fd4dcf46a36ba71d03 Mon Sep 17 00:00:00 2001 From: Simonx22 Date: Fri, 21 Nov 2025 21:47:40 -0500 Subject: [PATCH 2/2] Android: Format MainPresenter --- .../dolphinemu/ui/main/MainPresenter.kt | 213 ++++++++---------- 1 file changed, 99 insertions(+), 114 deletions(-) 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 77a7dcaa8a..893d6a16d4 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 @@ -104,8 +104,9 @@ class MainPresenter(private val mainView: MainView, private val activity: Fragme fun onCreate() { // Ask the user to grant write permission if relevant and not already granted - if (DirectoryInitialization.isWaitingForWriteAccess(activity)) + if (DirectoryInitialization.isWaitingForWriteAccess(activity)) { PermissionsHandler.requestWritePermission(activity) + } val versionName = BuildConfig.VERSION_NAME mainView.setVersionString(versionName) @@ -118,85 +119,83 @@ class MainPresenter(private val mainView: MainView, private val activity: Fragme } fun launchFileListActivity() { - val intent = - if (DirectoryInitialization.preferOldFolderPicker(activity)) { - FileBrowserHelper.createDirectoryPickerIntent( - activity, - FileBrowserHelper.GAME_EXTENSIONS, - ) - } else { - Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) - } + 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 = - when (itemId) { - R.id.menu_settings -> { - mainView.launchSettingsActivity(MenuTag.SETTINGS) - true - } - - R.id.menu_grid_options -> { - mainView.showGridOptions() - true - } - - R.id.menu_refresh -> { - mainView.setRefreshing(true) - GameFileCacheManager.startRescan() - true - } - - R.id.button_add_directory -> { - AfterDirectoryInitializationRunner().runWithLifecycle(activity) { launchFileListActivity() } - true - } - - R.id.menu_open_file -> { - requestGameFile.launch("*/*") - true - } - - R.id.menu_load_wii_system_menu -> { - launchWiiSystemMenu() - true - } - - R.id.menu_online_system_update -> { - AfterDirectoryInitializationRunner().runWithLifecycle(activity) { launchOnlineUpdate() } - true - } - - R.id.menu_install_wad -> { - AfterDirectoryInitializationRunner().runWithLifecycle( - activity - ) { requestWadFile.launch("*/*") } - true - } - - R.id.menu_import_wii_save -> { - AfterDirectoryInitializationRunner().runWithLifecycle( - activity - ) { requestWiiSaveFile.launch("*/*") } - true - } - - R.id.menu_import_nand_backup -> { - AfterDirectoryInitializationRunner().runWithLifecycle( - activity - ) { requestNandBinFile.launch("*/*") } - true - } - - R.id.menu_about -> { - showAboutDialog() - false - } - - else -> false + fun handleOptionSelection(itemId: Int, activity: ComponentActivity): Boolean = when (itemId) { + R.id.menu_settings -> { + mainView.launchSettingsActivity(MenuTag.SETTINGS) + true } + R.id.menu_grid_options -> { + mainView.showGridOptions() + true + } + + R.id.menu_refresh -> { + mainView.setRefreshing(true) + GameFileCacheManager.startRescan() + true + } + + R.id.button_add_directory -> { + AfterDirectoryInitializationRunner().runWithLifecycle(activity) { launchFileListActivity() } + true + } + + R.id.menu_open_file -> { + requestGameFile.launch("*/*") + true + } + + R.id.menu_load_wii_system_menu -> { + launchWiiSystemMenu() + true + } + + R.id.menu_online_system_update -> { + AfterDirectoryInitializationRunner().runWithLifecycle(activity) { launchOnlineUpdate() } + true + } + + R.id.menu_install_wad -> { + AfterDirectoryInitializationRunner().runWithLifecycle( + activity + ) { requestWadFile.launch("*/*") } + true + } + + R.id.menu_import_wii_save -> { + AfterDirectoryInitializationRunner().runWithLifecycle( + activity + ) { requestWiiSaveFile.launch("*/*") } + true + } + + R.id.menu_import_nand_backup -> { + AfterDirectoryInitializationRunner().runWithLifecycle( + activity + ) { requestNandBinFile.launch("*/*") } + true + } + + R.id.menu_about -> { + showAboutDialog() + false + } + + else -> false + } + fun onResume() { if (dirToAdd != null) { GameFileCache.addGameFolder(dirToAdd!!) @@ -226,24 +225,25 @@ class MainPresenter(private val mainView: MainView, private val activity: Fragme val recursive = BooleanSetting.MAIN_RECURSIVE_ISO_PATHS.boolean val childNames = ContentHandler.getChildNames(uri, recursive) if (Arrays.stream(childNames).noneMatch { - FileBrowserHelper.GAME_EXTENSIONS - .contains(FileBrowserHelper.getExtension(it, false)) - }) { - MaterialAlertDialogBuilder(activity) - .setMessage( - activity.getString( - R.string.wrong_file_extension_in_directory, - FileBrowserHelper.setToSortedDelimitedString(FileBrowserHelper.GAME_EXTENSIONS) + FileBrowserHelper.GAME_EXTENSIONS.contains( + FileBrowserHelper.getExtension( + it, false ) ) - .setPositiveButton(android.R.string.ok, null) - .show() + }) { + MaterialAlertDialogBuilder(activity).setMessage( + activity.getString( + R.string.wrong_file_extension_in_directory, + FileBrowserHelper.setToSortedDelimitedString(FileBrowserHelper.GAME_EXTENSIONS) + ) + ).setPositiveButton(android.R.string.ok, null).show() } val contentResolver = activity.contentResolver val canonicalizedUri = contentResolver.canonicalize(uri) - if (canonicalizedUri != null) + if (canonicalizedUri != null) { uri = canonicalizedUri + } val takeFlags = result.flags and Intent.FLAG_GRANT_READ_URI_PERMISSION activity.contentResolver.takePersistableUriPermission(uri, takeFlags) @@ -253,10 +253,7 @@ class MainPresenter(private val mainView: MainView, private val activity: Fragme fun installWAD(path: String?) { ThreadUtil.runOnThreadAndShowResult( - activity, - R.string.import_in_progress, - 0, - { + activity, R.string.import_in_progress, 0, { val success = WiiUtils.installWAD(path!!) val message = if (success) R.string.wad_install_success else R.string.wad_install_failure @@ -266,26 +263,20 @@ class MainPresenter(private val mainView: MainView, private val activity: Fragme fun importWiiSave(path: String?) { ThreadUtil.runOnThreadAndShowResult( - activity, - R.string.import_in_progress, - 0, - { + activity, R.string.import_in_progress, 0, { val canOverwrite = BooleanSupplier { val latch = CountDownLatch(1) val decision = AtomicReference() activity.runOnUiThread { - MaterialAlertDialogBuilder(activity) - .setMessage(R.string.wii_save_exists) + MaterialAlertDialogBuilder(activity).setMessage(R.string.wii_save_exists) .setCancelable(false) .setPositiveButton(R.string.yes) { _: DialogInterface?, _: Int -> decision.set(true) latch.countDown() - } - .setNegativeButton(R.string.no) { _: DialogInterface?, _: Int -> + }.setNegativeButton(R.string.no) { _: DialogInterface?, _: Int -> decision.set(false) latch.countDown() - } - .show() + }.show() } try { latch.await() @@ -307,23 +298,18 @@ class MainPresenter(private val mainView: MainView, private val activity: Fragme } fun importNANDBin(path: String?) { - MaterialAlertDialogBuilder(activity) - .setMessage(R.string.nand_import_warning) + MaterialAlertDialogBuilder(activity).setMessage(R.string.nand_import_warning) .setNegativeButton(R.string.no) { dialog: DialogInterface, _: Int -> dialog.dismiss() } .setPositiveButton(R.string.yes) { dialog: DialogInterface, _: Int -> dialog.dismiss() ThreadUtil.runOnThreadAndShowResult( - activity, - R.string.import_in_progress, - R.string.do_not_close_app, - { + activity, R.string.import_in_progress, R.string.do_not_close_app, { // ImportNANDBin unfortunately doesn't provide any result value... // It does however show a panic alert if something goes wrong. WiiUtils.importNANDBin(path!!) null }) - } - .show() + }.show() } private fun launchOnlineUpdate() { @@ -333,8 +319,7 @@ class MainPresenter(private val mainView: MainView, private val activity: Fragme launchUpdateProgressBarFragment(activity) } else { SystemMenuNotInstalledDialogFragment().show( - activity.supportFragmentManager, - SystemMenuNotInstalledDialogFragment.TAG + activity.supportFragmentManager, SystemMenuNotInstalledDialogFragment.TAG ) } } @@ -345,8 +330,7 @@ class MainPresenter(private val mainView: MainView, private val activity: Fragme EmulationActivity.launchSystemMenu(activity) } else { SystemMenuNotInstalledDialogFragment().show( - activity.supportFragmentManager, - SystemMenuNotInstalledDialogFragment.TAG + activity.supportFragmentManager, SystemMenuNotInstalledDialogFragment.TAG ) } } @@ -373,8 +357,9 @@ class MainPresenter(private val mainView: MainView, private val activity: Fragme private fun launchUpdateProgressBarFragment(activity: FragmentActivity) { val progressBarFragment = SystemUpdateProgressBarDialogFragment() - progressBarFragment - .show(activity.supportFragmentManager, SystemUpdateProgressBarDialogFragment.TAG) + progressBarFragment.show( + activity.supportFragmentManager, SystemUpdateProgressBarDialogFragment.TAG + ) progressBarFragment.isCancelable = false } }