@@ -109,7 +109,7 @@ private static void startService(Context context, String action)
*/
public static void startLoad(Context context)
{
new AfterDirectoryInitializationRunner().run(context,
new AfterDirectoryInitializationRunner().run(context, false,
() -> startService(context, ACTION_LOAD));
}

@@ -120,7 +120,7 @@ public static void startLoad(Context context)
*/
public static void startRescan(Context context)
{
new AfterDirectoryInitializationRunner().run(context,
new AfterDirectoryInitializationRunner().run(context, false,
() -> startService(context, ACTION_RESCAN));
}

@@ -75,7 +75,7 @@ protected void onCreate(Bundle savedInstanceState)
if (PermissionsHandler.hasWriteAccess(this))
{
new AfterDirectoryInitializationRunner()
.run(this, this::setPlatformTabsAndStartGameFileCacheService);
.run(this, false, this::setPlatformTabsAndStartGameFileCacheService);
}
}

@@ -216,7 +216,7 @@ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permis
{
DirectoryInitialization.start(this);
new AfterDirectoryInitializationRunner()
.run(this, this::setPlatformTabsAndStartGameFileCacheService);
.run(this, false, this::setPlatformTabsAndStartGameFileCacheService);
}
else
{
@@ -2,12 +2,38 @@

import android.content.Context;
import android.content.IntentFilter;
import android.widget.Toast;

import androidx.localbroadcastmanager.content.LocalBroadcastManager;

import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.utils.DirectoryInitialization.DirectoryInitializationState;

public class AfterDirectoryInitializationRunner
{
private DirectoryStateReceiver directoryStateReceiver;
private DirectoryStateReceiver mDirectoryStateReceiver;
private LocalBroadcastManager mLocalBroadcastManager;

private Runnable mUnregisterCallback;

/**
* Sets a Runnable which will be called when:
*
* 1. The Runnable supplied to {@link #run} is just about to run, or
* 2. {@link #run} was called with abortOnFailure == true and there is a failure
*/
public void setFinishedCallback(Runnable runnable)
{
mUnregisterCallback = runnable;
}

private void runFinishedCallback()
{
if (mUnregisterCallback != null)
{
mUnregisterCallback.run();
}
}

/**
* Executes a Runnable after directory initialization has finished.
@@ -19,33 +45,84 @@
* in case directory initialization doesn't finish successfully.
*
* Calling this function multiple times per object is not supported.
*
* If abortOnFailure is true and the user has not granted the required
* permission or the external storage was not found, a message will be
* shown to the user and the Runnable will not run. If it is false, the
* attempt to run the Runnable will never be aborted, and the Runnable
* is guaranteed to run if directory initialization ever finishes.
*/
public void run(Context context, Runnable runnable)
public void run(Context context, boolean abortOnFailure, Runnable runnable)
{
if (DirectoryInitialization.areDolphinDirectoriesReady())
{
runFinishedCallback();
runnable.run();
}
else if (abortOnFailure &&
showErrorMessage(context, DirectoryInitialization.getDolphinDirectoriesState(context)))
{
runFinishedCallback();
}
else
{
runAfterInitialization(context, abortOnFailure, runnable);
}
}

private void runAfterInitialization(Context context, boolean abortOnFailure, Runnable runnable)
{
if (!DirectoryInitialization.areDolphinDirectoriesReady())
mDirectoryStateReceiver = new DirectoryStateReceiver(state ->
{
// Wait for directories to get initialized
IntentFilter statusIntentFilter = new IntentFilter(
DirectoryInitialization.BROADCAST_ACTION);
boolean done = state == DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED;

if (!done && abortOnFailure)
{
done = showErrorMessage(context, state);
}

directoryStateReceiver = new DirectoryStateReceiver(directoryInitializationState ->
if (done)
{
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);
cancel();
runFinishedCallback();
}

if (state == DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED)
{
runnable.run();
}
});

mLocalBroadcastManager = LocalBroadcastManager.getInstance(context);

IntentFilter statusIntentFilter = new IntentFilter(DirectoryInitialization.BROADCAST_ACTION);
mLocalBroadcastManager.registerReceiver(mDirectoryStateReceiver, statusIntentFilter);
}

public void cancel()
{
if (mDirectoryStateReceiver != null)
{
mLocalBroadcastManager.unregisterReceiver(mDirectoryStateReceiver);
mDirectoryStateReceiver = null;
mLocalBroadcastManager = null;
}
else
}

private static boolean showErrorMessage(Context context, DirectoryInitializationState state)
{
switch (state)
{
runnable.run();
case EXTERNAL_STORAGE_PERMISSION_NEEDED:
Toast.makeText(context, R.string.write_permission_needed, Toast.LENGTH_SHORT).show();
return true;

case CANT_FIND_EXTERNAL_STORAGE:
Toast.makeText(context, R.string.external_storage_not_mounted, Toast.LENGTH_SHORT).show();
return true;

default:
return false;
}
}
}
@@ -32,7 +32,7 @@ public static void checkAnalyticsInit(Context context)
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
if (!preferences.getBoolean(analyticsAsked, false))
{
new AfterDirectoryInitializationRunner().run(context,
new AfterDirectoryInitializationRunner().run(context, false,
() -> showMessage(context, preferences));
}
}
@@ -37,13 +37,15 @@

public static final String EXTRA_STATE = "directoryState";
private static final int WiimoteNewVersion = 5; // Last changed in PR 8907
private static volatile DirectoryInitializationState directoryState = null;
private static volatile DirectoryInitializationState directoryState =
DirectoryInitializationState.NOT_YET_INITIALIZED;
private static String userPath;
private static String internalPath;
private static AtomicBoolean isDolphinDirectoryInitializationRunning = new AtomicBoolean(false);

public enum DirectoryInitializationState
{
NOT_YET_INITIALIZED,
DOLPHIN_DIRECTORIES_INITIALIZED,
EXTERNAL_STORAGE_PERMISSION_NEEDED,
CANT_FIND_EXTERNAL_STORAGE
@@ -199,9 +201,22 @@ public static boolean areDolphinDirectoriesReady()
return directoryState == DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED;
}

public static DirectoryInitializationState getDolphinDirectoriesState(Context context)
{
if (directoryState == DirectoryInitializationState.NOT_YET_INITIALIZED &&
!PermissionsHandler.hasWriteAccess(context))
{
return DirectoryInitializationState.EXTERNAL_STORAGE_PERMISSION_NEEDED;
}
else
{
return directoryState;
}
}

public static String getUserDirectory()
{
if (directoryState == null)
if (directoryState == DirectoryInitializationState.NOT_YET_INITIALIZED)
{
throw new IllegalStateException("DirectoryInitialization has to run at least once!");
}
@@ -216,7 +231,7 @@ else if (isDolphinDirectoryInitializationRunning.get())

public static String getDolphinInternalDirectory()
{
if (directoryState == null)
if (directoryState == DirectoryInitializationState.NOT_YET_INITIALIZED)
{
throw new IllegalStateException("DirectoryInitialization has to run at least once!");
}
@@ -76,7 +76,7 @@ public static void checkSessionReset(Context context)
long lastOpen = preferences.getLong(LAST_CLOSED, 0);
if (currentTime > (lastOpen + SESSION_TIMEOUT))
{
new AfterDirectoryInitializationRunner().run(context,
new AfterDirectoryInitializationRunner().run(context, false,
NativeLibrary::ReportStartToAnalytics);
}
}