Merge pull request #8190 from JosJuice/android-init

Android: Call UICommon::Init at app start instead of emulation start
This commit is contained in:
Anthony 2019-08-21 10:14:52 -07:00 committed by GitHub
commit c7fc9126aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 127 additions and 88 deletions

View File

@ -349,10 +349,29 @@ public final class NativeLibrary
public static native int DefaultCPUCore(); 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. * Begins emulation.
*/ */
public static native void Run(String[] path, boolean firstOpen); public static native void Run(String[] path);
/** /**
* Begins emulation from the specified savestate. * Begins emulation from the specified savestate.

View File

@ -2,6 +2,7 @@ package org.dolphinemu.dolphinemu.features.settings.model;
import android.text.TextUtils; import android.text.TextUtils;
import org.dolphinemu.dolphinemu.NativeLibrary;
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivityView; import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivityView;
import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile; import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile;
@ -175,6 +176,9 @@ public class Settings
SettingsFile.saveFile(fileName, iniSections, view); SettingsFile.saveFile(fileName, iniSections, view);
} }
// Notify the native code of the changes
NativeLibrary.ReloadConfig();
} }
else else
{ {

View File

@ -82,12 +82,7 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
mPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity()); mPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
String[] gamePaths = getArguments().getStringArray(KEY_GAMEPATHS); String[] gamePaths = getArguments().getStringArray(KEY_GAMEPATHS);
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity()); mEmulationState = new EmulationState(gamePaths, getTemporaryStateFilePath());
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);
} }
/** /**
@ -271,12 +266,10 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
private Surface mSurface; private Surface mSurface;
private boolean mRunWhenSurfaceIsValid; private boolean mRunWhenSurfaceIsValid;
private boolean loadPreviousTemporaryState; private boolean loadPreviousTemporaryState;
private boolean firstOpen;
private final String temporaryStatePath; private final String temporaryStatePath;
EmulationState(String[] gamePaths, String temporaryStatePath, boolean firstOpen) EmulationState(String[] gamePaths, String temporaryStatePath)
{ {
this.firstOpen = firstOpen;
mGamePaths = gamePaths; mGamePaths = gamePaths;
this.temporaryStatePath = temporaryStatePath; this.temporaryStatePath = temporaryStatePath;
// Starting state is stopped. // Starting state is stopped.
@ -420,7 +413,7 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
else else
{ {
Log.debug("[EmulationFragment] Starting emulation thread."); Log.debug("[EmulationFragment] Starting emulation thread.");
NativeLibrary.Run(mGamePaths, firstOpen); NativeLibrary.Run(mGamePaths);
} }
}, "NativeEmulation"); }, "NativeEmulation");
mEmulationThread.start(); mEmulationThread.start();

View File

@ -8,6 +8,7 @@ import android.support.v4.content.LocalBroadcastManager;
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;
import org.dolphinemu.dolphinemu.utils.AfterDirectoryInitializationRunner;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
@ -107,7 +108,8 @@ public final class GameFileCacheService extends IntentService
*/ */
public static void startLoad(Context context) 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) public static void startRescan(Context context)
{ {
startService(context, ACTION_RESCAN); new AfterDirectoryInitializationRunner().run(context,
() -> startService(context, ACTION_RESCAN));
} }
public static GameFile addOrGet(String gamePath) public static GameFile addOrGet(String gamePath)

View File

@ -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();
}
}
}

View File

@ -18,8 +18,6 @@ import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile;
public class Analytics public class Analytics
{ {
private static DirectoryStateReceiver directoryStateReceiver;
private static final String analyticsAsked = private static final String analyticsAsked =
Settings.SECTION_ANALYTICS + "_" + SettingsFile.KEY_ANALYTICS_PERMISSION_ASKED; Settings.SECTION_ANALYTICS + "_" + SettingsFile.KEY_ANALYTICS_PERMISSION_ASKED;
private static final String analyticsEnabled = private static final String analyticsEnabled =
@ -35,31 +33,8 @@ public class Analytics
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
if (!preferences.getBoolean(analyticsAsked, false)) if (!preferences.getBoolean(analyticsAsked, false))
{ {
if (!DirectoryInitialization.areDolphinDirectoriesReady()) new AfterDirectoryInitializationRunner().run(context,
{ () -> showMessage(context, preferences));
// 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);
}
} }
} }

View File

@ -67,6 +67,8 @@ public final class DirectoryInitialization
{ {
initializeInternalStorage(context); initializeInternalStorage(context);
initializeExternalStorage(context); initializeExternalStorage(context);
NativeLibrary.Initialize();
NativeLibrary.ReportStartToAnalytics();
directoryState = DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED; directoryState = DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED;
} }

View File

@ -8,15 +8,15 @@ import android.preference.PreferenceManager;
import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentActivity;
import android.text.TextUtils; import android.text.TextUtils;
import org.dolphinemu.dolphinemu.NativeLibrary;
import org.dolphinemu.dolphinemu.activities.EmulationActivity; import org.dolphinemu.dolphinemu.activities.EmulationActivity;
import java.util.Date; import java.util.Date;
public final class StartupHandler public final class StartupHandler
{ {
public static final String NEW_SESSION = "NEW_SESSION";
public static final String LAST_CLOSED = "LAST_CLOSED"; 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) public static void HandleInit(FragmentActivity parent)
{ {
@ -66,15 +66,13 @@ public final class StartupHandler
*/ */
public static void checkSessionReset(Context context) 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); SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
Long lastOpen = preferences.getLong(LAST_CLOSED, 0); long lastOpen = preferences.getLong(LAST_CLOSED, 0);
if (currentTime > (lastOpen + SESSION_TIMEOUT)) if (currentTime > (lastOpen + SESSION_TIMEOUT))
{ {
// Passed at emulation start to trigger first open event. new AfterDirectoryInitializationRunner().run(context,
SharedPreferences.Editor sPrefsEditor = preferences.edit(); () -> NativeLibrary.ReportStartToAnalytics());
sPrefsEditor.putBoolean(NEW_SESSION, true);
sPrefsEditor.apply();
} }
} }
} }

View File

@ -245,12 +245,14 @@ JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_DefaultCPUCo
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetProfiling(JNIEnv* env, JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetProfiling(JNIEnv* env,
jobject obj, jobject obj,
jboolean enable); 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 JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_NativeLibrary_WriteProfileResults(JNIEnv* env, jobject obj); Java_org_dolphinemu_dolphinemu_NativeLibrary_WriteProfileResults(JNIEnv* env, jobject obj);
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run__Ljava_lang_String_2(
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run__Ljava_lang_String_2Z( JNIEnv* env, jobject obj, jstring jFile);
JNIEnv* env, jobject obj, jstring jFile, jboolean jfirstOpen);
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_NativeLibrary_Run__Ljava_lang_String_2Ljava_lang_String_2Z( Java_org_dolphinemu_dolphinemu_NativeLibrary_Run__Ljava_lang_String_2Ljava_lang_String_2Z(
JNIEnv* env, jobject obj, jstring jFile, jstring jSavestate, jboolean jDeleteSavestate); 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(); 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. // Returns the scale factor for imgui rendering.
// Based on the scaledDensity of the device's display metrics. // Based on the scaledDensity of the device's display metrics.
static float GetRenderSurfaceScale(JNIEnv* env) static float GetRenderSurfaceScale(JNIEnv* env)
@ -630,23 +653,13 @@ static float GetRenderSurfaceScale(JNIEnv* env)
return scaled_density; return scaled_density;
} }
static void Run(JNIEnv* env, const std::vector<std::string>& paths, bool first_open, static void Run(JNIEnv* env, const std::vector<std::string>& paths,
std::optional<std::string> savestate_path = {}, bool delete_savestate = false) std::optional<std::string> savestate_path = {}, bool delete_savestate = false)
{ {
ASSERT(!paths.empty()); ASSERT(!paths.empty());
__android_log_print(ANDROID_LOG_INFO, DOLPHIN_TAG, "Running : %s", paths[0].c_str()); __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<std::mutex> guard(s_host_identity_lock); std::unique_lock<std::mutex> guard(s_host_identity_lock);
UICommon::Init();
if (first_open)
{
DolphinAnalytics::Instance().ReportDolphinStart(GetAnalyticValue("DEVICE_TYPE"));
}
WiimoteReal::InitAdapterClass(); WiimoteReal::InitAdapterClass();
@ -679,7 +692,6 @@ static void Run(JNIEnv* env, const std::vector<std::string>& paths, bool first_o
Core::Shutdown(); Core::Shutdown();
ButtonManager::Shutdown(); ButtonManager::Shutdown();
UICommon::Shutdown();
guard.unlock(); guard.unlock();
if (s_surf) if (s_surf)
@ -689,17 +701,17 @@ static void Run(JNIEnv* env, const std::vector<std::string>& paths, bool first_o
} }
} }
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run___3Ljava_lang_String_2Z( JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run___3Ljava_lang_String_2(
JNIEnv* env, jobject obj, jobjectArray jPaths, jboolean jfirstOpen) JNIEnv* env, jobject obj, jobjectArray jPaths)
{ {
Run(env, JStringArrayToVector(env, jPaths), jfirstOpen); Run(env, JStringArrayToVector(env, jPaths));
} }
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_NativeLibrary_Run___3Ljava_lang_String_2Ljava_lang_String_2Z( Java_org_dolphinemu_dolphinemu_NativeLibrary_Run___3Ljava_lang_String_2Ljava_lang_String_2Z(
JNIEnv* env, jobject obj, jobjectArray jPaths, jstring jSavestate, jboolean jDeleteSavestate) 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, JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_ChangeDisc(JNIEnv* env,

View File

@ -46,28 +46,11 @@ namespace
constexpr char COVER_URL[] = "https://art.gametdb.com/wii/cover/%s/%s.png"; constexpr char COVER_URL[] = "https://art.gametdb.com/wii/cover/%s/%s.png";
const std::string EMPTY_STRING; 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 } // Anonymous namespace
DiscIO::Language GameFile::GetConfigLanguage() const 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); return SConfig::GetInstance().GetLanguageAdjustedForRegion(DiscIO::IsWii(m_platform), m_region);
#endif
} }
bool operator==(const GameBanner& lhs, const GameBanner& rhs) bool operator==(const GameBanner& lhs, const GameBanner& rhs)
@ -174,7 +157,7 @@ bool GameFile::IsValid() const
bool GameFile::CustomCoverChanged() 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; return false;
std::string path, name; std::string path, name;
@ -201,7 +184,7 @@ bool GameFile::CustomCoverChanged()
void GameFile::DownloadDefaultCover() void GameFile::DownloadDefaultCover()
{ {
if (!m_default_cover.buffer.empty() || !UseGameCovers()) if (!m_default_cover.buffer.empty() || !Config::Get(Config::MAIN_USE_GAME_COVERS))
return; return;
const auto cover_path = File::GetUserPath(D_COVERCACHE_IDX) + DIR_SEP; const auto cover_path = File::GetUserPath(D_COVERCACHE_IDX) + DIR_SEP;
@ -267,7 +250,7 @@ void GameFile::DownloadDefaultCover()
bool GameFile::DefaultCoverChanged() 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; return false;
const auto cover_path = File::GetUserPath(D_COVERCACHE_IDX) + DIR_SEP; const auto cover_path = File::GetUserPath(D_COVERCACHE_IDX) + DIR_SEP;