diff --git a/Source/Android/app/build.gradle b/Source/Android/app/build.gradle
index 089c96a055..c53a0fa3d4 100644
--- a/Source/Android/app/build.gradle
+++ b/Source/Android/app/build.gradle
@@ -18,7 +18,7 @@ android {
// TODO If this is ever modified, change application_id in strings.xml
applicationId "org.dolphinemu.dolphinemu"
minSdkVersion 21
- targetSdkVersion 21
+ targetSdkVersion 25
// TODO This should be set to the Buildbot build number for release builds, and be "1" for debug builds.
versionCode 13
@@ -72,13 +72,13 @@ android {
}
dependencies {
- compile 'com.android.support:support-v13:25.2.0'
- compile 'com.android.support:cardview-v7:25.2.0'
- compile 'com.android.support:recyclerview-v7:25.2.0'
- compile 'com.android.support:design:25.2.0'
+ compile 'com.android.support:support-v13:25.3.0'
+ compile 'com.android.support:cardview-v7:25.3.0'
+ compile 'com.android.support:recyclerview-v7:25.3.0'
+ compile 'com.android.support:design:25.3.0'
// Android TV UI libraries.
- compile 'com.android.support:leanback-v17:25.2.0'
+ compile 'com.android.support:leanback-v17:25.3.0'
// For showing the banner as a circle a-la Material Design Guidelines
compile 'de.hdodenhof:circleimageview:2.1.0'
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 57923e0e0e..14e9d1758a 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
@@ -1,6 +1,7 @@
package org.dolphinemu.dolphinemu.ui.main;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.database.Cursor;
import android.os.Bundle;
import android.support.annotation.Nullable;
@@ -13,6 +14,7 @@ import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
+import android.widget.Toast;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.activities.AddDirectoryActivity;
@@ -20,6 +22,7 @@ import org.dolphinemu.dolphinemu.adapters.PlatformPagerAdapter;
import org.dolphinemu.dolphinemu.model.GameProvider;
import org.dolphinemu.dolphinemu.ui.platform.PlatformGamesView;
import org.dolphinemu.dolphinemu.ui.settings.SettingsActivity;
+import org.dolphinemu.dolphinemu.utils.PermissionsHandler;
import org.dolphinemu.dolphinemu.utils.StartupHandler;
/**
@@ -45,9 +48,6 @@ public final class MainActivity extends AppCompatActivity implements MainView
setSupportActionBar(mToolbar);
- PlatformPagerAdapter platformPagerAdapter = new PlatformPagerAdapter(getFragmentManager(), this);
-
- mViewPager.setAdapter(platformPagerAdapter);
mTabLayout.setupWithViewPager(mViewPager);
// Set up the FAB.
@@ -66,6 +66,14 @@ public final class MainActivity extends AppCompatActivity implements MainView
// TODO Split some of this stuff into Application.onCreate()
if (savedInstanceState == null)
StartupHandler.HandleInit(this);
+
+ if (PermissionsHandler.hasWriteAccess(this))
+ {
+ PlatformPagerAdapter platformPagerAdapter = new PlatformPagerAdapter(getFragmentManager(), this);
+ mViewPager.setAdapter(platformPagerAdapter);
+ } else {
+ mViewPager.setVisibility(View.INVISIBLE);
+ }
}
// TODO: Replace with a ButterKnife injection.
@@ -145,6 +153,28 @@ public final class MainActivity extends AppCompatActivity implements MainView
mPresenter.handleActivityResult(requestCode, resultCode);
}
+ @Override
+ public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
+ switch (requestCode) {
+ case PermissionsHandler.REQUEST_CODE_WRITE_PERMISSION:
+ if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ StartupHandler.copyAssetsIfNeeded(this);
+
+ PlatformPagerAdapter platformPagerAdapter = new PlatformPagerAdapter(getFragmentManager(), this);
+ mViewPager.setAdapter(platformPagerAdapter);
+ mTabLayout.setupWithViewPager(mViewPager);
+ mViewPager.setVisibility(View.VISIBLE);
+ } else {
+ Toast.makeText(this, R.string.write_permission_needed, Toast.LENGTH_SHORT)
+ .show();
+ }
+ break;
+ default:
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ break;
+ }
+ }
+
/**
* Called by the framework whenever any actionbar/toolbar icon is clicked.
*
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 a003603b1a..3f235fa1c4 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
@@ -3,6 +3,7 @@ package org.dolphinemu.dolphinemu.ui.main;
import android.app.Activity;
import android.app.FragmentManager;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.database.Cursor;
import android.os.Bundle;
import android.support.v17.leanback.app.BrowseFragment;
@@ -16,6 +17,7 @@ import android.support.v17.leanback.widget.OnItemViewClickedListener;
import android.support.v17.leanback.widget.Presenter;
import android.support.v17.leanback.widget.Row;
import android.support.v17.leanback.widget.RowPresenter;
+import android.widget.Toast;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.activities.AddDirectoryActivity;
@@ -25,6 +27,7 @@ import org.dolphinemu.dolphinemu.adapters.SettingsRowPresenter;
import org.dolphinemu.dolphinemu.model.Game;
import org.dolphinemu.dolphinemu.model.TvSettingsItem;
import org.dolphinemu.dolphinemu.ui.settings.SettingsActivity;
+import org.dolphinemu.dolphinemu.utils.PermissionsHandler;
import org.dolphinemu.dolphinemu.utils.StartupHandler;
import org.dolphinemu.dolphinemu.viewholders.TvGameViewHolder;
@@ -148,14 +151,31 @@ public final class TvMainActivity extends Activity implements MainView
mPresenter.handleActivityResult(requestCode, resultCode);
}
+ @Override
+ public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
+ switch (requestCode) {
+ case PermissionsHandler.REQUEST_CODE_WRITE_PERMISSION:
+ if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ StartupHandler.copyAssetsIfNeeded(this);
+ loadGames();
+ } else {
+ Toast.makeText(this, R.string.write_permission_needed, Toast.LENGTH_SHORT)
+ .show();
+ }
+ break;
+ default:
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ break;
+ }
+ }
+
private void buildRowsAdapter()
{
mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
- // For each platform
- for (int platformIndex = 0; platformIndex <= Game.PLATFORM_ALL; ++platformIndex)
+ if (PermissionsHandler.hasWriteAccess(this))
{
- mPresenter.loadGames(platformIndex);
+ loadGames();
}
mRowsAdapter.add(buildSettingsRow());
@@ -163,6 +183,13 @@ public final class TvMainActivity extends Activity implements MainView
mBrowseFragment.setAdapter(mRowsAdapter);
}
+ private void loadGames() {
+ // For each platform
+ for (int platformIndex = 0; platformIndex <= Game.PLATFORM_ALL; ++platformIndex) {
+ mPresenter.loadGames(platformIndex);
+ }
+ }
+
private ListRow buildGamesRow(int platform, Cursor games)
{
// Create an adapter for this row.
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
new file mode 100644
index 0000000000..84ea110884
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/PermissionsHandler.java
@@ -0,0 +1,71 @@
+package org.dolphinemu.dolphinemu.utils;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.support.v4.content.ContextCompat;
+import android.widget.Toast;
+
+import org.dolphinemu.dolphinemu.R;
+
+import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
+
+public class PermissionsHandler {
+ public static final int REQUEST_CODE_WRITE_PERMISSION = 500;
+
+ @TargetApi(Build.VERSION_CODES.M)
+ public static boolean checkWritePermission(final Activity activity) {
+ if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+ return true;
+ }
+
+ int hasWritePermission = ContextCompat.checkSelfPermission(activity, WRITE_EXTERNAL_STORAGE);
+
+ if (hasWritePermission != PackageManager.PERMISSION_GRANTED) {
+ if (activity.shouldShowRequestPermissionRationale(WRITE_EXTERNAL_STORAGE)) {
+ showMessageOKCancel(activity, activity.getString(R.string.write_permission_needed),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ activity.requestPermissions(new String[] {WRITE_EXTERNAL_STORAGE},
+ REQUEST_CODE_WRITE_PERMISSION);
+ }
+ });
+ return false;
+ }
+
+ activity.requestPermissions(new String[] {WRITE_EXTERNAL_STORAGE},
+ REQUEST_CODE_WRITE_PERMISSION);
+ return false;
+ }
+
+ return true;
+ }
+
+ public static boolean hasWriteAccess(Activity activity) {
+ if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ int hasWritePermission = ContextCompat.checkSelfPermission(activity, WRITE_EXTERNAL_STORAGE);
+ return hasWritePermission == PackageManager.PERMISSION_GRANTED;
+ }
+
+ return true;
+ }
+
+ private static void showMessageOKCancel(final Activity activity, String message, DialogInterface.OnClickListener okListener) {
+ new AlertDialog.Builder(activity)
+ .setMessage(message)
+ .setPositiveButton(android.R.string.ok, okListener)
+ .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialogInterface, int i) {
+ Toast.makeText(activity, R.string.write_permission_needed, Toast.LENGTH_SHORT)
+ .show();
+ }
+ })
+ .create()
+ .show();
+ }
+}
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 51b4553088..2e31f405d0 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
@@ -17,15 +17,9 @@ public final class StartupHandler
{
NativeLibrary.SetUserDirectory(""); // Auto-Detect
- SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(parent);
- boolean assetsCopied = preferences.getBoolean("assetsCopied", false);
-
// Only perform these extensive copy operations once.
- if (!assetsCopied)
- {
- // Copy assets into appropriate locations.
- Intent copyAssets = new Intent(parent, AssetCopyService.class);
- parent.startService(copyAssets);
+ if (PermissionsHandler.checkWritePermission(parent)) {
+ copyAssetsIfNeeded(parent);
}
Intent intent = parent.getIntent();
@@ -51,4 +45,16 @@ public final class StartupHandler
}
return false;
}
+
+ public static void copyAssetsIfNeeded(Activity 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 d92e2a9398..f41d8fe8a2 100644
--- a/Source/Android/app/src/main/res/values/strings.xml
+++ b/Source/Android/app/src/main/res/values/strings.xml
@@ -238,4 +238,6 @@
org.dolphinemu.dolphinemu
+
+ You need to allow write access to external storage for the emulator to work