Merge pull request #14155 from Simonx22/android/remove-completable-future

Android: Remove CompletableFuture
This commit is contained in:
JMC47 authored and GitHub committed 2025-12-22 13:34:58 -05:00
commit 8aabaf8b63
2 files changed
+109 -220

No files matched your search

@@ -26,7 +26,6 @@ import org.dolphinemu.dolphinemu.model.GameFileCache
import org.dolphinemu.dolphinemu.services.GameFileCacheManager import org.dolphinemu.dolphinemu.services.GameFileCacheManager
import org.dolphinemu.dolphinemu.utils.AfterDirectoryInitializationRunner import org.dolphinemu.dolphinemu.utils.AfterDirectoryInitializationRunner
import org.dolphinemu.dolphinemu.utils.BooleanSupplier import org.dolphinemu.dolphinemu.utils.BooleanSupplier
import org.dolphinemu.dolphinemu.utils.CompletableFuture
import org.dolphinemu.dolphinemu.utils.ContentHandler import org.dolphinemu.dolphinemu.utils.ContentHandler
import org.dolphinemu.dolphinemu.utils.DirectoryInitialization import org.dolphinemu.dolphinemu.utils.DirectoryInitialization
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper 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.ThreadUtil
import org.dolphinemu.dolphinemu.utils.WiiUtils import org.dolphinemu.dolphinemu.utils.WiiUtils
import java.util.Arrays 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) { class MainPresenter(private val mainView: MainView, private val activity: FragmentActivity) {
private var dirToAdd: String? = null private var dirToAdd: String? = null
@@ -104,8 +104,9 @@ class MainPresenter(private val mainView: MainView, private val activity: Fragme
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)) {
PermissionsHandler.requestWritePermission(activity) PermissionsHandler.requestWritePermission(activity)
}
val versionName = BuildConfig.VERSION_NAME val versionName = BuildConfig.VERSION_NAME
mainView.setVersionString(versionName) mainView.setVersionString(versionName)
@@ -118,85 +119,83 @@ class MainPresenter(private val mainView: MainView, private val activity: Fragme
} }
fun launchFileListActivity() { fun launchFileListActivity() {
val intent = val intent = if (DirectoryInitialization.preferOldFolderPicker(activity)) {
if (DirectoryInitialization.preferOldFolderPicker(activity)) { FileBrowserHelper.createDirectoryPickerIntent(
FileBrowserHelper.createDirectoryPickerIntent( activity,
activity, FileBrowserHelper.GAME_EXTENSIONS,
FileBrowserHelper.GAME_EXTENSIONS, )
) } else {
} else { Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) }
}
requestDirectory.launch(intent) requestDirectory.launch(intent)
} }
fun handleOptionSelection(itemId: Int, activity: ComponentActivity): Boolean = fun handleOptionSelection(itemId: Int, activity: ComponentActivity): Boolean = when (itemId) {
when (itemId) { R.id.menu_settings -> {
R.id.menu_settings -> { mainView.launchSettingsActivity(MenuTag.SETTINGS)
mainView.launchSettingsActivity(MenuTag.SETTINGS) true
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
} }
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() { fun onResume() {
if (dirToAdd != null) { if (dirToAdd != null) {
GameFileCache.addGameFolder(dirToAdd!!) 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 recursive = BooleanSetting.MAIN_RECURSIVE_ISO_PATHS.boolean
val childNames = ContentHandler.getChildNames(uri, recursive) val childNames = ContentHandler.getChildNames(uri, recursive)
if (Arrays.stream(childNames).noneMatch { if (Arrays.stream(childNames).noneMatch {
FileBrowserHelper.GAME_EXTENSIONS FileBrowserHelper.GAME_EXTENSIONS.contains(
.contains(FileBrowserHelper.getExtension(it, false)) FileBrowserHelper.getExtension(
}) { it, false
MaterialAlertDialogBuilder(activity)
.setMessage(
activity.getString(
R.string.wrong_file_extension_in_directory,
FileBrowserHelper.setToSortedDelimitedString(FileBrowserHelper.GAME_EXTENSIONS)
) )
) )
.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 contentResolver = activity.contentResolver
val canonicalizedUri = contentResolver.canonicalize(uri) val canonicalizedUri = contentResolver.canonicalize(uri)
if (canonicalizedUri != null) if (canonicalizedUri != null) {
uri = canonicalizedUri uri = canonicalizedUri
}
val takeFlags = result.flags and Intent.FLAG_GRANT_READ_URI_PERMISSION val takeFlags = result.flags and Intent.FLAG_GRANT_READ_URI_PERMISSION
activity.contentResolver.takePersistableUriPermission(uri, takeFlags) activity.contentResolver.takePersistableUriPermission(uri, takeFlags)
@@ -253,10 +253,7 @@ class MainPresenter(private val mainView: MainView, private val activity: Fragme
fun installWAD(path: String?) { fun installWAD(path: String?) {
ThreadUtil.runOnThreadAndShowResult( ThreadUtil.runOnThreadAndShowResult(
activity, activity, R.string.import_in_progress, 0, {
R.string.import_in_progress,
0,
{
val success = WiiUtils.installWAD(path!!) val success = WiiUtils.installWAD(path!!)
val message = val message =
if (success) R.string.wad_install_success else R.string.wad_install_failure if (success) R.string.wad_install_success else R.string.wad_install_failure
@@ -265,33 +262,28 @@ class MainPresenter(private val mainView: MainView, private val activity: Fragme
} }
fun importWiiSave(path: String?) { fun importWiiSave(path: String?) {
val canOverwriteFuture = CompletableFuture<Boolean>()
ThreadUtil.runOnThreadAndShowResult( ThreadUtil.runOnThreadAndShowResult(
activity, activity, R.string.import_in_progress, 0, {
R.string.import_in_progress,
0,
{
val canOverwrite = BooleanSupplier { val canOverwrite = BooleanSupplier {
val latch = CountDownLatch(1)
val decision = AtomicReference<Boolean>()
activity.runOnUiThread { activity.runOnUiThread {
MaterialAlertDialogBuilder(activity) MaterialAlertDialogBuilder(activity).setMessage(R.string.wii_save_exists)
.setMessage(R.string.wii_save_exists)
.setCancelable(false) .setCancelable(false)
.setPositiveButton(R.string.yes) { _: DialogInterface?, _: Int -> .setPositiveButton(R.string.yes) { _: DialogInterface?, _: Int ->
canOverwriteFuture.complete(true) decision.set(true)
} latch.countDown()
.setNegativeButton(R.string.no) { _: DialogInterface?, _: Int -> }.setNegativeButton(R.string.no) { _: DialogInterface?, _: Int ->
canOverwriteFuture.complete(false) decision.set(false)
} latch.countDown()
.show() }.show()
} }
try { try {
return@BooleanSupplier canOverwriteFuture.get() latch.await()
} catch (e: ExecutionException) {
// Shouldn't happen
throw RuntimeException(e)
} catch (e: InterruptedException) { } catch (e: InterruptedException) {
throw RuntimeException(e) throw RuntimeException(e)
} }
decision.get() ?: false
} }
val message: Int = when (WiiUtils.importWiiSave(path!!, canOverwrite)) { val message: Int = when (WiiUtils.importWiiSave(path!!, canOverwrite)) {
@@ -306,23 +298,18 @@ class MainPresenter(private val mainView: MainView, private val activity: Fragme
} }
fun importNANDBin(path: String?) { fun importNANDBin(path: String?) {
MaterialAlertDialogBuilder(activity) MaterialAlertDialogBuilder(activity).setMessage(R.string.nand_import_warning)
.setMessage(R.string.nand_import_warning)
.setNegativeButton(R.string.no) { dialog: DialogInterface, _: Int -> dialog.dismiss() } .setNegativeButton(R.string.no) { dialog: DialogInterface, _: Int -> dialog.dismiss() }
.setPositiveButton(R.string.yes) { dialog: DialogInterface, _: Int -> .setPositiveButton(R.string.yes) { dialog: DialogInterface, _: Int ->
dialog.dismiss() dialog.dismiss()
ThreadUtil.runOnThreadAndShowResult( ThreadUtil.runOnThreadAndShowResult(
activity, activity, R.string.import_in_progress, R.string.do_not_close_app, {
R.string.import_in_progress,
R.string.do_not_close_app,
{
// ImportNANDBin unfortunately doesn't provide any result value... // ImportNANDBin unfortunately doesn't provide any result value...
// It does however show a panic alert if something goes wrong. // It does however show a panic alert if something goes wrong.
WiiUtils.importNANDBin(path!!) WiiUtils.importNANDBin(path!!)
null null
}) })
} }.show()
.show()
} }
private fun launchOnlineUpdate() { private fun launchOnlineUpdate() {
@@ -332,8 +319,7 @@ class MainPresenter(private val mainView: MainView, private val activity: Fragme
launchUpdateProgressBarFragment(activity) launchUpdateProgressBarFragment(activity)
} else { } else {
SystemMenuNotInstalledDialogFragment().show( SystemMenuNotInstalledDialogFragment().show(
activity.supportFragmentManager, activity.supportFragmentManager, SystemMenuNotInstalledDialogFragment.TAG
SystemMenuNotInstalledDialogFragment.TAG
) )
} }
} }
@@ -344,8 +330,7 @@ class MainPresenter(private val mainView: MainView, private val activity: Fragme
EmulationActivity.launchSystemMenu(activity) EmulationActivity.launchSystemMenu(activity)
} else { } else {
SystemMenuNotInstalledDialogFragment().show( SystemMenuNotInstalledDialogFragment().show(
activity.supportFragmentManager, activity.supportFragmentManager, SystemMenuNotInstalledDialogFragment.TAG
SystemMenuNotInstalledDialogFragment.TAG
) )
} }
} }
@@ -372,8 +357,9 @@ class MainPresenter(private val mainView: MainView, private val activity: Fragme
private fun launchUpdateProgressBarFragment(activity: FragmentActivity) { private fun launchUpdateProgressBarFragment(activity: FragmentActivity) {
val progressBarFragment = SystemUpdateProgressBarDialogFragment() val progressBarFragment = SystemUpdateProgressBarDialogFragment()
progressBarFragment progressBarFragment.show(
.show(activity.supportFragmentManager, SystemUpdateProgressBarDialogFragment.TAG) activity.supportFragmentManager, SystemUpdateProgressBarDialogFragment.TAG
)
progressBarFragment.isCancelable = false progressBarFragment.isCancelable = false
} }
} }
@@ -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<T> implements Future<T>
{
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();
}
}
}