ANDROID: Use SAF if a directory is not writeable

This commit is contained in:
antoniou 2021-08-01 15:53:12 +03:00
parent e79e58e077
commit 4ac31d5481
5 changed files with 72 additions and 7 deletions

View file

@ -68,7 +68,14 @@ bool POSIXFilesystemNode::isReadable() const {
}
bool POSIXFilesystemNode::isWritable() const {
return access(_path.c_str(), W_OK) == 0;
bool retVal = access(_path.c_str(), W_OK) == 0;
#if defined(ANDROID_PLAIN_PORT)
if (!retVal) {
// Update return value if going through Android's SAF grants the permission
retVal = JNI::isDirectoryWritableWithSAF(_path);
}
#endif // ANDROID_PLAIN_PORT
return retVal;
}
void POSIXFilesystemNode::setFlags() {

View file

@ -91,6 +91,7 @@ jmethodID JNI::_MID_deinitSurface = 0;
jmethodID JNI::_MID_createDirectoryWithSAF = 0;
jmethodID JNI::_MID_createFileWithSAF = 0;
jmethodID JNI::_MID_closeFileWithSAF = 0;
jmethodID JNI::_MID_isDirectoryWritableWithSAF = 0;
jmethodID JNI::_MID_EGL10_eglSwapBuffers = 0;
@ -561,6 +562,7 @@ void JNI::create(JNIEnv *env, jobject self, jobject asset_manager,
FIND_METHOD(, createDirectoryWithSAF, "(Ljava/lang/String;)Z");
FIND_METHOD(, createFileWithSAF, "(Ljava/lang/String;)Ljava/lang/String;");
FIND_METHOD(, closeFileWithSAF, "(Ljava/lang/String;)V");
FIND_METHOD(, isDirectoryWritableWithSAF, "(Ljava/lang/String;)Z");
_jobj_egl = env->NewGlobalRef(egl);
_jobj_egl_display = env->NewGlobalRef(egl_display);
@ -814,7 +816,6 @@ Common::U32String JNI::createFileWithSAF(const Common::String &filePath) {
Common::U32String hackyFilenameStr = convertFromJString(env, hackyFilenameJSTR);
//LOGD("JNI - _MID_createFileWithSAF returned %s", hackyFilenameStr.c_str());
env->DeleteLocalRef(hackyFilenameJSTR);
return hackyFilenameStr;
@ -836,4 +837,22 @@ void JNI::closeFileWithSAF(const Common::String &hackyFilename) {
}
bool JNI::isDirectoryWritableWithSAF(const Common::String &dirPath) {
JNIEnv *env = JNI::getEnv();
jstring javaDirPath = env->NewStringUTF(dirPath.c_str());
bool isWritable = env->CallBooleanMethod(_jobj, _MID_isDirectoryWritableWithSAF, javaDirPath);
if (env->ExceptionCheck()) {
LOGE("JNI - Failed to check if directory is writable SAF enhanced method");
env->ExceptionDescribe();
env->ExceptionClear();
isWritable = false;
}
return isWritable;
}
#endif

View file

@ -87,6 +87,7 @@ public:
static bool createDirectoryWithSAF(const Common::String &dirPath);
static Common::U32String createFileWithSAF(const Common::String &filePath);
static void closeFileWithSAF(const Common::String &hackyFilename);
static bool isDirectoryWritableWithSAF(const Common::String &dirPath);
private:
static JavaVM *_vm;
@ -119,6 +120,7 @@ private:
static jmethodID _MID_createDirectoryWithSAF;
static jmethodID _MID_createFileWithSAF;
static jmethodID _MID_closeFileWithSAF;
static jmethodID _MID_isDirectoryWritableWithSAF;
static jmethodID _MID_EGL10_eglSwapBuffers;

View file

@ -74,6 +74,7 @@ public abstract class ScummVM implements SurfaceHolder.Callback, Runnable {
abstract protected boolean createDirectoryWithSAF(String dirPath);
abstract protected String createFileWithSAF(String filePath);
abstract protected void closeFileWithSAF(String hackyFilename);
abstract protected boolean isDirectoryWritableWithSAF(String dirPath);
public ScummVM(AssetManager asset_manager, SurfaceHolder holder, final MyScummVMDestroyedCallback scummVMDestroyedCallback) {
_asset_manager = asset_manager;

View file

@ -417,19 +417,19 @@ public class ScummVMActivity extends Activity implements OnKeyboardVisibilityLis
// TODO - "Swipe" behavior does not seem to work currently. Should we support it?
public void swipeLeft() {
//Log.d(ScummVM.LOG_TAG, "SHOW KEYBOARD - 001 - swipeLeft");
// Log.d(ScummVM.LOG_TAG, "SHOW KEYBOARD - 001 - swipeLeft");
}
public void swipeRight() {
//Log.d(ScummVM.LOG_TAG, "SHOW KEYBOARD - 001 - swipeRight" );
// Log.d(ScummVM.LOG_TAG, "SHOW KEYBOARD - 001 - swipeRight" );
}
public void swipeDown() {
//Log.d(ScummVM.LOG_TAG, "SHOW KEYBOARD - 001 - swipeDown" );
// Log.d(ScummVM.LOG_TAG, "SHOW KEYBOARD - 001 - swipeDown" );
}
public void swipeUp() {
//Log.d(ScummVM.LOG_TAG, "SHOW KEYBOARD - 001 - swipeUp ");
// Log.d(ScummVM.LOG_TAG, "SHOW KEYBOARD - 001 - swipeUp ");
}
public void onKey(int key, int[] keysAround) {
// Log.d(ScummVM.LOG_TAG, "SHOW KEYBOARD - 001 - onKey key: " + key );
@ -583,7 +583,6 @@ public class ScummVMActivity extends Activity implements OnKeyboardVisibilityLis
}
};
private class MyScummVM extends ScummVM {
public MyScummVM(SurfaceHolder holder, final MyScummVMDestroyedCallback destroyedCallback) {
@ -793,6 +792,36 @@ public class ScummVMActivity extends Activity implements OnKeyboardVisibilityLis
return retRes[0];
}
// This is a simplified version of createDirectoryWithSAF
// TODO Maybe we could merge isDirectoryWritableWithSAF() with createDirectoryWithSAF() using an extra argument parameter
@Override
protected boolean isDirectoryWritableWithSAF(String dirPath) {
final boolean[] retRes = {false};
Log.d(ScummVM.LOG_TAG, "Check if folder writable: " + dirPath);
File folderToCheck = new File (dirPath);
if (folderToCheck.canWrite()) {
Log.d(ScummVM.LOG_TAG, "This path has write permission!" + dirPath);
} else {
Log.d(ScummVM.LOG_TAG, "Trying to get write access with SAF");
if (getStorageAccessFrameworkTreeUri() == null) {
requestStorageAccessFramework(dirPath);
} else {
Log.d(ScummVM.LOG_TAG, "Already requested Storage Access (Storage Access Framework) in the past (share prefs saved)!");
}
}
if (canWriteFile(folderToCheck, true)) {
Log.d(ScummVM.LOG_TAG, "(post SAF request) Writing is possible for this directory node");
retRes[0] = true;
} else {
Log.d(ScummVM.LOG_TAG, "(post SAF request) Error - writing is still not possible for this directory node");
}
return retRes[0];
}
@Override
protected String createFileWithSAF(String filePath) {
final String[] retResStr = {""};
@ -2158,6 +2187,7 @@ public class ScummVMActivity extends Activity implements OnKeyboardVisibilityLis
_scummvm.displayMessageOnOSD(getString(R.string.saf_request_prompt) + dirPathSample);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
// Directory picker
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION
@ -2298,14 +2328,20 @@ public class ScummVMActivity extends Activity implements OnKeyboardVisibilityLis
} catch (Exception ignored) {
originalDirectory = true;
}
Uri treeUri;
if ((treeUri = getStorageAccessFrameworkTreeUri()) == null) {
return null;
}
DocumentFile dof = DocumentFile.fromTreeUri(getApplicationContext(), treeUri);
if (originalDirectory) {
return dof;
}
// Important note: We cannot assume that anything sent here is a relative path on top of the *ONLY* SAF "root" path
// since the the user could select another SD Card (from multiple inserted or replaces the current one and inserts another)
// TODO Can we translate our path string "/storage/XXXX-XXXXX/folder/doc.ext' a content URI? or a document URI?
String[] parts = relPath.split("\\/");
for (int i = 0; i < parts.length; i++) {
DocumentFile nextDof = dof.findFile(parts[i]);