Merge pull request #11663 from JosJuice/android-config-change-callback

Android: Use config changed callback for tracking recursive scan setting
This commit is contained in:
Mai 2023-12-07 16:48:02 -05:00 committed by GitHub
commit e0f4111561
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 117 additions and 11 deletions

View File

@ -0,0 +1,33 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.settings.model
import androidx.annotation.Keep
/**
* Calls the passed-in Runnable when Dolphin's config changes.
*
* Please note: The Runnable may be called from any thread.
*/
class ConfigChangedCallback(runnable: Runnable) {
@Keep
private var pointer: Long = initialize(runnable)
/**
* Stops the callback from being called in the future.
*/
fun unregister() {
if (pointer != 0L) {
deinitialize(pointer)
pointer = 0L
}
}
companion object {
@JvmStatic
private external fun initialize(runnable: Runnable): Long
@JvmStatic
private external fun deinitialize(pointer: Long)
}
}

View File

@ -8,7 +8,6 @@ import android.widget.Toast
import org.dolphinemu.dolphinemu.NativeLibrary import org.dolphinemu.dolphinemu.NativeLibrary
import org.dolphinemu.dolphinemu.R import org.dolphinemu.dolphinemu.R
import org.dolphinemu.dolphinemu.features.input.model.MappingCommon import org.dolphinemu.dolphinemu.features.input.model.MappingCommon
import org.dolphinemu.dolphinemu.services.GameFileCacheManager
import java.io.Closeable import java.io.Closeable
class Settings : Closeable { class Settings : Closeable {
@ -19,7 +18,6 @@ class Settings : Closeable {
private set private set
private var settingsLoaded = false private var settingsLoaded = false
private var loadedRecursiveIsoPathsValue = false
private val isGameSpecific: Boolean private val isGameSpecific: Boolean
get() = !TextUtils.isEmpty(gameId) get() = !TextUtils.isEmpty(gameId)
@ -41,8 +39,6 @@ class Settings : Closeable {
check(!NativeLibrary.IsRunning()) { "Attempted to load game INI while emulating" } check(!NativeLibrary.IsRunning()) { "Attempted to load game INI while emulating" }
NativeConfig.loadGameInis(gameId, revision) NativeConfig.loadGameInis(gameId, revision)
} }
loadedRecursiveIsoPathsValue = BooleanSetting.MAIN_RECURSIVE_ISO_PATHS.boolean
} }
fun loadSettings(gameId: String, revision: Int, isWii: Boolean) { fun loadSettings(gameId: String, revision: Int, isWii: Boolean) {
@ -65,11 +61,6 @@ class Settings : Closeable {
NativeLibrary.ReloadLoggerConfig() NativeLibrary.ReloadLoggerConfig()
NativeLibrary.UpdateGCAdapterScanThread() NativeLibrary.UpdateGCAdapterScanThread()
if (loadedRecursiveIsoPathsValue != BooleanSetting.MAIN_RECURSIVE_ISO_PATHS.boolean) {
// Refresh game library
GameFileCacheManager.startRescan()
}
} else { } else {
// custom game settings // custom game settings
if (context != null) { if (context != null) {

View File

@ -2,9 +2,14 @@
package org.dolphinemu.dolphinemu.services; package org.dolphinemu.dolphinemu.services;
import android.os.Handler;
import android.os.Looper;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.MutableLiveData;
import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting;
import org.dolphinemu.dolphinemu.features.settings.model.ConfigChangedCallback;
import org.dolphinemu.dolphinemu.model.GameFile; import org.dolphinemu.dolphinemu.model.GameFile;
import org.dolphinemu.dolphinemu.model.GameFileCache; import org.dolphinemu.dolphinemu.model.GameFileCache;
import org.dolphinemu.dolphinemu.ui.platform.Platform; import org.dolphinemu.dolphinemu.ui.platform.Platform;
@ -26,6 +31,7 @@ public final class GameFileCacheManager
new MutableLiveData<>(new GameFile[]{}); new MutableLiveData<>(new GameFile[]{});
private static boolean sFirstLoadDone = false; private static boolean sFirstLoadDone = false;
private static boolean sRunRescanAfterLoad = false; private static boolean sRunRescanAfterLoad = false;
private static boolean sRecursiveScanEnabled;
private static final ExecutorService sExecutor = Executors.newFixedThreadPool(1); private static final ExecutorService sExecutor = Executors.newFixedThreadPool(1);
private static final MutableLiveData<Boolean> sLoadInProgress = new MutableLiveData<>(false); private static final MutableLiveData<Boolean> sLoadInProgress = new MutableLiveData<>(false);
@ -154,8 +160,8 @@ public final class GameFileCacheManager
public static GameFile addOrGet(String gamePath) public static GameFile addOrGet(String gamePath)
{ {
// Common case: The game is in the cache, so just grab it from there. // Common case: The game is in the cache, so just grab it from there. (GameFileCache.addOrGet
// (Actually, addOrGet already checks for this case, but we want to avoid calling it if possible // actually already checks for this case, but we want to avoid calling it if possible
// because the executor thread may hold a lock on sGameFileCache for extended periods of time.) // because the executor thread may hold a lock on sGameFileCache for extended periods of time.)
GameFile[] allGames = sGameFiles.getValue(); GameFile[] allGames = sGameFiles.getValue();
for (GameFile game : allGames) for (GameFile game : allGames)
@ -182,6 +188,7 @@ public final class GameFileCacheManager
if (!sFirstLoadDone) if (!sFirstLoadDone)
{ {
sFirstLoadDone = true; sFirstLoadDone = true;
setUpAutomaticRescan();
sGameFileCache.load(); sGameFileCache.load();
if (sGameFileCache.getSize() != 0) if (sGameFileCache.getSize() != 0)
{ {
@ -191,6 +198,8 @@ public final class GameFileCacheManager
if (sRunRescanAfterLoad) if (sRunRescanAfterLoad)
{ {
// Without this, there will be a short blip where the loading indicator in the GUI disappears
// because neither sLoadInProgress nor sRescanInProgress is true
sRescanInProgress.postValue(true); sRescanInProgress.postValue(true);
} }
@ -258,4 +267,19 @@ public final class GameFileCacheManager
sGameFileCache = new GameFileCache(); sGameFileCache = new GameFileCache();
} }
} }
private static void setUpAutomaticRescan()
{
sRecursiveScanEnabled = BooleanSetting.MAIN_RECURSIVE_ISO_PATHS.getBoolean();
new ConfigChangedCallback(() ->
new Handler(Looper.getMainLooper()).post(() ->
{
boolean recursiveScanEnabled = BooleanSetting.MAIN_RECURSIVE_ISO_PATHS.getBoolean();
if (sRecursiveScanEnabled != recursiveScanEnabled)
{
sRecursiveScanEnabled = recursiveScanEnabled;
startRescan();
}
}));
}
} }

View File

@ -109,6 +109,8 @@ static jclass s_core_device_control_class;
static jfieldID s_core_device_control_pointer; static jfieldID s_core_device_control_pointer;
static jmethodID s_core_device_control_constructor; static jmethodID s_core_device_control_constructor;
static jmethodID s_runnable_run;
namespace IDCache namespace IDCache
{ {
JNIEnv* GetEnvForThread() JNIEnv* GetEnvForThread()
@ -504,6 +506,11 @@ jmethodID GetCoreDeviceControlConstructor()
return s_core_device_control_constructor; return s_core_device_control_constructor;
} }
jmethodID GetRunnableRun()
{
return s_runnable_run;
}
} // namespace IDCache } // namespace IDCache
extern "C" { extern "C" {
@ -709,6 +716,10 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
"(Lorg/dolphinemu/dolphinemu/features/input/model/CoreDevice;J)V"); "(Lorg/dolphinemu/dolphinemu/features/input/model/CoreDevice;J)V");
env->DeleteLocalRef(core_device_control_class); env->DeleteLocalRef(core_device_control_class);
const jclass runnable_class = env->FindClass("java/lang/Runnable");
s_runnable_run = env->GetMethodID(runnable_class, "run", "()V");
env->DeleteLocalRef(runnable_class);
return JNI_VERSION; return JNI_VERSION;
} }

View File

@ -108,4 +108,6 @@ jclass GetCoreDeviceControlClass();
jfieldID GetCoreDeviceControlPointer(); jfieldID GetCoreDeviceControlPointer();
jmethodID GetCoreDeviceControlConstructor(); jmethodID GetCoreDeviceControlConstructor();
jmethodID GetRunnableRun();
} // namespace IDCache } // namespace IDCache

View File

@ -5,6 +5,7 @@ add_library(main SHARED
Cheats/GraphicsMod.cpp Cheats/GraphicsMod.cpp
Cheats/GraphicsModGroup.cpp Cheats/GraphicsModGroup.cpp
Cheats/PatchCheat.cpp Cheats/PatchCheat.cpp
Config/ConfigChangedCallback.cpp
Config/NativeConfig.cpp Config/NativeConfig.cpp
Config/PostProcessing.cpp Config/PostProcessing.cpp
GameList/GameFile.cpp GameList/GameFile.cpp

View File

@ -0,0 +1,44 @@
// Copyright 2023 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <jni.h>
#include "Common/Config/Config.h"
#include "jni/AndroidCommon/AndroidCommon.h"
#include "jni/AndroidCommon/IDCache.h"
struct ConfigChangedCallbackContext
{
jobject runnable;
Config::ConfigChangedCallbackID callback_id;
};
extern "C" {
JNIEXPORT jlong JNICALL
Java_org_dolphinemu_dolphinemu_features_settings_model_ConfigChangedCallback_initialize(
JNIEnv* env, jclass, jobject runnable)
{
auto* context = new ConfigChangedCallbackContext;
jobject runnable_global = env->NewGlobalRef(runnable);
context->runnable = runnable_global;
context->callback_id = Config::AddConfigChangedCallback([runnable_global] {
IDCache::GetEnvForThread()->CallVoidMethod(runnable_global, IDCache::GetRunnableRun());
});
return reinterpret_cast<jlong>(context);
}
JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_features_settings_model_ConfigChangedCallback_deinitialize(
JNIEnv* env, jclass, jlong pointer)
{
auto* context = reinterpret_cast<ConfigChangedCallbackContext*>(pointer);
Config::RemoveConfigChangedCallback(context->callback_id);
env->DeleteGlobalRef(context->runnable);
delete context;
}
}