diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java index 174d2d1ff2..dc3c48c338 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java @@ -349,10 +349,29 @@ public final class NativeLibrary public static native int DefaultCPUCore(); + public static native void ReloadConfig(); + + /** + * Initializes the native parts of the app. + * + * Should be called at app start before running any other native code + * (other than the native methods in DirectoryInitialization). + */ + public static native void Initialize(); + + /** + * Tells analytics that Dolphin has been started. + * + * Since users typically don't explicitly close Android apps, it's appropriate to + * call this not only when the app starts but also when the user returns to the app + * after not using it for a significant amount of time. + */ + public static native void ReportStartToAnalytics(); + /** * Begins emulation. */ - public static native void Run(String[] path, boolean firstOpen); + public static native void Run(String[] path); /** * Begins emulation from the specified savestate. diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/Settings.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/Settings.java index ca34fc575b..d3cb2b6c66 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/Settings.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/Settings.java @@ -2,6 +2,7 @@ package org.dolphinemu.dolphinemu.features.settings.model; import android.text.TextUtils; +import org.dolphinemu.dolphinemu.NativeLibrary; import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivityView; import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile; @@ -175,6 +176,9 @@ public class Settings SettingsFile.saveFile(fileName, iniSections, view); } + + // Notify the native code of the changes + NativeLibrary.ReloadConfig(); } else { diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.java index f817414add..1ff9ba8ea0 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.java @@ -82,12 +82,7 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C mPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity()); String[] gamePaths = getArguments().getStringArray(KEY_GAMEPATHS); - SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity()); - boolean firstOpen = preferences.getBoolean(StartupHandler.NEW_SESSION, true); - SharedPreferences.Editor sPrefsEditor = preferences.edit(); - sPrefsEditor.putBoolean(StartupHandler.NEW_SESSION, false); - sPrefsEditor.apply(); - mEmulationState = new EmulationState(gamePaths, getTemporaryStateFilePath(), firstOpen); + mEmulationState = new EmulationState(gamePaths, getTemporaryStateFilePath()); } /** @@ -271,12 +266,10 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C private Surface mSurface; private boolean mRunWhenSurfaceIsValid; private boolean loadPreviousTemporaryState; - private boolean firstOpen; private final String temporaryStatePath; - EmulationState(String[] gamePaths, String temporaryStatePath, boolean firstOpen) + EmulationState(String[] gamePaths, String temporaryStatePath) { - this.firstOpen = firstOpen; mGamePaths = gamePaths; this.temporaryStatePath = temporaryStatePath; // Starting state is stopped. @@ -420,7 +413,7 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C else { Log.debug("[EmulationFragment] Starting emulation thread."); - NativeLibrary.Run(mGamePaths, firstOpen); + NativeLibrary.Run(mGamePaths); } }, "NativeEmulation"); mEmulationThread.start(); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/services/GameFileCacheService.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/services/GameFileCacheService.java index 5cddf93f5d..440f70a5bb 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/services/GameFileCacheService.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/services/GameFileCacheService.java @@ -8,6 +8,7 @@ import android.support.v4.content.LocalBroadcastManager; import org.dolphinemu.dolphinemu.model.GameFile; import org.dolphinemu.dolphinemu.model.GameFileCache; import org.dolphinemu.dolphinemu.ui.platform.Platform; +import org.dolphinemu.dolphinemu.utils.AfterDirectoryInitializationRunner; import java.io.File; import java.util.ArrayList; @@ -107,7 +108,8 @@ public final class GameFileCacheService extends IntentService */ public static void startLoad(Context context) { - startService(context, ACTION_LOAD); + new AfterDirectoryInitializationRunner().run(context, + () -> startService(context, ACTION_LOAD)); } /** @@ -117,7 +119,8 @@ public final class GameFileCacheService extends IntentService */ public static void startRescan(Context context) { - startService(context, ACTION_RESCAN); + new AfterDirectoryInitializationRunner().run(context, + () -> startService(context, ACTION_RESCAN)); } public static GameFile addOrGet(String gamePath) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/AfterDirectoryInitializationRunner.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/AfterDirectoryInitializationRunner.java new file mode 100644 index 0000000000..0ce87b2b8a --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/AfterDirectoryInitializationRunner.java @@ -0,0 +1,50 @@ +package org.dolphinemu.dolphinemu.utils; + +import android.content.Context; +import android.content.IntentFilter; +import android.support.v4.content.LocalBroadcastManager; + +public class AfterDirectoryInitializationRunner +{ + private DirectoryStateReceiver directoryStateReceiver; + + /** + * Executes a Runnable after directory initialization has finished. + * + * If this is called when directory initialization already is done, + * the Runnable will be executed immediately. If this is called before + * directory initialization is done, the Runnable will be executed + * after directory initialization finishes successfully, or never + * in case directory initialization doesn't finish successfully. + * + * Calling this function multiple times per object is not supported. + */ + public void run(Context context, Runnable runnable) + { + if (!DirectoryInitialization.areDolphinDirectoriesReady()) + { + // Wait for directories to get initialized + IntentFilter statusIntentFilter = new IntentFilter( + DirectoryInitialization.BROADCAST_ACTION); + + directoryStateReceiver = new DirectoryStateReceiver(directoryInitializationState -> + { + if (directoryInitializationState == + DirectoryInitialization.DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED) + { + LocalBroadcastManager.getInstance(context).unregisterReceiver(directoryStateReceiver); + directoryStateReceiver = null; + runnable.run(); + } + }); + // Registers the DirectoryStateReceiver and its intent filters + LocalBroadcastManager.getInstance(context).registerReceiver( + directoryStateReceiver, + statusIntentFilter); + } + else + { + runnable.run(); + } + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Analytics.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Analytics.java index 6f3e07cbb5..cb7aec2056 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Analytics.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Analytics.java @@ -18,8 +18,6 @@ import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile; public class Analytics { - private static DirectoryStateReceiver directoryStateReceiver; - private static final String analyticsAsked = Settings.SECTION_ANALYTICS + "_" + SettingsFile.KEY_ANALYTICS_PERMISSION_ASKED; private static final String analyticsEnabled = @@ -35,31 +33,8 @@ public class Analytics SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); if (!preferences.getBoolean(analyticsAsked, false)) { - if (!DirectoryInitialization.areDolphinDirectoriesReady()) - { - // Wait for directories to get initialized - IntentFilter statusIntentFilter = new IntentFilter( - DirectoryInitialization.BROADCAST_ACTION); - - directoryStateReceiver = new DirectoryStateReceiver(directoryInitializationState -> - { - if (directoryInitializationState == - DirectoryInitialization.DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED) - { - LocalBroadcastManager.getInstance(context).unregisterReceiver(directoryStateReceiver); - directoryStateReceiver = null; - showMessage(context, preferences); - } - }); - // Registers the DirectoryStateReceiver and its intent filters - LocalBroadcastManager.getInstance(context).registerReceiver( - directoryStateReceiver, - statusIntentFilter); - } - else - { - showMessage(context, preferences); - } + new AfterDirectoryInitializationRunner().run(context, + () -> showMessage(context, preferences)); } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/DirectoryInitialization.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/DirectoryInitialization.java index 47925df998..727240b478 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/DirectoryInitialization.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/DirectoryInitialization.java @@ -67,6 +67,8 @@ public final class DirectoryInitialization { initializeInternalStorage(context); initializeExternalStorage(context); + NativeLibrary.Initialize(); + NativeLibrary.ReportStartToAnalytics(); directoryState = DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED; } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/StartupHandler.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/StartupHandler.java index 8631e5ed8d..cdddd4cd37 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/StartupHandler.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/StartupHandler.java @@ -8,15 +8,15 @@ import android.preference.PreferenceManager; import android.support.v4.app.FragmentActivity; import android.text.TextUtils; +import org.dolphinemu.dolphinemu.NativeLibrary; import org.dolphinemu.dolphinemu.activities.EmulationActivity; import java.util.Date; public final class StartupHandler { - public static final String NEW_SESSION = "NEW_SESSION"; public static final String LAST_CLOSED = "LAST_CLOSED"; - public static final Long SESSION_TIMEOUT = 21600000L; // 6 hours in milliseconds + public static final long SESSION_TIMEOUT = 21600000L; // 6 hours in milliseconds public static void HandleInit(FragmentActivity parent) { @@ -66,15 +66,13 @@ public final class StartupHandler */ public static void checkSessionReset(Context context) { - Long currentTime = new Date(System.currentTimeMillis()).getTime(); + long currentTime = new Date(System.currentTimeMillis()).getTime(); SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); - Long lastOpen = preferences.getLong(LAST_CLOSED, 0); + long lastOpen = preferences.getLong(LAST_CLOSED, 0); if (currentTime > (lastOpen + SESSION_TIMEOUT)) { - // Passed at emulation start to trigger first open event. - SharedPreferences.Editor sPrefsEditor = preferences.edit(); - sPrefsEditor.putBoolean(NEW_SESSION, true); - sPrefsEditor.apply(); + new AfterDirectoryInitializationRunner().run(context, + () -> NativeLibrary.ReportStartToAnalytics()); } } } diff --git a/Source/Android/jni/MainAndroid.cpp b/Source/Android/jni/MainAndroid.cpp index 3fcae32df8..e1b19314c3 100644 --- a/Source/Android/jni/MainAndroid.cpp +++ b/Source/Android/jni/MainAndroid.cpp @@ -245,12 +245,14 @@ JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_DefaultCPUCo JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetProfiling(JNIEnv* env, jobject obj, jboolean enable); +JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Initialize(JNIEnv* env, + jobject obj); +JNIEXPORT void JNICALL +Java_org_dolphinemu_dolphinemu_NativeLibrary_ReportStartToAnalytics(JNIEnv* env, jobject obj); JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_WriteProfileResults(JNIEnv* env, jobject obj); - -JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run__Ljava_lang_String_2Z( - JNIEnv* env, jobject obj, jstring jFile, jboolean jfirstOpen); - +JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run__Ljava_lang_String_2( + JNIEnv* env, jobject obj, jstring jFile); JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run__Ljava_lang_String_2Ljava_lang_String_2Z( JNIEnv* env, jobject obj, jstring jFile, jstring jSavestate, jboolean jDeleteSavestate); @@ -585,6 +587,27 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_ReloadWiimot Wiimote::LoadConfig(); } +JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_ReloadConfig(JNIEnv* env, + jobject obj) +{ + SConfig::GetInstance().LoadSettings(); +} + +JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Initialize(JNIEnv* env, + jobject obj) +{ + Common::RegisterMsgAlertHandler(&MsgAlert); + Common::AndroidSetReportHandler(&ReportSend); + DolphinAnalytics::AndroidSetGetValFunc(&GetAnalyticValue); + UICommon::Init(); +} + +JNIEXPORT void JNICALL +Java_org_dolphinemu_dolphinemu_NativeLibrary_ReportStartToAnalytics(JNIEnv* env, jobject obj) +{ + DolphinAnalytics::Instance().ReportDolphinStart(GetAnalyticValue("DEVICE_TYPE")); +} + // Returns the scale factor for imgui rendering. // Based on the scaledDensity of the device's display metrics. static float GetRenderSurfaceScale(JNIEnv* env) @@ -630,23 +653,13 @@ static float GetRenderSurfaceScale(JNIEnv* env) return scaled_density; } -static void Run(JNIEnv* env, const std::vector& paths, bool first_open, +static void Run(JNIEnv* env, const std::vector& paths, std::optional savestate_path = {}, bool delete_savestate = false) { ASSERT(!paths.empty()); __android_log_print(ANDROID_LOG_INFO, DOLPHIN_TAG, "Running : %s", paths[0].c_str()); - Common::RegisterMsgAlertHandler(&MsgAlert); - Common::AndroidSetReportHandler(&ReportSend); - DolphinAnalytics::AndroidSetGetValFunc(&GetAnalyticValue); - std::unique_lock guard(s_host_identity_lock); - UICommon::Init(); - - if (first_open) - { - DolphinAnalytics::Instance().ReportDolphinStart(GetAnalyticValue("DEVICE_TYPE")); - } WiimoteReal::InitAdapterClass(); @@ -679,7 +692,6 @@ static void Run(JNIEnv* env, const std::vector& paths, bool first_o Core::Shutdown(); ButtonManager::Shutdown(); - UICommon::Shutdown(); guard.unlock(); if (s_surf) @@ -689,17 +701,17 @@ static void Run(JNIEnv* env, const std::vector& paths, bool first_o } } -JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run___3Ljava_lang_String_2Z( - JNIEnv* env, jobject obj, jobjectArray jPaths, jboolean jfirstOpen) +JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run___3Ljava_lang_String_2( + JNIEnv* env, jobject obj, jobjectArray jPaths) { - Run(env, JStringArrayToVector(env, jPaths), jfirstOpen); + Run(env, JStringArrayToVector(env, jPaths)); } JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run___3Ljava_lang_String_2Ljava_lang_String_2Z( JNIEnv* env, jobject obj, jobjectArray jPaths, jstring jSavestate, jboolean jDeleteSavestate) { - Run(env, JStringArrayToVector(env, jPaths), false, GetJString(env, jSavestate), jDeleteSavestate); + Run(env, JStringArrayToVector(env, jPaths), GetJString(env, jSavestate), jDeleteSavestate); } JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_ChangeDisc(JNIEnv* env, diff --git a/Source/Core/UICommon/GameFile.cpp b/Source/Core/UICommon/GameFile.cpp index 89d38e24f5..f5aefd7f01 100644 --- a/Source/Core/UICommon/GameFile.cpp +++ b/Source/Core/UICommon/GameFile.cpp @@ -46,28 +46,11 @@ namespace constexpr char COVER_URL[] = "https://art.gametdb.com/wii/cover/%s/%s.png"; const std::string EMPTY_STRING; - -bool UseGameCovers() -{ -// We ifdef this out on Android because accessing the config before emulation start makes us crash. -// The Android GUI handles covers in Java anyway, so this doesn't make us lose any functionality. -#ifdef ANDROID - return false; -#else - return Config::Get(Config::MAIN_USE_GAME_COVERS); -#endif -} } // Anonymous namespace DiscIO::Language GameFile::GetConfigLanguage() const { -#ifdef ANDROID - // TODO: Make the Android app load the config at app start instead of emulation start - // so that we can access the user's preference here - return DiscIO::Language::English; -#else return SConfig::GetInstance().GetLanguageAdjustedForRegion(DiscIO::IsWii(m_platform), m_region); -#endif } bool operator==(const GameBanner& lhs, const GameBanner& rhs) @@ -174,7 +157,7 @@ bool GameFile::IsValid() const bool GameFile::CustomCoverChanged() { - if (!m_custom_cover.buffer.empty() || !UseGameCovers()) + if (!m_custom_cover.buffer.empty() || !Config::Get(Config::MAIN_USE_GAME_COVERS)) return false; std::string path, name; @@ -201,7 +184,7 @@ bool GameFile::CustomCoverChanged() void GameFile::DownloadDefaultCover() { - if (!m_default_cover.buffer.empty() || !UseGameCovers()) + if (!m_default_cover.buffer.empty() || !Config::Get(Config::MAIN_USE_GAME_COVERS)) return; const auto cover_path = File::GetUserPath(D_COVERCACHE_IDX) + DIR_SEP; @@ -267,7 +250,7 @@ void GameFile::DownloadDefaultCover() bool GameFile::DefaultCoverChanged() { - if (!m_default_cover.buffer.empty() || !UseGameCovers()) + if (!m_default_cover.buffer.empty() || !Config::Get(Config::MAIN_USE_GAME_COVERS)) return false; const auto cover_path = File::GetUserPath(D_COVERCACHE_IDX) + DIR_SEP;