mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-26 23:10:38 +00:00
Use hacky methods to retrieve SD card directory if available.
This will disappear in future Android versions but can be useful for older devices. Fixes (or at least tries to) #10199, at least for some devices. Might help #13827 ? Tested on Pocophone F1.
This commit is contained in:
parent
5929c78307
commit
1c753e4a22
@ -40,6 +40,8 @@ void System_SendMessage(const char *command, const char *parameter);
|
||||
PermissionStatus System_GetPermissionStatus(SystemPermission permission);
|
||||
void System_AskForPermission(SystemPermission permission);
|
||||
|
||||
std::vector<std::string> System_GetExternalStorageDirs();
|
||||
|
||||
// This will get muddy with multi-screen support :/ But this will always be the type of the main device.
|
||||
enum SystemDeviceType {
|
||||
DEVICE_TYPE_MOBILE = 0, // phones and pads
|
||||
@ -55,14 +57,16 @@ enum SystemProperty {
|
||||
SYSPROP_CLIPBOARD_TEXT,
|
||||
SYSPROP_GPUDRIVER_VERSION,
|
||||
|
||||
// Separate SD cards or similar.
|
||||
// Need hacky solutions to get at this.
|
||||
SYSPROP_HAS_ADDITIONAL_STORAGE,
|
||||
SYSPROP_ADDITIONAL_STORAGE_DIRS,
|
||||
|
||||
SYSPROP_HAS_FILE_BROWSER,
|
||||
SYSPROP_HAS_FOLDER_BROWSER,
|
||||
SYSPROP_HAS_IMAGE_BROWSER,
|
||||
SYSPROP_HAS_BACK_BUTTON,
|
||||
|
||||
// Legacy on Android 29+.
|
||||
SYSPROP_HAS_EXTERNAL_STORAGE,
|
||||
|
||||
// Available as Int:
|
||||
SYSPROP_SYSTEMVERSION,
|
||||
SYSPROP_DISPLAY_XRES,
|
||||
@ -98,6 +102,7 @@ enum SystemProperty {
|
||||
};
|
||||
|
||||
std::string System_GetProperty(SystemProperty prop);
|
||||
std::vector<std::string> System_GetPropertyStringVec(SystemProperty prop);
|
||||
int System_GetPropertyInt(SystemProperty prop);
|
||||
float System_GetPropertyFloat(SystemProperty prop);
|
||||
bool System_GetPropertyBool(SystemProperty prop);
|
||||
|
@ -149,6 +149,13 @@ std::string System_GetProperty(SystemProperty prop) {
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> System_GetPropertyStringVec(SystemProperty prop) {
|
||||
switch (prop) {
|
||||
default:
|
||||
return std::vector<std::string>();
|
||||
}
|
||||
}
|
||||
|
||||
int System_GetPropertyInt(SystemProperty prop) {
|
||||
switch (prop) {
|
||||
#if defined(SDL)
|
||||
|
@ -325,6 +325,13 @@ std::string System_GetProperty(SystemProperty prop) {
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> System_GetPropertyStringVec(SystemProperty prop) {
|
||||
switch (prop) {
|
||||
default:
|
||||
return std::vector<std::string>();
|
||||
}
|
||||
}
|
||||
|
||||
int System_GetPropertyInt(SystemProperty prop) {
|
||||
switch (prop) {
|
||||
case SYSPROP_AUDIO_SAMPLE_RATE:
|
||||
|
@ -546,8 +546,17 @@ UI::EventReturn GameBrowser::BrowseClick(UI::EventParams &e) {
|
||||
}
|
||||
|
||||
UI::EventReturn GameBrowser::StorageClick(UI::EventParams &e) {
|
||||
// TODO: Get the SD card directory on Android.
|
||||
SetPath("");
|
||||
std::vector<std::string> storageDirs = System_GetPropertyStringVec(SYSPROP_ADDITIONAL_STORAGE_DIRS);
|
||||
if (storageDirs.empty()) {
|
||||
// Shouldn't happen - this button shouldn't be clickable.
|
||||
return UI::EVENT_DONE;
|
||||
}
|
||||
if (storageDirs.size() == 1) {
|
||||
SetPath(storageDirs[0]);
|
||||
} else {
|
||||
// TODO: We should popup a dialog letting the user choose one.
|
||||
SetPath(storageDirs[0]);
|
||||
}
|
||||
return UI::EVENT_DONE;
|
||||
}
|
||||
|
||||
@ -672,7 +681,7 @@ void GameBrowser::Refresh() {
|
||||
} else {
|
||||
topBar->Add(new Choice(mm->T("Home"), new LayoutParams(WRAP_CONTENT, 64.0f)))->OnClick.Handle(this, &GameBrowser::HomeClick);
|
||||
}
|
||||
if (System_GetPropertyBool(SYSPROP_HAS_EXTERNAL_STORAGE)) {
|
||||
if (System_GetPropertyBool(SYSPROP_HAS_ADDITIONAL_STORAGE)) {
|
||||
topBar->Add(new Choice(ImageID("I_SDCARD"), new LayoutParams(WRAP_CONTENT, 64.0f)))->OnClick.Handle(this, &GameBrowser::StorageClick);
|
||||
}
|
||||
topBar->Add(new Choice(ImageID("I_HOME"), new LayoutParams(WRAP_CONTENT, 64.0f)))->OnClick.Handle(this, &GameBrowser::HomeClick);
|
||||
|
@ -356,6 +356,13 @@ std::string System_GetProperty(SystemProperty prop) {
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> System_GetPropertyStringVec(SystemProperty prop) {
|
||||
switch (prop) {
|
||||
default:
|
||||
return std::vector<std::string>();
|
||||
}
|
||||
}
|
||||
|
||||
int System_GetPropertyInt(SystemProperty prop) {
|
||||
switch (prop) {
|
||||
case SYSPROP_AUDIO_SAMPLE_RATE:
|
||||
|
@ -216,6 +216,13 @@ std::string System_GetProperty(SystemProperty prop) {
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> System_GetPropertyStringVec(SystemProperty prop) {
|
||||
switch (prop) {
|
||||
default:
|
||||
return std::vector<std::string>();
|
||||
}
|
||||
}
|
||||
|
||||
// Ugly!
|
||||
extern WindowsAudioBackend *winAudioBackend;
|
||||
|
||||
|
@ -13,6 +13,8 @@
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#include <jni.h>
|
||||
#include <android/native_window_jni.h>
|
||||
@ -122,6 +124,8 @@ std::string langRegion;
|
||||
std::string mogaVersion;
|
||||
std::string boardName;
|
||||
|
||||
std::vector<std::string> g_additionalStorageDirs;
|
||||
|
||||
static float left_joystick_x_async;
|
||||
static float left_joystick_y_async;
|
||||
static float right_joystick_x_async;
|
||||
@ -364,6 +368,15 @@ std::string System_GetProperty(SystemProperty prop) {
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> System_GetPropertyStringVec(SystemProperty prop) {
|
||||
switch (prop) {
|
||||
case SYSPROP_ADDITIONAL_STORAGE_DIRS:
|
||||
return g_additionalStorageDirs;
|
||||
default:
|
||||
return std::vector<std::string>();
|
||||
}
|
||||
}
|
||||
|
||||
int System_GetPropertyInt(SystemProperty prop) {
|
||||
switch (prop) {
|
||||
case SYSPROP_SYSTEMVERSION:
|
||||
@ -410,6 +423,8 @@ bool System_GetPropertyBool(SystemProperty prop) {
|
||||
return androidVersion >= 23; // 6.0 Marshmallow introduced run time permissions.
|
||||
case SYSPROP_SUPPORTS_SUSTAINED_PERF_MODE:
|
||||
return sustainedPerfSupported; // 7.0 introduced sustained performance mode as an optional feature.
|
||||
case SYSPROP_HAS_ADDITIONAL_STORAGE:
|
||||
return !g_additionalStorageDirs.empty();
|
||||
case SYSPROP_HAS_BACK_BUTTON:
|
||||
return true;
|
||||
case SYSPROP_HAS_IMAGE_BROWSER:
|
||||
@ -530,16 +545,19 @@ static void parse_args(std::vector<std::string> &args, const std::string value)
|
||||
}
|
||||
}
|
||||
|
||||
// Need to use raw Android logging before NativeInit.
|
||||
#define EARLY_LOG(...) __android_log_print(ANDROID_LOG_INFO, "PPSSPP", __VA_ARGS__)
|
||||
|
||||
extern "C" void Java_org_ppsspp_ppsspp_NativeApp_init
|
||||
(JNIEnv *env, jclass, jstring jmodel, jint jdeviceType, jstring jlangRegion, jstring japkpath,
|
||||
jstring jdataDir, jstring jexternalDir, jstring jlibraryDir, jstring jcacheDir, jstring jshortcutParam,
|
||||
jstring jdataDir, jstring jexternalStorageDir, jstring jadditionalStorageDirs, jstring jlibraryDir, jstring jcacheDir, jstring jshortcutParam,
|
||||
jint jAndroidVersion, jstring jboard) {
|
||||
setCurrentThreadName("androidInit");
|
||||
|
||||
// Makes sure we get early permission grants.
|
||||
ProcessFrameCommands(env);
|
||||
|
||||
INFO_LOG(SYSTEM, "NativeApp.init() -- begin");
|
||||
EARLY_LOG("NativeApp.init() -- begin");
|
||||
PROFILE_INIT();
|
||||
|
||||
renderer_inited = false;
|
||||
@ -559,9 +577,18 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_init
|
||||
systemName = GetJavaString(env, jmodel);
|
||||
langRegion = GetJavaString(env, jlangRegion);
|
||||
|
||||
INFO_LOG(SYSTEM, "NativeApp.init(): device name: '%s'", systemName.c_str());
|
||||
EARLY_LOG("NativeApp.init(): device name: '%s'", systemName.c_str());
|
||||
|
||||
std::string externalStorageDir = GetJavaString(env, jexternalStorageDir);
|
||||
std::string additionalStorageDirsString = GetJavaString(env, jadditionalStorageDirs);
|
||||
|
||||
if (!additionalStorageDirsString.empty()) {
|
||||
SplitString(additionalStorageDirsString, ':', g_additionalStorageDirs);
|
||||
for (auto &str : g_additionalStorageDirs) {
|
||||
EARLY_LOG("Additional storage: %s", str.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
std::string externalDir = GetJavaString(env, jexternalDir);
|
||||
std::string user_data_path = GetJavaString(env, jdataDir);
|
||||
if (user_data_path.size() > 0)
|
||||
user_data_path += "/";
|
||||
@ -570,8 +597,8 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_init
|
||||
std::string cacheDir = GetJavaString(env, jcacheDir);
|
||||
std::string buildBoard = GetJavaString(env, jboard);
|
||||
boardName = buildBoard;
|
||||
INFO_LOG(SYSTEM, "NativeApp.init(): External storage path: %s", externalDir.c_str());
|
||||
INFO_LOG(SYSTEM, "NativeApp.init(): Launch shortcut parameter: %s", shortcut_param.c_str());
|
||||
EARLY_LOG("NativeApp.init(): External storage path: %s", externalStorageDir.c_str());
|
||||
EARLY_LOG("NativeApp.init(): Launch shortcut parameter: %s", shortcut_param.c_str());
|
||||
|
||||
std::string app_name;
|
||||
std::string app_nice_name;
|
||||
@ -600,7 +627,9 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_init
|
||||
}
|
||||
}
|
||||
|
||||
NativeInit((int)args.size(), &args[0], user_data_path.c_str(), externalDir.c_str(), cacheDir.c_str());
|
||||
NativeInit((int)args.size(), &args[0], user_data_path.c_str(), externalStorageDir.c_str(), cacheDir.c_str());
|
||||
|
||||
// No need to use EARLY_LOG anymore.
|
||||
|
||||
retry:
|
||||
// Now that we've loaded config, set javaGL.
|
||||
|
@ -46,6 +46,7 @@ import android.widget.Toast;
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@ -241,6 +242,91 @@ public abstract class NativeActivity extends Activity {
|
||||
this.shortcutParam = ((shortcutParam == null) ? "" : shortcutParam);
|
||||
}
|
||||
|
||||
// Unofficial hacks to get a list of SD cards that are not the main "external storage".
|
||||
private static List<String> getSdCardPaths(final Context context) {
|
||||
// Q is the last version that will support normal file access.
|
||||
List<String> list = null;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
|
||||
Log.i(TAG, "getSdCardPaths: Trying KitKat method");
|
||||
list = getSdCardPaths19(context);
|
||||
}
|
||||
|
||||
if (list == null) {
|
||||
Log.i(TAG, "getSdCardPaths: Attempting fallback");
|
||||
// Try another method.
|
||||
String removableStoragePath;
|
||||
list = new ArrayList<String>();
|
||||
File fileList[] = new File("/storage/").listFiles();
|
||||
for (File file : fileList) {
|
||||
if (!file.getAbsolutePath().equalsIgnoreCase(Environment.getExternalStorageDirectory().getAbsolutePath()) && file.isDirectory() && file.canRead()) {
|
||||
list.add(file.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: On older devices, try System.getenv(“EXTERNAL_SDCARD_STORAGE”)
|
||||
|
||||
if (list == null) {
|
||||
return new ArrayList<String>();
|
||||
} else {
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* returns a list of all available sd cards paths, or null if not found.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.KITKAT)
|
||||
private static List<String> getSdCardPaths19(final Context context)
|
||||
{
|
||||
final File[] externalCacheDirs = context.getExternalCacheDirs();
|
||||
if (externalCacheDirs == null || externalCacheDirs.length==0)
|
||||
return null;
|
||||
if (externalCacheDirs.length == 1) {
|
||||
if (externalCacheDirs[0] == null)
|
||||
return null;
|
||||
final String storageState = Environment.getStorageState(externalCacheDirs[0]);
|
||||
if (!Environment.MEDIA_MOUNTED.equals(storageState))
|
||||
return null;
|
||||
if (Environment.isExternalStorageEmulated())
|
||||
return null;
|
||||
}
|
||||
final List<String> result = new ArrayList<>();
|
||||
if (externalCacheDirs.length == 1)
|
||||
result.add(getRootOfInnerSdCardFolder(externalCacheDirs[0]));
|
||||
for (int i = 1; i < externalCacheDirs.length; ++i)
|
||||
{
|
||||
final File file=externalCacheDirs[i];
|
||||
if (file == null)
|
||||
continue;
|
||||
final String storageState=Environment.getStorageState(file);
|
||||
if (Environment.MEDIA_MOUNTED.equals(storageState))
|
||||
result.add(getRootOfInnerSdCardFolder(externalCacheDirs[i]));
|
||||
}
|
||||
if (result.isEmpty())
|
||||
return null;
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Given any file/folder inside an sd card, this will return the path of the sd card */
|
||||
private static String getRootOfInnerSdCardFolder(File file)
|
||||
{
|
||||
if (file == null)
|
||||
return null;
|
||||
final long totalSpace = file.getTotalSpace();
|
||||
while (true) {
|
||||
final File parentFile = file.getParentFile();
|
||||
if (parentFile == null || !parentFile.canRead()) {
|
||||
break;
|
||||
}
|
||||
if (parentFile.getTotalSpace() != totalSpace) {
|
||||
break;
|
||||
}
|
||||
file = parentFile;
|
||||
}
|
||||
return file.getAbsolutePath();
|
||||
}
|
||||
|
||||
public void Initialize() {
|
||||
// Initialize audio classes. Do this here since detectOptimalAudioSettings()
|
||||
// needs audioManager
|
||||
@ -292,9 +378,20 @@ public abstract class NativeActivity extends Activity {
|
||||
isXperiaPlay = IsXperiaPlay();
|
||||
|
||||
String libraryDir = getApplicationLibraryDir(appInfo);
|
||||
File sdcard = Environment.getExternalStorageDirectory();
|
||||
|
||||
String externalStorageDir = sdcard.getAbsolutePath();
|
||||
String extStorageState = Environment.getExternalStorageState();
|
||||
String extStorageDir = Environment.getExternalStorageDirectory().getAbsolutePath();
|
||||
|
||||
Log.i(TAG, "Ext storage: " + extStorageState + " " + extStorageDir);
|
||||
|
||||
List<String> sdCards = getSdCardPaths(this);
|
||||
for (String sdcard: sdCards) {
|
||||
Log.i(TAG, "SD card: " + sdcard);
|
||||
}
|
||||
String additionalStorageDirs = String.join(":", sdCards);
|
||||
|
||||
Log.i(TAG, "End of storage paths");
|
||||
|
||||
File filesDir = this.getFilesDir();
|
||||
String dataDir = null;
|
||||
if (filesDir != null) {
|
||||
@ -310,7 +407,7 @@ public abstract class NativeActivity extends Activity {
|
||||
overrideShortcutParam = null;
|
||||
|
||||
NativeApp.audioConfig(optimalFramesPerBuffer, optimalSampleRate);
|
||||
NativeApp.init(model, deviceType, languageRegion, apkFilePath, dataDir, externalStorageDir, libraryDir, cacheDir, shortcut, Build.VERSION.SDK_INT, Build.BOARD);
|
||||
NativeApp.init(model, deviceType, languageRegion, apkFilePath, dataDir, extStorageDir, additionalStorageDirs, libraryDir, cacheDir, shortcut, Build.VERSION.SDK_INT, Build.BOARD);
|
||||
|
||||
// Allow C++ to tell us to use JavaGL or not.
|
||||
javaGL = "true".equalsIgnoreCase(NativeApp.queryConfig("androidJavaGL"));
|
||||
|
@ -12,7 +12,7 @@ public class NativeApp {
|
||||
public static final int DEVICE_TYPE_TV = 1;
|
||||
public static final int DEVICE_TYPE_DESKTOP = 2;
|
||||
|
||||
public static native void init(String model, int deviceType, String languageRegion, String apkPath, String dataDir, String externalDir, String libraryDir, String cacheDir, String shortcutParam, int androidVersion, String board);
|
||||
public static native void init(String model, int deviceType, String languageRegion, String apkPath, String dataDir, String externalStorageDir, String additionalStorageDirs, String libraryDir, String cacheDir, String shortcutParam, int androidVersion, String board);
|
||||
public static native void audioInit();
|
||||
public static native void audioShutdown();
|
||||
public static native void audioConfig(int optimalFramesPerBuffer, int optimalSampleRate);
|
||||
|
@ -81,15 +81,11 @@ void NativeRender(GraphicsContext *graphicsContext) { }
|
||||
void NativeResized() { }
|
||||
|
||||
std::string System_GetProperty(SystemProperty prop) { return ""; }
|
||||
int System_GetPropertyInt(SystemProperty prop) {
|
||||
return -1;
|
||||
}
|
||||
float System_GetPropertyFloat(SystemProperty prop) {
|
||||
return -1;
|
||||
}
|
||||
bool System_GetPropertyBool(SystemProperty prop) {
|
||||
return false;
|
||||
}
|
||||
std::vector<std::string> System_GetPropertyStringVec(SystemProperty prop) { return std::vector<std::string>(); }
|
||||
int System_GetPropertyInt(SystemProperty prop) { return -1; }
|
||||
float System_GetPropertyFloat(SystemProperty prop) { return -1.0f; }
|
||||
bool System_GetPropertyBool(SystemProperty prop) { return false; }
|
||||
|
||||
void System_SendMessage(const char *command, const char *parameter) {}
|
||||
void System_InputBoxGetString(const std::string &title, const std::string &defaultValue, std::function<void(bool, const std::string &)> cb) { cb(false, ""); }
|
||||
void System_AskForPermission(SystemPermission permission) {}
|
||||
|
@ -64,6 +64,13 @@ std::string System_GetProperty(SystemProperty prop) {
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> System_GetPropertyStringVec(SystemProperty prop) {
|
||||
switch (prop) {
|
||||
default:
|
||||
return std::vector<std::string>();
|
||||
}
|
||||
}
|
||||
|
||||
int System_GetPropertyInt(SystemProperty prop) {
|
||||
switch (prop) {
|
||||
case SYSPROP_AUDIO_SAMPLE_RATE:
|
||||
|
@ -905,6 +905,8 @@ float System_GetPropertyFloat(SystemProperty prop)
|
||||
}
|
||||
|
||||
std::string System_GetProperty(SystemProperty prop) { return ""; }
|
||||
std::vector<std::string> System_GetPropertyStringVec(SystemProperty prop) { return std::vector<std::string>(); }
|
||||
|
||||
void System_SendMessage(const char *command, const char *parameter) {}
|
||||
void NativeUpdate() {}
|
||||
void NativeRender(GraphicsContext *graphicsContext) {}
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#if defined(ANDROID)
|
||||
@ -58,7 +59,9 @@
|
||||
#include "unittest/TestVertexJit.h"
|
||||
#include "unittest/UnitTest.h"
|
||||
|
||||
|
||||
std::string System_GetProperty(SystemProperty prop) { return ""; }
|
||||
std::vector<std::string> System_GetPropertyStringVec(SystemProperty prop) { return std::vector<std::string>(); }
|
||||
int System_GetPropertyInt(SystemProperty prop) {
|
||||
return -1;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user