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:
commit
e0f4111561
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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) {
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -108,4 +108,6 @@ jclass GetCoreDeviceControlClass();
|
||||||
jfieldID GetCoreDeviceControlPointer();
|
jfieldID GetCoreDeviceControlPointer();
|
||||||
jmethodID GetCoreDeviceControlConstructor();
|
jmethodID GetCoreDeviceControlConstructor();
|
||||||
|
|
||||||
|
jmethodID GetRunnableRun();
|
||||||
|
|
||||||
} // namespace IDCache
|
} // namespace IDCache
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue