Merge pull request #10980 from JosJuice/android-no-dir-init-fail
Android: Force quit app if external storage isn't mounted
This commit is contained in:
commit
b6a18b0da5
|
@ -63,7 +63,7 @@ public class AppLinkActivity extends FragmentActivity
|
||||||
private void initResources()
|
private void initResources()
|
||||||
{
|
{
|
||||||
mAfterDirectoryInitializationRunner = new AfterDirectoryInitializationRunner();
|
mAfterDirectoryInitializationRunner = new AfterDirectoryInitializationRunner();
|
||||||
mAfterDirectoryInitializationRunner.runWithLifecycle(this, true, () -> tryPlay(playAction));
|
mAfterDirectoryInitializationRunner.runWithLifecycle(this, () -> tryPlay(playAction));
|
||||||
|
|
||||||
GameFileCacheManager.isLoading().observe(this, (isLoading) ->
|
GameFileCacheManager.isLoading().observe(this, (isLoading) ->
|
||||||
{
|
{
|
||||||
|
|
|
@ -185,7 +185,7 @@ public final class EmulationActivity extends AppCompatActivity
|
||||||
private static void performLaunchChecks(FragmentActivity activity,
|
private static void performLaunchChecks(FragmentActivity activity,
|
||||||
Runnable continueCallback)
|
Runnable continueCallback)
|
||||||
{
|
{
|
||||||
new AfterDirectoryInitializationRunner().runWithLifecycle(activity, true, () ->
|
new AfterDirectoryInitializationRunner().runWithLifecycle(activity, () ->
|
||||||
{
|
{
|
||||||
if (!FileBrowserHelper.isPathEmptyOrValid(StringSetting.MAIN_DEFAULT_ISO) ||
|
if (!FileBrowserHelper.isPathEmptyOrValid(StringSetting.MAIN_DEFAULT_ISO) ||
|
||||||
!FileBrowserHelper.isPathEmptyOrValid(StringSetting.MAIN_FS_PATH) ||
|
!FileBrowserHelper.isPathEmptyOrValid(StringSetting.MAIN_FS_PATH) ||
|
||||||
|
|
|
@ -63,6 +63,8 @@ public final class SettingsActivityPresenter
|
||||||
|
|
||||||
private void loadSettingsUI()
|
private void loadSettingsUI()
|
||||||
{
|
{
|
||||||
|
mView.hideLoading();
|
||||||
|
|
||||||
if (mSettings.isEmpty())
|
if (mSettings.isEmpty())
|
||||||
{
|
{
|
||||||
if (!TextUtils.isEmpty(mGameId))
|
if (!TextUtils.isEmpty(mGameId))
|
||||||
|
@ -85,19 +87,10 @@ public final class SettingsActivityPresenter
|
||||||
}
|
}
|
||||||
|
|
||||||
private void prepareDolphinDirectoriesIfNeeded()
|
private void prepareDolphinDirectoriesIfNeeded()
|
||||||
{
|
|
||||||
if (DirectoryInitialization.areDolphinDirectoriesReady())
|
|
||||||
{
|
|
||||||
loadSettingsUI();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
mView.showLoading();
|
mView.showLoading();
|
||||||
|
|
||||||
new AfterDirectoryInitializationRunner()
|
new AfterDirectoryInitializationRunner().runWithLifecycle(mActivity, this::loadSettingsUI);
|
||||||
.setFinishedCallback(mView::hideLoading)
|
|
||||||
.runWithLifecycle(mActivity, true, this::loadSettingsUI);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Settings getSettings()
|
public Settings getSettings()
|
||||||
|
|
|
@ -100,7 +100,7 @@ public interface SettingsActivityView
|
||||||
void showLoading();
|
void showLoading();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hide the loading the dialog
|
* Hide the loading dialog
|
||||||
*/
|
*/
|
||||||
void hideLoading();
|
void hideLoading();
|
||||||
|
|
||||||
|
|
|
@ -128,7 +128,7 @@ public final class GameFileCacheManager
|
||||||
if (!loadInProgress.getValue())
|
if (!loadInProgress.getValue())
|
||||||
{
|
{
|
||||||
loadInProgress.setValue(true);
|
loadInProgress.setValue(true);
|
||||||
new AfterDirectoryInitializationRunner().runWithoutLifecycle(context, false,
|
new AfterDirectoryInitializationRunner().runWithoutLifecycle(
|
||||||
() -> executor.execute(GameFileCacheManager::load));
|
() -> executor.execute(GameFileCacheManager::load));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -144,7 +144,7 @@ public final class GameFileCacheManager
|
||||||
if (!rescanInProgress.getValue())
|
if (!rescanInProgress.getValue())
|
||||||
{
|
{
|
||||||
rescanInProgress.setValue(true);
|
rescanInProgress.setValue(true);
|
||||||
new AfterDirectoryInitializationRunner().runWithoutLifecycle(context, false,
|
new AfterDirectoryInitializationRunner().runWithoutLifecycle(
|
||||||
() -> executor.execute(GameFileCacheManager::rescan));
|
() -> executor.execute(GameFileCacheManager::rescan));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,7 +79,7 @@ public final class MainActivity extends AppCompatActivity
|
||||||
if (!DirectoryInitialization.isWaitingForWriteAccess(this))
|
if (!DirectoryInitialization.isWaitingForWriteAccess(this))
|
||||||
{
|
{
|
||||||
new AfterDirectoryInitializationRunner()
|
new AfterDirectoryInitializationRunner()
|
||||||
.runWithLifecycle(this, false, this::setPlatformTabsAndStartGameFileCacheService);
|
.runWithLifecycle(this, this::setPlatformTabsAndStartGameFileCacheService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@ public final class MainActivity extends AppCompatActivity
|
||||||
{
|
{
|
||||||
DirectoryInitialization.start(this);
|
DirectoryInitialization.start(this);
|
||||||
new AfterDirectoryInitializationRunner()
|
new AfterDirectoryInitializationRunner()
|
||||||
.runWithLifecycle(this, false, this::setPlatformTabsAndStartGameFileCacheService);
|
.runWithLifecycle(this, this::setPlatformTabsAndStartGameFileCacheService);
|
||||||
}
|
}
|
||||||
|
|
||||||
mPresenter.onResume();
|
mPresenter.onResume();
|
||||||
|
@ -268,7 +268,7 @@ public final class MainActivity extends AppCompatActivity
|
||||||
|
|
||||||
DirectoryInitialization.start(this);
|
DirectoryInitialization.start(this);
|
||||||
new AfterDirectoryInitializationRunner()
|
new AfterDirectoryInitializationRunner()
|
||||||
.runWithLifecycle(this, false, this::setPlatformTabsAndStartGameFileCacheService);
|
.runWithLifecycle(this, this::setPlatformTabsAndStartGameFileCacheService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -81,7 +81,7 @@ public final class MainPresenter
|
||||||
|
|
||||||
public void onFabClick()
|
public void onFabClick()
|
||||||
{
|
{
|
||||||
new AfterDirectoryInitializationRunner().runWithLifecycle(mActivity, true,
|
new AfterDirectoryInitializationRunner().runWithLifecycle(mActivity,
|
||||||
mView::launchFileListActivity);
|
mView::launchFileListActivity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ public final class MainPresenter
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case R.id.button_add_directory:
|
case R.id.button_add_directory:
|
||||||
new AfterDirectoryInitializationRunner().runWithLifecycle(activity, true,
|
new AfterDirectoryInitializationRunner().runWithLifecycle(activity,
|
||||||
mView::launchFileListActivity);
|
mView::launchFileListActivity);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
@ -112,22 +112,22 @@ public final class MainPresenter
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case R.id.menu_online_system_update:
|
case R.id.menu_online_system_update:
|
||||||
new AfterDirectoryInitializationRunner().runWithLifecycle(activity, true,
|
new AfterDirectoryInitializationRunner().runWithLifecycle(activity,
|
||||||
this::launchOnlineUpdate);
|
this::launchOnlineUpdate);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case R.id.menu_install_wad:
|
case R.id.menu_install_wad:
|
||||||
new AfterDirectoryInitializationRunner().runWithLifecycle(activity, true,
|
new AfterDirectoryInitializationRunner().runWithLifecycle(activity,
|
||||||
() -> mView.launchOpenFileActivity(REQUEST_WAD_FILE));
|
() -> mView.launchOpenFileActivity(REQUEST_WAD_FILE));
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case R.id.menu_import_wii_save:
|
case R.id.menu_import_wii_save:
|
||||||
new AfterDirectoryInitializationRunner().runWithLifecycle(activity, true,
|
new AfterDirectoryInitializationRunner().runWithLifecycle(activity,
|
||||||
() -> mView.launchOpenFileActivity(REQUEST_WII_SAVE_FILE));
|
() -> mView.launchOpenFileActivity(REQUEST_WII_SAVE_FILE));
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case R.id.menu_import_nand_backup:
|
case R.id.menu_import_nand_backup:
|
||||||
new AfterDirectoryInitializationRunner().runWithLifecycle(activity, true,
|
new AfterDirectoryInitializationRunner().runWithLifecycle(activity,
|
||||||
() -> mView.launchOpenFileActivity(REQUEST_NAND_BIN_FILE));
|
() -> mView.launchOpenFileActivity(REQUEST_NAND_BIN_FILE));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -325,7 +325,7 @@ public final class MainPresenter
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
new AfterDirectoryInitializationRunner().runWithLifecycle(mActivity, true, () ->
|
new AfterDirectoryInitializationRunner().runWithLifecycle(mActivity, () ->
|
||||||
{
|
{
|
||||||
SystemMenuNotInstalledDialogFragment dialogFragment =
|
SystemMenuNotInstalledDialogFragment dialogFragment =
|
||||||
new SystemMenuNotInstalledDialogFragment();
|
new SystemMenuNotInstalledDialogFragment();
|
||||||
|
|
|
@ -2,43 +2,14 @@
|
||||||
|
|
||||||
package org.dolphinemu.dolphinemu.utils;
|
package org.dolphinemu.dolphinemu.utils;
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import androidx.core.app.ComponentActivity;
|
import androidx.core.app.ComponentActivity;
|
||||||
import androidx.lifecycle.Observer;
|
import androidx.lifecycle.Observer;
|
||||||
|
|
||||||
import org.dolphinemu.dolphinemu.R;
|
|
||||||
import org.dolphinemu.dolphinemu.utils.DirectoryInitialization.DirectoryInitializationState;
|
import org.dolphinemu.dolphinemu.utils.DirectoryInitialization.DirectoryInitializationState;
|
||||||
|
|
||||||
public class AfterDirectoryInitializationRunner
|
public class AfterDirectoryInitializationRunner
|
||||||
{
|
{
|
||||||
private Observer<DirectoryInitializationState> mObserver;
|
private Observer<DirectoryInitializationState> mObserver;
|
||||||
private Runnable mUnregisterCallback;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets a Runnable which will be called when:
|
|
||||||
*
|
|
||||||
* 1. The Runnable supplied to {@link #runWithLifecycle}/{@link #runWithoutLifecycle}
|
|
||||||
* is just about to run, or
|
|
||||||
* 2. {@link #runWithLifecycle}/{@link #runWithoutLifecycle} was called with
|
|
||||||
* abortOnFailure == true and there is a failure
|
|
||||||
*
|
|
||||||
* @return this
|
|
||||||
*/
|
|
||||||
public AfterDirectoryInitializationRunner setFinishedCallback(Runnable runnable)
|
|
||||||
{
|
|
||||||
mUnregisterCallback = runnable;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void runFinishedCallback()
|
|
||||||
{
|
|
||||||
if (mUnregisterCallback != null)
|
|
||||||
{
|
|
||||||
mUnregisterCallback.run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes a Runnable after directory initialization has finished.
|
* Executes a Runnable after directory initialization has finished.
|
||||||
|
@ -59,23 +30,15 @@ public class AfterDirectoryInitializationRunner
|
||||||
* If the passed-in activity gets destroyed before this operation finishes,
|
* If the passed-in activity gets destroyed before this operation finishes,
|
||||||
* it will be automatically canceled.
|
* it will be automatically canceled.
|
||||||
*/
|
*/
|
||||||
public void runWithLifecycle(ComponentActivity activity, boolean abortOnFailure,
|
public void runWithLifecycle(ComponentActivity activity, Runnable runnable)
|
||||||
Runnable runnable)
|
|
||||||
{
|
{
|
||||||
if (DirectoryInitialization.areDolphinDirectoriesReady())
|
if (DirectoryInitialization.areDolphinDirectoriesReady())
|
||||||
{
|
{
|
||||||
runFinishedCallback();
|
|
||||||
runnable.run();
|
runnable.run();
|
||||||
}
|
}
|
||||||
else if (abortOnFailure &&
|
|
||||||
showErrorMessage(activity,
|
|
||||||
DirectoryInitialization.getDolphinDirectoriesState().getValue()))
|
|
||||||
{
|
|
||||||
runFinishedCallback();
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mObserver = createObserver(activity, abortOnFailure, runnable);
|
mObserver = createObserver(runnable);
|
||||||
DirectoryInitialization.getDolphinDirectoriesState().observe(activity, mObserver);
|
DirectoryInitialization.getDolphinDirectoriesState().observe(activity, mObserver);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,46 +59,26 @@ public class AfterDirectoryInitializationRunner
|
||||||
* the attempt to run the Runnable will never be aborted, and the Runnable
|
* the attempt to run the Runnable will never be aborted, and the Runnable
|
||||||
* is guaranteed to run if directory initialization ever finishes.
|
* is guaranteed to run if directory initialization ever finishes.
|
||||||
*/
|
*/
|
||||||
public void runWithoutLifecycle(Context context, boolean abortOnFailure, Runnable runnable)
|
public void runWithoutLifecycle(Runnable runnable)
|
||||||
{
|
{
|
||||||
if (DirectoryInitialization.areDolphinDirectoriesReady())
|
if (DirectoryInitialization.areDolphinDirectoriesReady())
|
||||||
{
|
{
|
||||||
runFinishedCallback();
|
|
||||||
runnable.run();
|
runnable.run();
|
||||||
}
|
}
|
||||||
else if (abortOnFailure &&
|
|
||||||
showErrorMessage(context,
|
|
||||||
DirectoryInitialization.getDolphinDirectoriesState().getValue()))
|
|
||||||
{
|
|
||||||
runFinishedCallback();
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mObserver = createObserver(context, abortOnFailure, runnable);
|
mObserver = createObserver(runnable);
|
||||||
DirectoryInitialization.getDolphinDirectoriesState().observeForever(mObserver);
|
DirectoryInitialization.getDolphinDirectoriesState().observeForever(mObserver);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Observer<DirectoryInitializationState> createObserver(Context context,
|
private Observer<DirectoryInitializationState> createObserver(Runnable runnable)
|
||||||
boolean abortOnFailure, Runnable runnable)
|
|
||||||
{
|
{
|
||||||
return (state) ->
|
return (state) ->
|
||||||
{
|
{
|
||||||
boolean done = state == DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED;
|
|
||||||
|
|
||||||
if (!done && abortOnFailure)
|
|
||||||
{
|
|
||||||
done = showErrorMessage(context, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (done)
|
|
||||||
{
|
|
||||||
cancel();
|
|
||||||
runFinishedCallback();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state == DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED)
|
if (state == DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED)
|
||||||
{
|
{
|
||||||
|
cancel();
|
||||||
runnable.run();
|
runnable.run();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -145,17 +88,4 @@ public class AfterDirectoryInitializationRunner
|
||||||
{
|
{
|
||||||
DirectoryInitialization.getDolphinDirectoriesState().removeObserver(mObserver);
|
DirectoryInitialization.getDolphinDirectoriesState().removeObserver(mObserver);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean showErrorMessage(Context context, DirectoryInitializationState state)
|
|
||||||
{
|
|
||||||
switch (state)
|
|
||||||
{
|
|
||||||
case CANT_FIND_EXTERNAL_STORAGE:
|
|
||||||
Toast.makeText(context, R.string.external_storage_not_mounted, Toast.LENGTH_LONG).show();
|
|
||||||
return true;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ public class Analytics
|
||||||
|
|
||||||
public static void checkAnalyticsInit(Context context)
|
public static void checkAnalyticsInit(Context context)
|
||||||
{
|
{
|
||||||
new AfterDirectoryInitializationRunner().runWithoutLifecycle(context, false, () ->
|
new AfterDirectoryInitializationRunner().runWithoutLifecycle(() ->
|
||||||
{
|
{
|
||||||
if (!BooleanSetting.MAIN_ANALYTICS_PERMISSION_ASKED.getBooleanGlobal())
|
if (!BooleanSetting.MAIN_ANALYTICS_PERMISSION_ASKED.getBooleanGlobal())
|
||||||
{
|
{
|
||||||
|
|
|
@ -10,6 +10,7 @@ import android.content.SharedPreferences;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
@ -17,6 +18,7 @@ import androidx.lifecycle.LiveData;
|
||||||
import androidx.lifecycle.MutableLiveData;
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
|
||||||
import org.dolphinemu.dolphinemu.NativeLibrary;
|
import org.dolphinemu.dolphinemu.NativeLibrary;
|
||||||
|
import org.dolphinemu.dolphinemu.R;
|
||||||
import org.dolphinemu.dolphinemu.activities.EmulationActivity;
|
import org.dolphinemu.dolphinemu.activities.EmulationActivity;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@ -43,13 +45,12 @@ public final class DirectoryInitialization
|
||||||
{
|
{
|
||||||
NOT_YET_INITIALIZED,
|
NOT_YET_INITIALIZED,
|
||||||
INITIALIZING,
|
INITIALIZING,
|
||||||
DOLPHIN_DIRECTORIES_INITIALIZED,
|
DOLPHIN_DIRECTORIES_INITIALIZED
|
||||||
CANT_FIND_EXTERNAL_STORAGE
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void start(Context context)
|
public static void start(Context context)
|
||||||
{
|
{
|
||||||
if (directoryState.getValue() == DirectoryInitializationState.INITIALIZING)
|
if (directoryState.getValue() != DirectoryInitializationState.NOT_YET_INITIALIZED)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
directoryState.setValue(DirectoryInitializationState.INITIALIZING);
|
directoryState.setValue(DirectoryInitializationState.INITIALIZING);
|
||||||
|
@ -60,10 +61,15 @@ public final class DirectoryInitialization
|
||||||
|
|
||||||
private static void init(Context context)
|
private static void init(Context context)
|
||||||
{
|
{
|
||||||
if (directoryState.getValue() != DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED)
|
if (directoryState.getValue() == DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED)
|
||||||
{
|
return;
|
||||||
if (setDolphinUserDirectory(context))
|
|
||||||
|
if (!setDolphinUserDirectory(context))
|
||||||
{
|
{
|
||||||
|
Toast.makeText(context, R.string.external_storage_not_mounted, Toast.LENGTH_LONG).show();
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
initializeInternalStorage(context);
|
initializeInternalStorage(context);
|
||||||
boolean wiimoteIniWritten = initializeExternalStorage(context);
|
boolean wiimoteIniWritten = initializeExternalStorage(context);
|
||||||
NativeLibrary.Initialize();
|
NativeLibrary.Initialize();
|
||||||
|
@ -80,12 +86,6 @@ public final class DirectoryInitialization
|
||||||
|
|
||||||
directoryState.postValue(DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED);
|
directoryState.postValue(DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
directoryState.postValue(DirectoryInitializationState.CANT_FIND_EXTERNAL_STORAGE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private static File getLegacyUserDirectoryPath()
|
private static File getLegacyUserDirectoryPath()
|
||||||
|
|
|
@ -112,7 +112,7 @@ public final class StartupHandler
|
||||||
final Instant lastOpened = Instant.ofEpochMilli(lastOpen);
|
final Instant lastOpened = Instant.ofEpochMilli(lastOpen);
|
||||||
if (current.isAfter(lastOpened.plus(6, ChronoUnit.HOURS)))
|
if (current.isAfter(lastOpened.plus(6, ChronoUnit.HOURS)))
|
||||||
{
|
{
|
||||||
new AfterDirectoryInitializationRunner().runWithoutLifecycle(context, false,
|
new AfterDirectoryInitializationRunner().runWithoutLifecycle(
|
||||||
NativeLibrary::ReportStartToAnalytics);
|
NativeLibrary::ReportStartToAnalytics);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue