mirror of
https://github.com/libretro/RetroArch
synced 2025-03-30 16:20:27 +00:00
[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:
parent
5d3654ddbb
commit
5bacf1e9be
@ -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",
|
||||
|
@ -175,6 +175,9 @@ struct android_app
|
||||
jmethodID downloadCore;
|
||||
jmethodID deleteCore;
|
||||
|
||||
jmethodID getVolumeCount;
|
||||
jmethodID getVolumePath;
|
||||
|
||||
struct
|
||||
{
|
||||
unsigned width, height;
|
||||
|
@ -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 \
|
||||
|
@ -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);
|
||||
|
@ -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" />
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user