From 6cef33b501f4e8eddd33a0b598b6d6e69df4c8aa Mon Sep 17 00:00:00 2001 From: Aaron Culliney Date: Sat, 12 Dec 2015 13:33:35 -0800 Subject: [PATCH] Request and handle external storage permissions on Marshmallow - Also disentangles exposing APK assets internally and on external storage --- .../org/deadc0de/apple2ix/Apple2Activity.java | 51 ++++++++++++++++++- .../deadc0de/apple2ix/Apple2DisksMenu.java | 43 ++++++++++++---- 2 files changed, 84 insertions(+), 10 deletions(-) diff --git a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Activity.java b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Activity.java index 6c0b52be..48862746 100644 --- a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Activity.java +++ b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Activity.java @@ -11,10 +11,12 @@ package org.deadc0de.apple2ix; +import android.Manifest; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; +import android.content.pm.PackageManager; import android.media.AudioManager; import android.net.Uri; import android.os.Build; @@ -92,6 +94,8 @@ public class Apple2Activity extends Activity { public final static long NATIVE_TOUCH_ASCII_MASK = 0xFF00L; public final static long NATIVE_TOUCH_SCANCODE_MASK = 0x00FFL; + public final static int REQUEST_PERMISSION_RWSTORE = 42; + private native void nativeOnCreate(String dataDir, int sampleRate, int monoBufferSize, int stereoBufferSize); private native void nativeOnKeyDown(int keyCode, int metaState); @@ -166,6 +170,27 @@ public class Apple2Activity extends Activity { showSplashScreen(!firstTime); Apple2CrashHandler.getInstance().checkForCrashes(this); + boolean extperm = true; + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + // On Marshmallow+ specifically ask for permission to read/write storage + String readPermission = Manifest.permission.READ_EXTERNAL_STORAGE; + String writePermission = Manifest.permission.WRITE_EXTERNAL_STORAGE; + int hasReadPermission = checkSelfPermission(readPermission); + int hasWritePermission = checkSelfPermission(writePermission); + ArrayList permissions = new ArrayList(); + if (hasReadPermission != PackageManager.PERMISSION_GRANTED) { + permissions.add(readPermission); + } + if (hasWritePermission != PackageManager.PERMISSION_GRANTED) { + permissions.add(writePermission); + } + if (!permissions.isEmpty()) { + extperm = false; + String[] params = permissions.toArray(new String[permissions.size()]); + requestPermissions(params, REQUEST_PERMISSION_RWSTORE); + } + } + mGraphicsInitializedRunnable = new Runnable() { @Override public void run() { @@ -177,11 +202,15 @@ public class Apple2Activity extends Activity { }; // first-time initializations + final boolean externalStoragePermission = extperm; if (firstTime) { new Thread(new Runnable() { @Override public void run() { - Apple2DisksMenu.firstTime(Apple2Activity.this); + Apple2DisksMenu.exposeAPKAssets(Apple2Activity.this); + if (externalStoragePermission) { + Apple2DisksMenu.exposeAPKAssetsToExternal(Apple2Activity.this); + } mSplashScreen.setDismissable(true); Log.d(TAG, "Finished first time copying..."); } @@ -204,6 +233,26 @@ public class Apple2Activity extends Activity { } } + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + // We should already be gracefully handling the case where user denies access. + if (requestCode == REQUEST_PERMISSION_RWSTORE) { + boolean grantedPermissions = true; + for (int grant : grantResults) { + if (grant == PackageManager.PERMISSION_DENIED) { + grantedPermissions = false; + break; + } + } + if (grantedPermissions) { + // this will force copying APK files (now that we have permission + Apple2DisksMenu.exposeAPKAssetsToExternal(Apple2Activity.this); + } // else ... we keep nagging on app startup ... + } else { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + } + } + @Override protected void onResume() { super.onResume(); diff --git a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2DisksMenu.java b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2DisksMenu.java index c2734579..780eac16 100644 --- a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2DisksMenu.java +++ b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2DisksMenu.java @@ -135,7 +135,7 @@ public class Apple2DisksMenu implements Apple2MenuView { return sDataDir; } - public static void firstTime(Apple2Activity activity) { + public static void exposeAPKAssetsToExternal(Apple2Activity activity) { final ProgressBar bar = (ProgressBar) activity.findViewById(R.id.crash_progressBar); activity.runOnUiThread(new Runnable() { @Override @@ -144,7 +144,38 @@ public class Apple2DisksMenu implements Apple2MenuView { bar.setVisibility(View.VISIBLE); bar.setIndeterminate(true); } catch (NullPointerException npe) { - Log.v(TAG, "Whoa, avoided NPE in first time #1"); + Log.v(TAG, "Avoid NPE in exposeAPKAssetsToExternal #1"); + } + } + }); + + getExternalStorageDirectory(activity); + Log.v(TAG, "Overwriting system files in /sdcard/apple2ix/ (external storage) ..."); + recursivelyCopyAPKAssets(activity, /*from APK directory:*/"keyboards", /*to location:*/sExternalFilesDir.getAbsolutePath()); + + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + try { + bar.setVisibility(View.INVISIBLE); + bar.setIndeterminate(false); + } catch (NullPointerException npe) { + Log.v(TAG, "Avoid NPE in exposeAPKAssetsToExternal #2"); + } + } + }); + } + + public static void exposeAPKAssets(Apple2Activity activity) { + final ProgressBar bar = (ProgressBar) activity.findViewById(R.id.crash_progressBar); + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + try { + bar.setVisibility(View.VISIBLE); + bar.setIndeterminate(true); + } catch (NullPointerException npe) { + Log.v(TAG, "Avoid NPE in exposeAPKAssets #1"); } } }); @@ -154,16 +185,10 @@ public class Apple2DisksMenu implements Apple2MenuView { Log.d(TAG, "First time copying stuff-n-things out of APK for ease-of-NDK access..."); getExternalStorageDirectory(activity); - recursivelyCopyAPKAssets(activity, /*from APK directory:*/"disks", /*to location:*/new File(sDataDir, "disks").getAbsolutePath()); recursivelyCopyAPKAssets(activity, /*from APK directory:*/"keyboards", /*to location:*/new File(sDataDir, "keyboards").getAbsolutePath()); recursivelyCopyAPKAssets(activity, /*from APK directory:*/"shaders", /*to location:*/new File(sDataDir, "shaders").getAbsolutePath()); - // expose keyboards to modding - if (sExternalFilesDir != null) { - recursivelyCopyAPKAssets(activity, /*from APK directory:*/"keyboards", /*to location:*/sExternalFilesDir.getAbsolutePath()); - } - activity.runOnUiThread(new Runnable() { @Override public void run() { @@ -171,7 +196,7 @@ public class Apple2DisksMenu implements Apple2MenuView { bar.setVisibility(View.INVISIBLE); bar.setIndeterminate(false); } catch (NullPointerException npe) { - Log.v(TAG, "Whoa, avoided NPE in first time #2"); + Log.v(TAG, "Avoid NPE in exposeAPKAssets #1"); } } });