From 5bacf1e9be82173ed6d547c83bc7d572f085da35 Mon Sep 17 00:00:00 2001 From: Braden Farmer Date: Tue, 15 Feb 2022 08:11:39 -0700 Subject: [PATCH] [Android] Populate external storage devices inside the file browser on Android 11+ devices (#13615) [Android] Populate external storage devices inside the file browser on Android 11+ devices --- frontend/drivers/platform_unix.c | 58 +++++++++++++++++ frontend/drivers/platform_unix.h | 3 + pkg/android/phoenix-common/jni/Android.mk | 3 +- .../retroactivity/RetroActivityCommon.java | 42 +++++++++++++ pkg/android/phoenix/AndroidManifest.xml | 2 - .../browser/mainmenu/MainMenuActivity.java | 62 +++---------------- 6 files changed, 112 insertions(+), 58 deletions(-) diff --git a/frontend/drivers/platform_unix.c b/frontend/drivers/platform_unix.c index 0266397c5a..edc7c0902f 100644 --- a/frontend/drivers/platform_unix.c +++ b/frontend/drivers/platform_unix.c @@ -2140,6 +2140,10 @@ static void frontend_unix_init(void *data) "deleteCore", "(Ljava/lang/String;)V"); CALL_OBJ_METHOD(env, obj, android_app->activity->clazz, android_app->getIntent); + GET_METHOD_ID(env, android_app->getVolumeCount, class, + "getVolumeCount", "()I"); + GET_METHOD_ID(env, android_app->getVolumePath, class, + "getVolumePath", "(Ljava/lang/String;)Ljava/lang/String;"); GET_OBJECT_CLASS(env, class, obj); GET_METHOD_ID(env, android_app->getStringExtra, class, @@ -2157,6 +2161,29 @@ static int frontend_unix_parse_drive_list(void *data, bool load_content) MENU_ENUM_LABEL_FILE_BROWSER_DIRECTORY; #ifdef ANDROID + + JNIEnv *env = jni_thread_getenv(); + jint output = 0; + jobject obj = NULL; + jstring jstr = NULL; + + int volume_count = 0; + + if (!env || !g_android) + return 0; + + CALL_OBJ_METHOD(env, obj, g_android->activity->clazz, + g_android->getIntent); + + if (g_android->getVolumeCount) + { + CALL_INT_METHOD(env, output, + g_android->activity->clazz, g_android->getVolumeCount); + volume_count = output; + } + + RARCH_LOG("external volumes: %d\n", volume_count); + if (!string_is_empty(internal_storage_path)) { if (storage_permissions == INTERNAL_STORAGE_WRITABLE) @@ -2203,6 +2230,37 @@ static int frontend_unix_parse_drive_list(void *data, bool load_content) msg_hash_to_str(MSG_APPLICATION_DIR), enum_idx, FILE_TYPE_DIRECTORY, 0, 0); + for (unsigned i=0; i < volume_count; i++) + { + static char aux_path[PATH_MAX_LENGTH]; + char index[2]; + index[0] = '\0'; + + snprintf(index, sizeof(index), "%d", i); + + CALL_OBJ_METHOD_PARAM(env, jstr, g_android->activity->clazz, g_android->getVolumePath, + (*env)->NewStringUTF(env, index)); + + if (jstr) + { + const char *str = (*env)->GetStringUTFChars(env, jstr, 0); + + aux_path[0] = '\0'; + + if (str && *str) + strlcpy(aux_path, str, + sizeof(aux_path)); + + (*env)->ReleaseStringUTFChars(env, jstr, str); + if (!string_is_empty(aux_path)) + menu_entries_append_enum(list, + aux_path, + msg_hash_to_str(MSG_APPLICATION_DIR), + enum_idx, + FILE_TYPE_DIRECTORY, 0, 0); + } + + } #elif defined(WEBOS) if (path_is_directory("/media/internal")) menu_entries_append_enum(list, "/media/internal", diff --git a/frontend/drivers/platform_unix.h b/frontend/drivers/platform_unix.h index a235a9ae16..b95446339a 100644 --- a/frontend/drivers/platform_unix.h +++ b/frontend/drivers/platform_unix.h @@ -175,6 +175,9 @@ struct android_app jmethodID downloadCore; jmethodID deleteCore; + jmethodID getVolumeCount; + jmethodID getVolumePath; + struct { unsigned width, height; diff --git a/pkg/android/phoenix-common/jni/Android.mk b/pkg/android/phoenix-common/jni/Android.mk index 837b5c61cd..36009b414f 100644 --- a/pkg/android/phoenix-common/jni/Android.mk +++ b/pkg/android/phoenix-common/jni/Android.mk @@ -73,7 +73,6 @@ endif DEFINES += -DRARCH_MOBILE \ -DHAVE_GRIFFIN \ -DHAVE_STB_VORBIS \ - -DHAVE_LANGEXTRA \ -DANDROID \ -DHAVE_DYNAMIC \ -DHAVE_OPENGL \ @@ -189,7 +188,7 @@ LOCAL_CXXFLAGS += $(INCLUDE_DIRS) ifeq ($(HAVE_VULKAN),1) INCFLAGS += $(LOCAL_PATH)/$(RARCH_DIR)/gfx/include - + LOCAL_C_INCLUDES += $(INCFLAGS) LOCAL_CPPFLAGS += -I$(LOCAL_PATH)/$(DEPS_DIR)/glslang \ -I$(LOCAL_PATH)/$(DEPS_DIR)/glslang/glslang/glslang/Public \ diff --git a/pkg/android/phoenix-common/src/com/retroarch/browser/retroactivity/RetroActivityCommon.java b/pkg/android/phoenix-common/src/com/retroarch/browser/retroactivity/RetroActivityCommon.java index c8c92d7622..d6145f239f 100644 --- a/pkg/android/phoenix-common/src/com/retroarch/browser/retroactivity/RetroActivityCommon.java +++ b/pkg/android/phoenix-common/src/com/retroarch/browser/retroactivity/RetroActivityCommon.java @@ -20,6 +20,8 @@ import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.media.AudioAttributes; import android.os.Bundle; +import android.os.storage.StorageManager; +import android.os.storage.StorageVolume; import android.system.Os; import android.view.InputDevice; import android.view.Surface; @@ -32,6 +34,7 @@ import android.os.Vibrator; import android.os.VibrationEffect; import android.util.Log; + import java.io.File; import java.util.ArrayList; import java.util.Arrays; @@ -165,6 +168,45 @@ public class RetroActivityCommon extends NativeActivity finish(); } + public int getVolumeCount() + { + int ret = 0; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + StorageManager storageManager = (StorageManager) getApplicationContext().getSystemService(Context.STORAGE_SERVICE); + List storageVolumeList = storageManager.getStorageVolumes(); + + for (int i = 0; i < storageVolumeList.size(); i++) { + ret++; + } + Log.i("RetroActivity", "volume count: " + ret); + } + + return (int)ret; + } + + public String getVolumePath(String input) + { + String ret = ""; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + int index = Integer.valueOf(input); + int j = 0; + + StorageManager storageManager = (StorageManager) getApplicationContext().getSystemService(Context.STORAGE_SERVICE); + List storageVolumeList = storageManager.getStorageVolumes(); + + for (int i = 0; i < storageVolumeList.size(); i++) { + if (i == j) { + ret = String.valueOf(storageVolumeList.get(index).getDirectory()); + } + } + Log.i("RetroActivity", "volume path: " + ret); + } + + return ret; + } + // https://stackoverflow.com/questions/4553650/how-to-check-device-natural-default-orientation-on-android-i-e-get-landscape/4555528#4555528 public int getDeviceDefaultOrientation() { WindowManager windowManager = (WindowManager)getSystemService(Context.WINDOW_SERVICE); diff --git a/pkg/android/phoenix/AndroidManifest.xml b/pkg/android/phoenix/AndroidManifest.xml index 857cb28f2d..35216870a2 100644 --- a/pkg/android/phoenix/AndroidManifest.xml +++ b/pkg/android/phoenix/AndroidManifest.xml @@ -9,10 +9,8 @@ - - 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 3cc6f3d524..7a7117c901 100644 --- a/pkg/android/phoenix/src/com/retroarch/browser/mainmenu/MainMenuActivity.java +++ b/pkg/android/phoenix/src/com/retroarch/browser/mainmenu/MainMenuActivity.java @@ -3,12 +3,9 @@ package com.retroarch.browser.mainmenu; import com.retroarch.browser.preferences.util.UserPreferences; import com.retroarch.browser.retroactivity.RetroActivityFuture; -import android.content.ActivityNotFoundException; import android.content.Intent; import android.content.SharedPreferences; import android.media.AudioManager; -import android.net.Uri; -import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.preference.PreferenceActivity; @@ -22,7 +19,6 @@ import android.Manifest; import android.content.DialogInterface; import android.app.AlertDialog; import android.util.Log; -import android.widget.Toast; /** * {@link PreferenceActivity} subclass that provides all of the @@ -33,7 +29,6 @@ public final class MainMenuActivity extends PreferenceActivity final private int REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS = 124; public static String PACKAGE_NAME; boolean checkPermissions = false; - boolean checkManageExternalStoragePermission = false; public void showMessageOKCancel(String message, DialogInterface.OnClickListener onClickListener) { @@ -67,25 +62,12 @@ public final class MainMenuActivity extends PreferenceActivity List permissionsNeeded = new ArrayList(); final List permissionsList = new ArrayList(); - final boolean requiresManageExternalStoragePermission = - getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.Q - && Build.VERSION.SDK_INT > Build.VERSION_CODES.Q; + 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"); - boolean shouldRequestManageExternalStoragePermission = false; - - if (requiresManageExternalStoragePermission) { - if (!Environment.isExternalStorageManager()) { - shouldRequestManageExternalStoragePermission = true; - permissionsNeeded.add("Manage External Storage"); - } - } else { - 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 || shouldRequestManageExternalStoragePermission) + if (permissionsList.size() > 0) { checkPermissions = true; @@ -107,29 +89,10 @@ public final class MainMenuActivity extends PreferenceActivity { if (which == AlertDialog.BUTTON_POSITIVE) { - if (requiresManageExternalStoragePermission) { - checkManageExternalStoragePermission = true; + requestPermissions(permissionsList.toArray(new String[permissionsList.size()]), + REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS); - Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION); - intent.setData(Uri.fromParts("package", getPackageName(), null)); - - try { - startActivity(intent); - } catch (ActivityNotFoundException e) { - // Redirect to app info page instead, so that the user can manually grant the permission - String text = "Navigate to Permissions -> Files and media -> Allow all the time"; - Toast.makeText(MainMenuActivity.this, text, Toast.LENGTH_LONG).show(); - - intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); - intent.setData(Uri.fromParts("package", getPackageName(), null)); - startActivity(intent); - } - } else { - requestPermissions(permissionsList.toArray(new String[permissionsList.size()]), - REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS); - - Log.i("MainMenuActivity", "User accepted request for external storage permissions."); - } + Log.i("MainMenuActivity", "User accepted request for external storage permissions."); } } }); @@ -226,13 +189,4 @@ public final class MainMenuActivity extends PreferenceActivity checkRuntimePermissions(); } - - @Override - public void onTopResumedActivityChanged(boolean onTop) { - if(onTop && checkManageExternalStoragePermission) { - checkPermissions = false; - checkManageExternalStoragePermission = false; - checkRuntimePermissions(); - } - } }