diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/AppLinkActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/AppLinkActivity.java index 01a87fdd3a..ed5dac911e 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/AppLinkActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/AppLinkActivity.java @@ -2,16 +2,12 @@ package org.dolphinemu.dolphinemu.activities; -import android.content.BroadcastReceiver; -import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.net.Uri; import android.os.Bundle; import android.util.Log; import androidx.fragment.app.FragmentActivity; -import androidx.localbroadcastmanager.content.LocalBroadcastManager; import org.dolphinemu.dolphinemu.model.GameFile; import org.dolphinemu.dolphinemu.services.GameFileCacheManager; @@ -69,22 +65,13 @@ public class AppLinkActivity extends FragmentActivity mAfterDirectoryInitializationRunner = new AfterDirectoryInitializationRunner(); mAfterDirectoryInitializationRunner.run(this, true, () -> tryPlay(playAction)); - IntentFilter gameFileCacheIntentFilter = new IntentFilter(GameFileCacheManager.DONE_LOADING); - - BroadcastReceiver gameFileCacheReceiver = new BroadcastReceiver() + GameFileCacheManager.isLoading().observe(this, (isLoading) -> { - @Override - public void onReceive(Context context, Intent intent) + if (!isLoading && DirectoryInitialization.areDolphinDirectoriesReady()) { - if (DirectoryInitialization.areDolphinDirectoriesReady()) - { - tryPlay(playAction); - } + tryPlay(playAction); } - }; - - LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(this); - broadcastManager.registerReceiver(gameFileCacheReceiver, gameFileCacheIntentFilter); + }); DirectoryInitialization.start(this); GameFileCacheManager.startLoad(this); @@ -110,7 +97,7 @@ public class AppLinkActivity extends FragmentActivity // If game == null and the load isn't done, wait for the next GameFileCacheService broadcast. // If game == null and the load is done, call play with a null game, making us exit in failure. - if (game != null || !GameFileCacheManager.isLoading()) + if (game != null || !GameFileCacheManager.isLoading().getValue()) { play(action, game); } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/services/GameFileCacheManager.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/services/GameFileCacheManager.java index f4ce251be1..d08d28f989 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/services/GameFileCacheManager.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/services/GameFileCacheManager.java @@ -3,11 +3,10 @@ package org.dolphinemu.dolphinemu.services; import android.content.Context; -import android.content.Intent; -import androidx.localbroadcastmanager.content.LocalBroadcastManager; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; -import org.dolphinemu.dolphinemu.DolphinApplication; import org.dolphinemu.dolphinemu.model.GameFile; import org.dolphinemu.dolphinemu.model.GameFileCache; import org.dolphinemu.dolphinemu.ui.platform.Platform; @@ -18,41 +17,32 @@ import java.util.Arrays; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; /** * Loads game list data on a separate thread. */ public final class GameFileCacheManager { - /** - * This is broadcast when the contents of the cache change. - */ - public static final String CACHE_UPDATED = "org.dolphinemu.dolphinemu.GAME_FILE_CACHE_UPDATED"; - - /** - * This is broadcast when the service is done with all requested work, regardless of whether - * the contents of the cache actually changed. (Maybe the cache was already up to date.) - */ - public static final String DONE_LOADING = - "org.dolphinemu.dolphinemu.GAME_FILE_CACHE_DONE_LOADING"; - private static GameFileCache gameFileCache = null; - private static final AtomicReference gameFiles = - new AtomicReference<>(new GameFile[]{}); + private static final MutableLiveData gameFiles = + new MutableLiveData<>(new GameFile[]{}); private static final ExecutorService executor = Executors.newFixedThreadPool(1); - private static final AtomicBoolean loadInProgress = new AtomicBoolean(false); - private static final AtomicBoolean rescanInProgress = new AtomicBoolean(false); + private static final MutableLiveData loadInProgress = new MutableLiveData<>(false); + private static final MutableLiveData rescanInProgress = new MutableLiveData<>(false); private GameFileCacheManager() { } + public static LiveData getGameFiles() + { + return gameFiles; + } + public static List getGameFilesForPlatform(Platform platform) { - GameFile[] allGames = gameFiles.get(); + GameFile[] allGames = gameFiles.getValue(); ArrayList platformGames = new ArrayList<>(); for (GameFile game : allGames) { @@ -66,7 +56,7 @@ public final class GameFileCacheManager public static GameFile getGameFileByGameId(String gameId) { - GameFile[] allGames = gameFiles.get(); + GameFile[] allGames = gameFiles.getValue(); for (GameFile game : allGames) { if (game.getGameId().equals(gameId)) @@ -81,7 +71,7 @@ public final class GameFileCacheManager { GameFile matchWithoutRevision = null; - GameFile[] allGames = gameFiles.get(); + GameFile[] allGames = gameFiles.getValue(); for (GameFile otherGame : allGames) { if (game.getGameId().equals(otherGame.getGameId()) && @@ -107,19 +97,24 @@ public final class GameFileCacheManager } /** - * Returns true if in the process of either loading the cache or rescanning. + * Returns true if in the process of loading the cache for the first time. */ - public static boolean isLoading() + public static LiveData isLoading() { - return loadInProgress.get(); + return loadInProgress; } /** * Returns true if in the process of rescanning. */ - public static boolean isRescanning() + public static LiveData isRescanning() { - return rescanInProgress.get(); + return rescanInProgress; + } + + public static boolean isLoadingOrRescanning() + { + return loadInProgress.getValue() || rescanInProgress.getValue(); } /** @@ -129,8 +124,9 @@ public final class GameFileCacheManager */ public static void startLoad(Context context) { - if (loadInProgress.compareAndSet(false, true)) + if (!loadInProgress.getValue()) { + loadInProgress.setValue(true); new AfterDirectoryInitializationRunner().run(context, false, () -> executor.execute(GameFileCacheManager::load)); } @@ -143,8 +139,9 @@ public final class GameFileCacheManager */ public static void startRescan(Context context) { - if (rescanInProgress.compareAndSet(false, true)) + if (!rescanInProgress.getValue()) { + rescanInProgress.setValue(true); new AfterDirectoryInitializationRunner().run(context, false, () -> executor.execute(GameFileCacheManager::rescan)); } @@ -155,7 +152,7 @@ public final class GameFileCacheManager // Common case: The game is in the cache, so just grab it from there. // (Actually, addOrGet already checks for this case, but we want to avoid calling it if possible // because onHandleIntent may hold a lock on gameFileCache for extended periods of time.) - GameFile[] allGames = gameFiles.get(); + GameFile[] allGames = gameFiles.getValue(); for (GameFile game : allGames) { if (game.getPath().equals(gamePath)) @@ -189,14 +186,11 @@ public final class GameFileCacheManager if (gameFileCache.getSize() != 0) { updateGameFileArray(); - sendBroadcast(CACHE_UPDATED); } } } - loadInProgress.set(false); - if (!rescanInProgress.get()) - sendBroadcast(DONE_LOADING); + loadInProgress.postValue(false); } /** @@ -218,14 +212,12 @@ public final class GameFileCacheManager if (changed) { updateGameFileArray(); - sendBroadcast(CACHE_UPDATED); } boolean additionalMetadataChanged = gameFileCache.updateAdditionalMetadata(); if (additionalMetadataChanged) { updateGameFileArray(); - sendBroadcast(CACHE_UPDATED); } if (changed || additionalMetadataChanged) @@ -234,21 +226,13 @@ public final class GameFileCacheManager } } - rescanInProgress.set(false); - if (!loadInProgress.get()) - sendBroadcast(DONE_LOADING); + rescanInProgress.postValue(false); } private static void updateGameFileArray() { GameFile[] gameFilesTemp = gameFileCache.getAllGames(); Arrays.sort(gameFilesTemp, (lhs, rhs) -> lhs.getTitle().compareToIgnoreCase(rhs.getTitle())); - gameFiles.set(gameFilesTemp); - } - - private static void sendBroadcast(String action) - { - LocalBroadcastManager.getInstance(DolphinApplication.getAppContext()) - .sendBroadcast(new Intent(action)); + gameFiles.postValue(gameFilesTemp); } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainPresenter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainPresenter.java index 9aa95f3998..778e38f6a1 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainPresenter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainPresenter.java @@ -2,16 +2,14 @@ package org.dolphinemu.dolphinemu.ui.main; -import android.app.Activity; -import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.net.Uri; import androidx.appcompat.app.AlertDialog; -import androidx.localbroadcastmanager.content.LocalBroadcastManager; +import androidx.core.app.ComponentActivity; +import androidx.lifecycle.Observer; import org.dolphinemu.dolphinemu.BuildConfig; import org.dolphinemu.dolphinemu.R; @@ -42,14 +40,13 @@ public final class MainPresenter private static boolean sShouldRescanLibrary = true; private final MainView mView; - private final Context mContext; - private BroadcastReceiver mBroadcastReceiver = null; + private final ComponentActivity mActivity; private String mDirToAdd; - public MainPresenter(MainView view, Context context) + public MainPresenter(MainView view, ComponentActivity activity) { mView = view; - mContext = context; + mActivity = activity; } public void onCreate() @@ -57,34 +54,18 @@ public final class MainPresenter String versionName = BuildConfig.VERSION_NAME; mView.setVersionString(versionName); - IntentFilter filter = new IntentFilter(); - filter.addAction(GameFileCacheManager.CACHE_UPDATED); - filter.addAction(GameFileCacheManager.DONE_LOADING); - mBroadcastReceiver = new BroadcastReceiver() + GameFileCacheManager.getGameFiles().observe(mActivity, (gameFiles) -> mView.showGames()); + + Observer refreshObserver = (isLoading) -> { - @Override - public void onReceive(Context context, Intent intent) - { - switch (intent.getAction()) - { - case GameFileCacheManager.CACHE_UPDATED: - mView.showGames(); - break; - case GameFileCacheManager.DONE_LOADING: - mView.setRefreshing(false); - break; - } - } + mView.setRefreshing(GameFileCacheManager.isLoadingOrRescanning()); }; - LocalBroadcastManager.getInstance(mContext).registerReceiver(mBroadcastReceiver, filter); + GameFileCacheManager.isLoading().observe(mActivity, refreshObserver); + GameFileCacheManager.isRescanning().observe(mActivity, refreshObserver); } public void onDestroy() { - if (mBroadcastReceiver != null) - { - LocalBroadcastManager.getInstance(mContext).unregisterReceiver(mBroadcastReceiver); - } } public void onFabClick() @@ -140,12 +121,11 @@ public final class MainPresenter mDirToAdd = null; } - if (sShouldRescanLibrary && !GameFileCacheManager.isRescanning()) + if (sShouldRescanLibrary && !GameFileCacheManager.isRescanning().getValue()) { - new AfterDirectoryInitializationRunner().run(mContext, false, () -> + new AfterDirectoryInitializationRunner().run(mActivity, false, () -> { - mView.setRefreshing(true); - GameFileCacheManager.startRescan(mContext); + GameFileCacheManager.startRescan(mActivity); }); } @@ -172,20 +152,20 @@ public final class MainPresenter if (Arrays.stream(childNames).noneMatch((name) -> FileBrowserHelper.GAME_EXTENSIONS.contains( FileBrowserHelper.getExtension(name, false)))) { - AlertDialog.Builder builder = new AlertDialog.Builder(mContext, R.style.DolphinDialogBase); - builder.setMessage(mContext.getString(R.string.wrong_file_extension_in_directory, + AlertDialog.Builder builder = new AlertDialog.Builder(mActivity, R.style.DolphinDialogBase); + builder.setMessage(mActivity.getString(R.string.wrong_file_extension_in_directory, FileBrowserHelper.setToSortedDelimitedString(FileBrowserHelper.GAME_EXTENSIONS))); builder.setPositiveButton(R.string.ok, null); builder.show(); } - ContentResolver contentResolver = mContext.getContentResolver(); + ContentResolver contentResolver = mActivity.getContentResolver(); Uri canonicalizedUri = contentResolver.canonicalize(uri); if (canonicalizedUri != null) uri = canonicalizedUri; int takeFlags = result.getFlags() & Intent.FLAG_GRANT_READ_URI_PERMISSION; - mContext.getContentResolver().takePersistableUriPermission(uri, takeFlags); + mActivity.getContentResolver().takePersistableUriPermission(uri, takeFlags); mDirToAdd = uri.toString(); } @@ -196,24 +176,22 @@ public final class MainPresenter { boolean success = WiiUtils.installWAD(path); int message = success ? R.string.wad_install_success : R.string.wad_install_failure; - return mContext.getResources().getString(message); + return mActivity.getResources().getString(message); }); } public void importWiiSave(String path) { - final Activity mainPresenterActivity = (Activity) mContext; - CompletableFuture canOverwriteFuture = new CompletableFuture<>(); runOnThreadAndShowResult(R.string.import_in_progress, 0, () -> { BooleanSupplier canOverwrite = () -> { - mainPresenterActivity.runOnUiThread(() -> + mActivity.runOnUiThread(() -> { AlertDialog.Builder builder = - new AlertDialog.Builder(mContext, R.style.DolphinDialogBase); + new AlertDialog.Builder(mActivity, R.style.DolphinDialogBase); builder.setMessage(R.string.wii_save_exists); builder.setCancelable(false); builder.setPositiveButton(R.string.yes, (dialog, i) -> canOverwriteFuture.complete(true)); @@ -252,14 +230,14 @@ public final class MainPresenter message = R.string.wii_save_import_error; break; } - return mContext.getResources().getString(message); + return mActivity.getResources().getString(message); }); } public void importNANDBin(String path) { AlertDialog.Builder builder = - new AlertDialog.Builder(mContext, R.style.DolphinDialogBase); + new AlertDialog.Builder(mActivity, R.style.DolphinDialogBase); builder.setMessage(R.string.nand_import_warning); builder.setNegativeButton(R.string.no, (dialog, i) -> dialog.dismiss()); @@ -281,33 +259,31 @@ public final class MainPresenter private void runOnThreadAndShowResult(int progressTitle, int progressMessage, Supplier f) { - final Activity mainPresenterActivity = (Activity) mContext; - - AlertDialog progressDialog = new AlertDialog.Builder(mContext, R.style.DolphinDialogBase) + AlertDialog progressDialog = new AlertDialog.Builder(mActivity, R.style.DolphinDialogBase) .create(); progressDialog.setTitle(progressTitle); if (progressMessage != 0) - progressDialog.setMessage(mContext.getResources().getString(progressMessage)); + progressDialog.setMessage(mActivity.getResources().getString(progressMessage)); progressDialog.setCancelable(false); progressDialog.show(); new Thread(() -> { String result = f.get(); - mainPresenterActivity.runOnUiThread(() -> + mActivity.runOnUiThread(() -> { progressDialog.dismiss(); if (result != null) { AlertDialog.Builder builder = - new AlertDialog.Builder(mContext, R.style.DolphinDialogBase); + new AlertDialog.Builder(mActivity, R.style.DolphinDialogBase); builder.setMessage(result); builder.setPositiveButton(R.string.ok, (dialog, i) -> dialog.dismiss()); builder.show(); } }); - }, mContext.getResources().getString(progressTitle)).start(); + }, mActivity.getResources().getString(progressTitle)).start(); } public static void skipRescanningLibrary() diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/TvMainActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/TvMainActivity.java index 15f8c866e7..58f3bbafb7 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/TvMainActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/TvMainActivity.java @@ -122,7 +122,7 @@ public final class TvMainActivity extends FragmentActivity mSwipeRefresh.setOnRefreshListener(this); - setRefreshing(GameFileCacheManager.isLoading()); + setRefreshing(GameFileCacheManager.isLoadingOrRescanning()); final FragmentManager fragmentManager = getSupportFragmentManager(); mBrowseFragment = new BrowseSupportFragment(); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/platform/PlatformGamesFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/platform/PlatformGamesFragment.java index 641802df54..feb08adfcd 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/platform/PlatformGamesFragment.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/platform/PlatformGamesFragment.java @@ -73,7 +73,7 @@ public final class PlatformGamesFragment extends Fragment implements PlatformGam mRecyclerView.addItemDecoration(new GameAdapter.SpacesItemDecoration(8)); - setRefreshing(GameFileCacheManager.isLoading()); + setRefreshing(GameFileCacheManager.isLoadingOrRescanning()); showGames(); }