[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
This commit is contained in:
Braden Farmer 2022-02-15 08:11:39 -07:00 committed by GitHub
parent 5d3654ddbb
commit 5bacf1e9be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 112 additions and 58 deletions

View File

@ -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",

View File

@ -175,6 +175,9 @@ struct android_app
jmethodID downloadCore;
jmethodID deleteCore;
jmethodID getVolumeCount;
jmethodID getVolumePath;
struct
{
unsigned width, height;

View File

@ -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 \

View File

@ -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<StorageVolume> 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<StorageVolume> 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);

View File

@ -9,10 +9,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.MANAGE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.VIBRATE" />

View File

@ -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<String> permissionsNeeded = new ArrayList<String>();
final List<String> permissionsList = new ArrayList<String>();
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();
}
}
}