From 1190afef51fc91042efce96e7c4a0cafe3d78ae5 Mon Sep 17 00:00:00 2001 From: mahdihijazi Date: Sun, 17 Dec 2017 01:09:55 +0100 Subject: [PATCH] [Android] Refactor AssetCopyService and the way we extract resources from the assets This solves the following issues: 1. If user uninstall Dolphin and install it again the resources will not be copied unless the user manually clear the app cache because we are enabling the allowBackup flag in the manifest which will make the app restore the settings saved in the shared prefernces including the flag assetsCopied. This PR always copy the files everytime you open Dolphin. 2. If the AssetCopyService took long time and you tried to open the settings screen or start a game the behaviour was not expected or the emulator will crash, this PR make sure that whatever we add to the DirectoryInitializationService or how long it will take will still work as expected by blocking both the settings screen and the emulaion screen to wait untill all resources needed are copied. 3. Better communication between the DirectoryInitializationService and the UI screens using brocast messages. --- .../Android/app/src/main/AndroidManifest.xml | 2 +- .../fragments/EmulationFragment.java | 46 +++++- .../dolphinemu/services/AssetCopyService.java | 112 ------------- .../DirectoryInitializationService.java | 154 ++++++++++++++++++ .../dolphinemu/ui/main/MainActivity.java | 3 +- .../dolphinemu/ui/main/TvMainActivity.java | 3 +- .../ui/settings/SettingsActivity.java | 55 +++++++ .../settings/SettingsActivityPresenter.java | 54 +++++- .../ui/settings/SettingsActivityView.java | 33 ++++ .../utils/DirectoryStateReceiver.java | 24 +++ .../dolphinemu/utils/PermissionsHandler.java | 5 +- .../dolphinemu/utils/StartupHandler.java | 19 +-- .../app/src/main/res/values/strings.xml | 2 + 13 files changed, 374 insertions(+), 138 deletions(-) delete mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/services/AssetCopyService.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/services/DirectoryInitializationService.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/DirectoryStateReceiver.java diff --git a/Source/Android/app/src/main/AndroidManifest.xml b/Source/Android/app/src/main/AndroidManifest.xml index 5eae9e4445..d445f5d822 100644 --- a/Source/Android/app/src/main/AndroidManifest.xml +++ b/Source/Android/app/src/main/AndroidManifest.xml @@ -65,7 +65,7 @@ android:theme="@style/DolphinEmulationGamecube"/> - + { + if (directoryInitializationState == DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED) { + mEmulationState.run(); + } else if (directoryInitializationState == DirectoryInitializationState.EXTERNAL_STORAGE_PERMISSION_NEEDED) { + Toast.makeText(getContext(), R.string.write_permission_needed, Toast.LENGTH_SHORT) + .show(); + } + }); + + // Registers the DirectoryStateReceiver and its intent filters + LocalBroadcastManager.getInstance(getActivity()).registerReceiver( + directoryStateReceiver, + statusIntentFilter); + DirectoryInitializationService.startService(getActivity()); + } + public void toggleInputOverlayVisibility() { SharedPreferences.Editor editor = mPreferences.edit(); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/services/AssetCopyService.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/services/AssetCopyService.java deleted file mode 100644 index d8c39459f6..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/services/AssetCopyService.java +++ /dev/null @@ -1,112 +0,0 @@ -/** - * Copyright 2014 Dolphin Emulator Project - * Licensed under GPLv2+ - * Refer to the license.txt file included. - */ - -package org.dolphinemu.dolphinemu.services; - -import android.app.IntentService; -import android.content.Intent; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; - -import org.dolphinemu.dolphinemu.NativeLibrary; -import org.dolphinemu.dolphinemu.utils.Log; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -/** - * A service that spawns its own thread in order to copy several binary and shader files - * from the Dolphin APK to the external file system. - */ -public final class AssetCopyService extends IntentService -{ - public AssetCopyService() - { - // Superclass constructor is called to name the thread on which this service executes. - super("AssetCopyService"); - } - - @Override - protected void onHandleIntent(Intent intent) - { - String BaseDir = NativeLibrary.GetUserDirectory(); - String ConfigDir = BaseDir + File.separator + "Config"; - - // Copy assets if needed - NativeLibrary.CreateUserFolders(); - copyAssetFolder("GC", BaseDir + File.separator + "GC", false); - copyAssetFolder("Shaders", BaseDir + File.separator + "Shaders", false); - copyAssetFolder("Wii", BaseDir + File.separator + "Wii", false); - - // Always copy over the GCPad config in case of change or corruption. - // Not a user configurable file. - copyAsset("GCPadNew.ini", ConfigDir + File.separator + "GCPadNew.ini", true); - copyAsset("WiimoteNew.ini", ConfigDir + File.separator + "WiimoteNew.ini", true); - - // Record the fact that we've done this before, so we don't do it on every launch. - SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); - SharedPreferences.Editor editor = preferences.edit(); - - editor.putBoolean("assetsCopied", true); - editor.apply(); - } - - private void copyAsset(String asset, String output, Boolean overwrite) - { - Log.verbose("[AssetCopyService] Copying File " + asset + " to " + output); - InputStream in; - OutputStream out; - - try - { - File file = new File(output); - if(!file.exists() || overwrite) - { - in = getAssets().open(asset); - out = new FileOutputStream(output); - copyFile(in, out); - in.close(); - out.close(); - } - } - catch (IOException e) - { - Log.error("[AssetCopyService] Failed to copy asset file: " + asset + e.getMessage()); - } - } - - private void copyAssetFolder(String assetFolder, String outputFolder, Boolean overwrite) - { - Log.verbose("[AssetCopyService] Copying Folder " + assetFolder + " to " + outputFolder); - - try - { - for (String file : getAssets().list(assetFolder)) - { - copyAssetFolder(assetFolder + File.separator + file, outputFolder + File.separator + file, overwrite); - copyAsset(assetFolder + File.separator + file, outputFolder + File.separator + file, overwrite); - } - } - catch (IOException e) - { - Log.error("[AssetCopyService] Failed to copy asset folder: " + assetFolder + e.getMessage()); - } - } - - private void copyFile(InputStream in, OutputStream out) throws IOException - { - byte[] buffer = new byte[1024]; - int read; - - while ((read = in.read(buffer)) != -1) - { - out.write(buffer, 0, read); - } - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/services/DirectoryInitializationService.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/services/DirectoryInitializationService.java new file mode 100644 index 0000000000..acd1f9f821 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/services/DirectoryInitializationService.java @@ -0,0 +1,154 @@ +/** + * Copyright 2014 Dolphin Emulator Project + * Licensed under GPLv2+ + * Refer to the license.txt file included. + */ + +package org.dolphinemu.dolphinemu.services; + +import android.app.IntentService; +import android.content.Context; +import android.content.Intent; +import android.support.v4.content.LocalBroadcastManager; + +import org.dolphinemu.dolphinemu.NativeLibrary; +import org.dolphinemu.dolphinemu.utils.Log; +import org.dolphinemu.dolphinemu.utils.PermissionsHandler; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * A service that spawns its own thread in order to copy several binary and shader files + * from the Dolphin APK to the external file system. + */ +public final class DirectoryInitializationService extends IntentService +{ + public static final String BROADCAST_ACTION = "org.dolphinemu.dolphinemu.BROADCAST"; + + public static final String EXTRA_STATE = "directoryState"; + private static DirectoryInitializationState directoryState = null; + + public enum DirectoryInitializationState + { + DOLPHIN_DIRECTORIES_INITIALIZED, + EXTERNAL_STORAGE_PERMISSION_NEEDED + } + + public DirectoryInitializationService() + { + // Superclass constructor is called to name the thread on which this service executes. + super("DirectoryInitializationService"); + } + + public static void startService(Context context) + { + Intent intent = new Intent(context, DirectoryInitializationService.class); + context.startService(intent); + } + + @Override + protected void onHandleIntent(Intent intent) + { + if (directoryState == DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED) + { + sendBroadcastState(DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED); + } + else if (PermissionsHandler.hasWriteAccess(this)) + { + initDolphinDirectories(); + directoryState = DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED; + sendBroadcastState(directoryState); + } + else + { + sendBroadcastState(DirectoryInitializationState.EXTERNAL_STORAGE_PERMISSION_NEEDED); + } + } + + private void initDolphinDirectories() + { + String BaseDir = NativeLibrary.GetUserDirectory(); + String ConfigDir = BaseDir + File.separator + "Config"; + + // Copy assets if needed + NativeLibrary.CreateUserFolders(); + copyAssetFolder("GC", BaseDir + File.separator + "GC", false); + copyAssetFolder("Shaders", BaseDir + File.separator + "Shaders", false); + copyAssetFolder("Wii", BaseDir + File.separator + "Wii", false); + + // Always copy over the GCPad config in case of change or corruption. + // Not a user configurable file. + copyAsset("GCPadNew.ini", ConfigDir + File.separator + "GCPadNew.ini", true); + copyAsset("WiimoteNew.ini", ConfigDir + File.separator + "WiimoteNew.ini", true); + } + + public static boolean areDolphinDirectoriesReady() + { + return directoryState == DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED; + } + + private void sendBroadcastState(DirectoryInitializationState state) + { + Intent localIntent = + new Intent(BROADCAST_ACTION) + .putExtra(EXTRA_STATE, state); + LocalBroadcastManager.getInstance(this).sendBroadcast(localIntent); + } + + private void copyAsset(String asset, String output, Boolean overwrite) + { + Log.verbose("[DirectoryInitializationService] Copying File " + asset + " to " + output); + InputStream in; + OutputStream out; + + try + { + File file = new File(output); + if (!file.exists() || overwrite) + { + in = getAssets().open(asset); + out = new FileOutputStream(output); + copyFile(in, out); + in.close(); + out.close(); + } + } + catch (IOException e) + { + Log.error("[DirectoryInitializationService] Failed to copy asset file: " + asset + e.getMessage()); + } + } + + private void copyAssetFolder(String assetFolder, String outputFolder, Boolean overwrite) + { + Log.verbose("[DirectoryInitializationService] Copying Folder " + assetFolder + " to " + outputFolder); + + try + { + for (String file : getAssets().list(assetFolder)) + { + copyAssetFolder(assetFolder + File.separator + file, outputFolder + File.separator + file, overwrite); + copyAsset(assetFolder + File.separator + file, outputFolder + File.separator + file, overwrite); + } + } + catch (IOException e) + { + Log.error("[DirectoryInitializationService] Failed to copy asset folder: " + assetFolder + e.getMessage()); + } + } + + private void copyFile(InputStream in, OutputStream out) throws IOException + { + byte[] buffer = new byte[1024]; + int read; + + while ((read = in.read(buffer)) != -1) + { + out.write(buffer, 0, read); + } + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainActivity.java index 38c478480c..72a9477f00 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainActivity.java @@ -20,6 +20,7 @@ import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.activities.AddDirectoryActivity; import org.dolphinemu.dolphinemu.adapters.PlatformPagerAdapter; import org.dolphinemu.dolphinemu.model.GameProvider; +import org.dolphinemu.dolphinemu.services.DirectoryInitializationService; import org.dolphinemu.dolphinemu.ui.platform.Platform; import org.dolphinemu.dolphinemu.ui.platform.PlatformGamesView; import org.dolphinemu.dolphinemu.ui.settings.SettingsActivity; @@ -154,7 +155,7 @@ public final class MainActivity extends AppCompatActivity implements MainView switch (requestCode) { case PermissionsHandler.REQUEST_CODE_WRITE_PERMISSION: if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { - StartupHandler.copyAssetsIfNeeded(this); + DirectoryInitializationService.startService(this); PlatformPagerAdapter platformPagerAdapter = new PlatformPagerAdapter( getSupportFragmentManager(), this); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/TvMainActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/TvMainActivity.java index 6e4dfa6063..cb72c34a3e 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/TvMainActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/TvMainActivity.java @@ -28,6 +28,7 @@ import org.dolphinemu.dolphinemu.adapters.GameRowPresenter; import org.dolphinemu.dolphinemu.adapters.SettingsRowPresenter; import org.dolphinemu.dolphinemu.model.Game; import org.dolphinemu.dolphinemu.model.TvSettingsItem; +import org.dolphinemu.dolphinemu.services.DirectoryInitializationService; import org.dolphinemu.dolphinemu.ui.platform.Platform; import org.dolphinemu.dolphinemu.ui.settings.SettingsActivity; import org.dolphinemu.dolphinemu.utils.PermissionsHandler; @@ -157,7 +158,7 @@ public final class TvMainActivity extends FragmentActivity implements MainView switch (requestCode) { case PermissionsHandler.REQUEST_CODE_WRITE_PERMISSION: if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { - StartupHandler.copyAssetsIfNeeded(this); + DirectoryInitializationService.startService(this); loadGames(); } else { Toast.makeText(this, R.string.write_permission_needed, Toast.LENGTH_SHORT) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/settings/SettingsActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/settings/SettingsActivity.java index 43715cecba..cd942eb091 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/settings/SettingsActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/settings/SettingsActivity.java @@ -1,9 +1,12 @@ package org.dolphinemu.dolphinemu.ui.settings; +import android.app.ProgressDialog; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.os.Bundle; import android.support.v4.app.FragmentTransaction; +import android.support.v4.content.LocalBroadcastManager; import android.support.v7.app.AppCompatActivity; import android.view.Menu; import android.view.MenuInflater; @@ -12,6 +15,8 @@ import android.widget.Toast; import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.model.settings.SettingSection; +import org.dolphinemu.dolphinemu.services.DirectoryInitializationService; +import org.dolphinemu.dolphinemu.utils.DirectoryStateReceiver; import java.util.ArrayList; import java.util.HashMap; @@ -22,6 +27,8 @@ public final class SettingsActivity extends AppCompatActivity implements Setting private static final String FRAGMENT_TAG = "settings"; private SettingsActivityPresenter mPresenter = new SettingsActivityPresenter(this); + private ProgressDialog dialog; + public static void launch(Context context, String menuTag) { Intent settings = new Intent(context, SettingsActivity.class); @@ -65,6 +72,13 @@ public final class SettingsActivity extends AppCompatActivity implements Setting mPresenter.saveState(outState); } + @Override + protected void onStart() + { + super.onStart(); + mPresenter.onStart(); + } + /** * If this is called, the user has left the settings screen (potentially through the * home button) and will expect their changes to be persisted. So we kick off an @@ -106,6 +120,47 @@ public final class SettingsActivity extends AppCompatActivity implements Setting transaction.commit(); } + @Override + public void startDirectoryInitializationService(DirectoryStateReceiver receiver, IntentFilter filter) + { + LocalBroadcastManager.getInstance(this).registerReceiver( + receiver, + filter); + DirectoryInitializationService.startService(this); + } + + @Override + public void stopListeningToDirectoryInitializationService(DirectoryStateReceiver receiver) + { + LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver); + } + + @Override + public void showLoading() + { + if (dialog == null) + { + dialog = new ProgressDialog(this); + dialog.setMessage(getString(R.string.load_settings)); + dialog.setIndeterminate(true); + } + + dialog.show(); + } + + @Override + public void hideLoading() + { + dialog.dismiss(); + } + + @Override + public void showPermissionNeededHint() + { + Toast.makeText(this, R.string.write_permission_needed, Toast.LENGTH_SHORT) + .show(); + } + @Override public HashMap getSettings(int file) { diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/settings/SettingsActivityPresenter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/settings/SettingsActivityPresenter.java index 9e68c00d20..20cf5bcc78 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/settings/SettingsActivityPresenter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/settings/SettingsActivityPresenter.java @@ -1,15 +1,20 @@ package org.dolphinemu.dolphinemu.ui.settings; +import android.content.IntentFilter; import android.os.Bundle; import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.model.settings.SettingSection; +import org.dolphinemu.dolphinemu.services.DirectoryInitializationService; +import org.dolphinemu.dolphinemu.utils.DirectoryStateReceiver; import org.dolphinemu.dolphinemu.utils.Log; import org.dolphinemu.dolphinemu.utils.SettingsFile; import java.util.ArrayList; import java.util.HashMap; +import rx.functions.Action1; + public final class SettingsActivityPresenter { private static final String KEY_SHOULD_SAVE = "should_save"; @@ -22,6 +27,10 @@ public final class SettingsActivityPresenter private boolean mShouldSave; + private DirectoryStateReceiver directoryStateReceiver; + + private String menuTag; + public SettingsActivityPresenter(SettingsActivityView view) { mView = view; @@ -31,12 +40,10 @@ public final class SettingsActivityPresenter { if (savedInstanceState == null) { - mView.showSettingsFragment(menuTag, false); - mSettings.add(SettingsFile.SETTINGS_DOLPHIN, SettingsFile.readFile(SettingsFile.FILE_NAME_DOLPHIN, mView)); mSettings.add(SettingsFile.SETTINGS_GFX, SettingsFile.readFile(SettingsFile.FILE_NAME_GFX, mView)); mSettings.add(SettingsFile.SETTINGS_WIIMOTE, SettingsFile.readFile(SettingsFile.FILE_NAME_WIIMOTE, mView)); - mView.onSettingsFileLoaded(mSettings); + this.menuTag = menuTag; } else { @@ -44,6 +51,41 @@ public final class SettingsActivityPresenter } } + public void onStart() + { + prepareDolphinDirectoriesIfNeeded(); + } + + void loadSettingsUI() + { + mView.showSettingsFragment(menuTag, false); + mView.onSettingsFileLoaded(mSettings); + } + + private void prepareDolphinDirectoriesIfNeeded() + { + if (DirectoryInitializationService.areDolphinDirectoriesReady()) { + loadSettingsUI(); + } else { + mView.showLoading(); + IntentFilter statusIntentFilter = new IntentFilter( + DirectoryInitializationService.BROADCAST_ACTION); + + directoryStateReceiver = + new DirectoryStateReceiver(directoryInitializationState -> { + if (directoryInitializationState == DirectoryInitializationService.DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED) { + mView.hideLoading(); + loadSettingsUI(); + } else if (directoryInitializationState == DirectoryInitializationService.DirectoryInitializationState.EXTERNAL_STORAGE_PERMISSION_NEEDED) { + mView.showPermissionNeededHint(); + mView.hideLoading(); + } + }); + + mView.startDirectoryInitializationService(directoryStateReceiver, statusIntentFilter); + } + } + public void setSettings(ArrayList> settings) { mSettings = settings; @@ -56,6 +98,12 @@ public final class SettingsActivityPresenter public void onStop(boolean finishing) { + if (directoryStateReceiver != null) + { + mView.stopListeningToDirectoryInitializationService(directoryStateReceiver); + directoryStateReceiver = null; + } + if (mSettings != null && finishing && mShouldSave) { Log.debug("[SettingsActivity] Settings activity stopping. Saving settings to INI..."); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/settings/SettingsActivityView.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/settings/SettingsActivityView.java index 8c5d6b9731..686338eaee 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/settings/SettingsActivityView.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/settings/SettingsActivityView.java @@ -1,6 +1,9 @@ package org.dolphinemu.dolphinemu.ui.settings; +import android.content.IntentFilter; + import org.dolphinemu.dolphinemu.model.settings.SettingSection; +import org.dolphinemu.dolphinemu.utils.DirectoryStateReceiver; import java.util.ArrayList; import java.util.HashMap; @@ -99,4 +102,34 @@ public interface SettingsActivityView * @param value New setting for the extension. */ void onExtensionSettingChanged(String key, int value); + + /** + * Show loading dialog while loading the settings + */ + void showLoading(); + + /** + * Hide the loading the dialog + */ + void hideLoading(); + + /** + * Show a hint to the user that the app needs write to external storage access + */ + void showPermissionNeededHint(); + + /** + * Start the DirectoryInitializationService and listen for the result. + * + * @param receiver the broadcast receiver for the DirectoryInitializationService + * @param filter the Intent broadcasts to be received. + */ + void startDirectoryInitializationService(DirectoryStateReceiver receiver, IntentFilter filter); + + /** + * Stop listening to the DirectoryInitializationService. + * + * @param receiver The broadcast receiver to unregister. + */ + void stopListeningToDirectoryInitializationService(DirectoryStateReceiver receiver); } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/DirectoryStateReceiver.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/DirectoryStateReceiver.java new file mode 100644 index 0000000000..ac00782366 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/DirectoryStateReceiver.java @@ -0,0 +1,24 @@ +package org.dolphinemu.dolphinemu.utils; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +import org.dolphinemu.dolphinemu.services.DirectoryInitializationService; +import org.dolphinemu.dolphinemu.services.DirectoryInitializationService.DirectoryInitializationState; + +import rx.functions.Action1; + +public class DirectoryStateReceiver extends BroadcastReceiver { + Action1 callback; + public DirectoryStateReceiver(Action1 callback) { + this.callback = callback; + } + + @Override + public void onReceive(Context context, Intent intent) + { + DirectoryInitializationState state = (DirectoryInitializationState) intent.getSerializableExtra(DirectoryInitializationService.EXTRA_STATE); + callback.call(state); + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/PermissionsHandler.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/PermissionsHandler.java index e569df112a..a38e33207a 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/PermissionsHandler.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/PermissionsHandler.java @@ -2,6 +2,7 @@ package org.dolphinemu.dolphinemu.utils; import android.annotation.TargetApi; import android.app.AlertDialog; +import android.content.Context; import android.content.DialogInterface; import android.content.pm.PackageManager; import android.os.Build; @@ -40,9 +41,9 @@ public class PermissionsHandler { return true; } - public static boolean hasWriteAccess(FragmentActivity activity) { + public static boolean hasWriteAccess(Context context) { if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - int hasWritePermission = ContextCompat.checkSelfPermission(activity, WRITE_EXTERNAL_STORAGE); + int hasWritePermission = ContextCompat.checkSelfPermission(context, WRITE_EXTERNAL_STORAGE); return hasWritePermission == PackageManager.PERMISSION_GRANTED; } 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 34cde9e59c..398f569741 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 @@ -1,15 +1,13 @@ package org.dolphinemu.dolphinemu.utils; import android.content.Intent; -import android.content.SharedPreferences; import android.os.Bundle; -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 org.dolphinemu.dolphinemu.services.AssetCopyService; +import org.dolphinemu.dolphinemu.services.DirectoryInitializationService; public final class StartupHandler { @@ -17,9 +15,8 @@ public final class StartupHandler { NativeLibrary.SetUserDirectory(""); // Auto-Detect - // Only perform these extensive copy operations once. if (PermissionsHandler.checkWritePermission(parent)) { - copyAssetsIfNeeded(parent); + DirectoryInitializationService.startService(parent); } Intent intent = parent.getIntent(); @@ -45,16 +42,4 @@ public final class StartupHandler } return false; } - - public static void copyAssetsIfNeeded(FragmentActivity parent) { - SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(parent); - boolean assetsCopied = preferences.getBoolean("assetsCopied", false); - - if (!assetsCopied) - { - // Copy assets into appropriate locations. - Intent copyAssets = new Intent(parent, AssetCopyService.class); - parent.startService(copyAssets); - } - } } diff --git a/Source/Android/app/src/main/res/values/strings.xml b/Source/Android/app/src/main/res/values/strings.xml index 9f51d02edd..d508c148e8 100644 --- a/Source/Android/app/src/main/res/values/strings.xml +++ b/Source/Android/app/src/main/res/values/strings.xml @@ -244,4 +244,6 @@ Controllers You need to allow write access to external storage for the emulator to work + + Loading Settings...