diff --git a/pkg/android/phoenix/AndroidManifest.xml b/pkg/android/phoenix/AndroidManifest.xml index dafdc7e2e6..16c235a30b 100644 --- a/pkg/android/phoenix/AndroidManifest.xml +++ b/pkg/android/phoenix/AndroidManifest.xml @@ -10,6 +10,8 @@ <uses-feature android:name="android.hardware.touchscreen" android:required="false"/> <uses-feature android:name="android.software.leanback" android:required="false" /> <uses-feature android:name="android.hardware.gamepad" android:required="false"/> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.VIBRATE" /> @@ -23,6 +25,7 @@ android:isGame="true" android:banner="@drawable/banner" android:extractNativeLibs="true" + android:requestLegacyExternalStorage="true" tools:ignore="UnusedAttribute"> <activity android:name="com.retroarch.browser.mainmenu.MainMenuActivity" android:exported="true" android:launchMode="singleInstance"> <intent-filter> diff --git a/pkg/android/phoenix/src/com/retroarch/browser/mainmenu/MainMenuActivity.java b/pkg/android/phoenix/src/com/retroarch/browser/mainmenu/MainMenuActivity.java index bc555a74f9..7a7117c901 100644 --- a/pkg/android/phoenix/src/com/retroarch/browser/mainmenu/MainMenuActivity.java +++ b/pkg/android/phoenix/src/com/retroarch/browser/mainmenu/MainMenuActivity.java @@ -26,7 +26,92 @@ import android.util.Log; */ public final class MainMenuActivity extends PreferenceActivity { + final private int REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS = 124; public static String PACKAGE_NAME; + boolean checkPermissions = false; + + 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<String> permissionsList, String permission) + { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) + { + if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) + { + permissionsList.add(permission); + + // Check for Rationale Option + if (!shouldShowRequestPermissionRationale(permission)) + return false; + } + } + + return true; + } + + public void checkRuntimePermissions() + { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) + { + // Android 6.0+ needs runtime permission checks + List<String> permissionsNeeded = new ArrayList<String>(); + final List<String> permissionsList = new ArrayList<String>(); + + 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) + { + checkPermissions = true; + + if (permissionsNeeded.size() > 0) + { + // Need Rationale + Log.i("MainMenuActivity", "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) + { + if (which == AlertDialog.BUTTON_POSITIVE) + { + requestPermissions(permissionsList.toArray(new String[permissionsList.size()]), + REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS); + + Log.i("MainMenuActivity", "User accepted request for external storage permissions."); + } + } + }); + } + else + { + requestPermissions(permissionsList.toArray(new String[permissionsList.size()]), + REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS); + + Log.i("MainMenuActivity", "Requested external storage permissions."); + } + } + } + + if (!checkPermissions) + { + finalStartup(); + } + } public void finalStartup() { @@ -47,6 +132,33 @@ public final class MainMenuActivity extends PreferenceActivity finish(); } + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) + { + switch (requestCode) + { + case REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS: + for (int i = 0; i < permissions.length; i++) + { + if(grantResults[i] == PackageManager.PERMISSION_GRANTED) + { + Log.i("MainMenuActivity", "Permission: " + permissions[i] + " was granted."); + } + else + { + Log.i("MainMenuActivity", "Permission: " + permissions[i] + " was not granted."); + } + } + + break; + default: + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + break; + } + + finalStartup(); + } + public static void startRetroActivity(Intent retro, String contentPath, String corePath, String configFilePath, String imePath, String dataDirPath, String dataSourcePath) { @@ -58,8 +170,8 @@ public final class MainMenuActivity extends PreferenceActivity retro.putExtra("IME", imePath); retro.putExtra("DATADIR", dataDirPath); retro.putExtra("APK", dataSourcePath); + retro.putExtra("SDCARD", Environment.getExternalStorageDirectory().getAbsolutePath()); String external = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Android/data/" + PACKAGE_NAME + "/files"; - retro.putExtra("SDCARD", external); retro.putExtra("EXTERNAL", external); } @@ -75,6 +187,6 @@ public final class MainMenuActivity extends PreferenceActivity UserPreferences.updateConfigFile(this); - finalStartup(); + checkRuntimePermissions(); } } diff --git a/pkg/android/phoenix/src/com/retroarch/browser/provider/RetroDocumentsProvider.java b/pkg/android/phoenix/src/com/retroarch/browser/provider/RetroDocumentsProvider.java index 52bd33a058..4d2f927835 100644 --- a/pkg/android/phoenix/src/com/retroarch/browser/provider/RetroDocumentsProvider.java +++ b/pkg/android/phoenix/src/com/retroarch/browser/provider/RetroDocumentsProvider.java @@ -10,7 +10,6 @@ import android.database.MatrixCursor; import android.graphics.Point; import android.os.Build; import android.os.CancellationSignal; -import android.os.Environment; import android.os.ParcelFileDescriptor; import android.provider.DocumentsContract; import android.provider.DocumentsContract.Document; @@ -70,31 +69,19 @@ public class RetroDocumentsProvider extends DocumentsProvider { @Override public Cursor queryRoots(String[] projection) throws FileNotFoundException { + final File BASE_DIR = new File(getContext().getFilesDir().getParent()); final MatrixCursor result = new MatrixCursor(projection != null ? projection : DEFAULT_ROOT_PROJECTION); @SuppressWarnings("ConstantConditions") final String applicationName = getContext().getString(R.string.app_name); - final File CORE_DIR = new File(getContext().getFilesDir().getParent()); - final MatrixCursor.RowBuilder core = result.newRow(); - core.add(Root.COLUMN_ROOT_ID, getDocIdForFile(CORE_DIR)); - core.add(Root.COLUMN_DOCUMENT_ID, getDocIdForFile(CORE_DIR)); - core.add(Root.COLUMN_SUMMARY, "Core Data"); - core.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_CREATE | Root.FLAG_SUPPORTS_SEARCH | Root.FLAG_SUPPORTS_IS_CHILD); - core.add(Root.COLUMN_TITLE, applicationName); - core.add(Root.COLUMN_MIME_TYPES, ALL_MIME_TYPES); - core.add(Root.COLUMN_AVAILABLE_BYTES, CORE_DIR.getFreeSpace()); - core.add(Root.COLUMN_ICON, R.mipmap.ic_launcher); - - final File USER_DIR = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/Android/data/" + getContext().getPackageName() + "/files/RetroArch"); - final MatrixCursor.RowBuilder user = result.newRow(); - user.add(Root.COLUMN_ROOT_ID, getDocIdForFile(USER_DIR)); - user.add(Root.COLUMN_DOCUMENT_ID, getDocIdForFile(USER_DIR)); - user.add(Root.COLUMN_SUMMARY, "User Data"); - user.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_CREATE | Root.FLAG_SUPPORTS_SEARCH | Root.FLAG_SUPPORTS_IS_CHILD); - user.add(Root.COLUMN_TITLE, applicationName); - user.add(Root.COLUMN_MIME_TYPES, ALL_MIME_TYPES); - user.add(Root.COLUMN_AVAILABLE_BYTES, USER_DIR.getFreeSpace()); - user.add(Root.COLUMN_ICON, R.mipmap.ic_launcher); - + final MatrixCursor.RowBuilder row = result.newRow(); + row.add(Root.COLUMN_ROOT_ID, getDocIdForFile(BASE_DIR)); + row.add(Root.COLUMN_DOCUMENT_ID, getDocIdForFile(BASE_DIR)); + row.add(Root.COLUMN_SUMMARY, null); + row.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_CREATE | Root.FLAG_SUPPORTS_SEARCH | Root.FLAG_SUPPORTS_IS_CHILD); + row.add(Root.COLUMN_TITLE, applicationName); + row.add(Root.COLUMN_MIME_TYPES, ALL_MIME_TYPES); + row.add(Root.COLUMN_AVAILABLE_BYTES, BASE_DIR.getFreeSpace()); + row.add(Root.COLUMN_ICON, R.mipmap.ic_launcher); return result; }