diff --git a/frontend/drivers/platform_linux.c b/frontend/drivers/platform_linux.c
index 0f68a4c0d0e5..d0fc3013d141 100644
--- a/frontend/drivers/platform_linux.c
+++ b/frontend/drivers/platform_linux.c
@@ -1463,6 +1463,10 @@ static void frontend_linux_get_env(int *argc,
strlcpy(app_dir, argv, sizeof(app_dir));
(*env)->ReleaseStringUTFChars(env, jstr, argv);
+ /* Check for runtime permissions on Android 6.0+ */
+ if (env && android_app->checkRuntimePermissions)
+ CALL_VOID_METHOD(env, android_app->activity->clazz, android_app->checkRuntimePermissions);
+
//set paths depending on the ability to write to internal_storage_path
if(!string_is_empty(internal_storage_path))
@@ -1867,6 +1871,8 @@ static void frontend_linux_init(void *data)
"onRetroArchExit", "()V");
GET_METHOD_ID(env, android_app->isAndroidTV, class,
"isAndroidTV", "()Z");
+ GET_METHOD_ID(env, android_app->checkRuntimePermissions, class,
+ "checkRuntimePermissions", "()V");
CALL_OBJ_METHOD(env, obj, android_app->activity->clazz,
android_app->getIntent);
diff --git a/frontend/drivers/platform_linux.h b/frontend/drivers/platform_linux.h
index 7fe9dc83f726..762887b24cb5 100644
--- a/frontend/drivers/platform_linux.h
+++ b/frontend/drivers/platform_linux.h
@@ -161,6 +161,7 @@ struct android_app
jmethodID getPendingIntentDownloadsLocation;
jmethodID getPendingIntentScreenshotsLocation;
jmethodID isAndroidTV;
+ jmethodID checkRuntimePermissions;
};
diff --git a/pkg/android/phoenix/AndroidManifest.xml b/pkg/android/phoenix/AndroidManifest.xml
index b9de3c3a0087..07e7d9d95637 100644
--- a/pkg/android/phoenix/AndroidManifest.xml
+++ b/pkg/android/phoenix/AndroidManifest.xml
@@ -9,7 +9,7 @@
+ android:targetSdkVersion="23" />
diff --git a/pkg/android/phoenix/project.properties b/pkg/android/phoenix/project.properties
index 82181fe16aeb..319851bab09c 100644
--- a/pkg/android/phoenix/project.properties
+++ b/pkg/android/phoenix/project.properties
@@ -11,5 +11,5 @@
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
# Project target.
-target=android-22
+target=android-23
android.library.reference.1=libs/googleplay
diff --git a/pkg/android/phoenix/src/com/retroarch/browser/retroactivity/RetroActivityCommon.java b/pkg/android/phoenix/src/com/retroarch/browser/retroactivity/RetroActivityCommon.java
index 06e1a8c7bab7..48a858976ff1 100644
--- a/pkg/android/phoenix/src/com/retroarch/browser/retroactivity/RetroActivityCommon.java
+++ b/pkg/android/phoenix/src/com/retroarch/browser/retroactivity/RetroActivityCommon.java
@@ -1,15 +1,23 @@
package com.retroarch.browser.retroactivity;
+import java.util.List;
+import java.util.ArrayList;
import com.retroarch.browser.preferences.util.UserPreferences;
import android.content.res.Configuration;
import android.app.UiModeManager;
import android.util.Log;
+import android.content.pm.PackageManager;
+import android.Manifest;
+import android.content.DialogInterface;
+import android.app.AlertDialog;
/**
* Class which provides common methods for RetroActivity related classes.
*/
public class RetroActivityCommon extends RetroActivityLocation
{
+ final private int REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS = 124;
+
// Exiting cleanly from NDK seems to be nearly impossible.
// Have to use exit(0) to avoid weird things happening, even with runOnUiThread() approaches.
// Use a separate JNI function to explicitly trigger the readback.
@@ -18,6 +26,81 @@ public void onRetroArchExit()
finish();
}
+ public void showMessageOKCancel(String message, DialogInterface.OnClickListener onClickListener)
+ {
+ new AlertDialog.Builder(this).setMessage(message)
+ .setPositiveButton("OK", onClickListener).setCancelable(false)
+ .setNegativeButton("Cancel", null).create().show();
+ }
+
+ private boolean addPermission(List permissionsList, String permission)
+ {
+ if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
+ permissionsList.add(permission);
+ // Check for Rationale Option
+ if (!shouldShowRequestPermissionRationale(permission))
+ return false;
+ }
+ return true;
+ }
+
+ public void checkRuntimePermissions()
+ {
+ runOnUiThread(new Runnable() {
+ public void run() {
+ checkRuntimePermissionsRunnable();
+ }
+ });
+ }
+
+ public void checkRuntimePermissionsRunnable()
+ {
+ if (android.os.Build.VERSION.SDK_INT >= 23)
+ {
+ // Android 6.0+ needs runtime permission checks
+ List permissionsNeeded = new ArrayList();
+ final List permissionsList = new ArrayList();
+
+ if (!addPermission(permissionsList, Manifest.permission.READ_EXTERNAL_STORAGE))
+ permissionsNeeded.add("Read External Storage");
+ if (!addPermission(permissionsList, Manifest.permission.WRITE_EXTERNAL_STORAGE))
+ permissionsNeeded.add("Write External Storage");
+
+ if (permissionsList.size() > 0)
+ {
+ if (permissionsNeeded.size() > 0)
+ {
+ // Need Rationale
+ Log.i("RetroActivity", "Need to request external storage permissions.");
+
+ String message = "You need to grant access to " + permissionsNeeded.get(0);
+
+ for (int i = 1; i < permissionsNeeded.size(); i++)
+ message = message + ", " + permissionsNeeded.get(i);
+
+ showMessageOKCancel(message,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which)
+ {
+ requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),
+ REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
+
+ Log.i("RetroActivity", "User accepted request for external storage permissions.");
+ }
+ });
+ }
+ else
+ {
+ requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),
+ REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
+
+ Log.i("RetroActivity", "Requested external storage permissions.");
+ }
+ }
+ }
+ }
+
public boolean isAndroidTV()
{
Configuration config = getResources().getConfiguration();