Skip to content

Commit

Permalink
Use file ops for file:// and document ops for content:// (#1495)
Browse files Browse the repository at this point in the history
  • Loading branch information
stuartmscott committed Nov 3, 2020
1 parent a5df8f1 commit 4566b50
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 16 deletions.
120 changes: 116 additions & 4 deletions internal/driver/gomobile/android.c
@@ -1,10 +1,12 @@
// +build android

#include <android/log.h>
#include <dirent.h>
#include <jni.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>

#define LOG_FATAL(...) __android_log_print(ANDROID_LOG_FATAL, "Fyne", __VA_ARGS__)

Expand Down Expand Up @@ -155,11 +157,26 @@ void closeStream(uintptr_t jni_env, uintptr_t ctx, void* stream) {
(*env)->DeleteGlobalRef(env, stream);
}

bool uriCanList(uintptr_t jni_env, uintptr_t ctx, char* uriCstr) {
bool hasPrefix(char* string, char* prefix) {
size_t lp = strlen(prefix);
size_t ls = strlen(string);
if (ls < lp) {
return false;
}
return memcmp(prefix, string, lp) == 0;
}

bool canListContentURI(uintptr_t jni_env, uintptr_t ctx, char* uriCstr) {
JNIEnv *env = (JNIEnv*)jni_env;
jobject resolver = getContentResolver(jni_env, ctx);
jstring uriStr = (*env)->NewStringUTF(env, uriCstr);
jobject uri = parseURI(jni_env, ctx, uriCstr);
jthrowable loadErr = (*env)->ExceptionOccurred(env);

if (loadErr != NULL) {
(*env)->ExceptionClear(env);
return false;
}

jclass contractClass = find_class(env, "android/provider/DocumentsContract");
if (contractClass == NULL) { // API 19
Expand All @@ -186,11 +203,43 @@ bool uriCanList(uintptr_t jni_env, uintptr_t ctx, char* uriCstr) {
return strcmp(str, "vnd.android.document/directory") == 0;
}

char* uriList(uintptr_t jni_env, uintptr_t ctx, char* uriCstr) {
bool canListFileURI(char* uriCstr) {
// Get file path from URI
size_t length = strlen(uriCstr)-7;// -7 for 'file://'
char* path = malloc(sizeof(char)*(length+1));// +1 for '\0'
memcpy(path, &uriCstr[7], length);
path[length] = '\0';

// Stat path to determine if it points to a directory
struct stat statbuf;
int result = stat(path, &statbuf);

free(path);

return (result == 0) && S_ISDIR(statbuf.st_mode);
}

bool canListURI(uintptr_t jni_env, uintptr_t ctx, char* uriCstr) {
if (hasPrefix(uriCstr, "file://")) {
return canListFileURI(uriCstr);
} else if (hasPrefix(uriCstr, "content://")) {
return canListContentURI(jni_env, ctx, uriCstr);
}
LOG_FATAL("Unrecognized scheme: %s", uriCstr);
return false;
}

char* listContentURI(uintptr_t jni_env, uintptr_t ctx, char* uriCstr) {
JNIEnv *env = (JNIEnv*)jni_env;
jobject resolver = getContentResolver(jni_env, ctx);
jstring uriStr = (*env)->NewStringUTF(env, uriCstr);
jobject uri = parseURI(jni_env, ctx, uriCstr);
jthrowable loadErr = (*env)->ExceptionOccurred(env);

if (loadErr != NULL) {
(*env)->ExceptionClear(env);
return "";
}

jclass contractClass = find_class(env, "android/provider/DocumentsContract");
if (contractClass == NULL) { // API 19
Expand Down Expand Up @@ -237,11 +286,74 @@ char* uriList(uintptr_t jni_env, uintptr_t ctx, char* uriCstr) {
if (old != NULL) {
strcpy(ret, old);
free(old);
} else {
ret[0] = '\0';
}
strcat(ret, uid);
strcat(ret, "|");
}

ret[len-1] = '\0';
if (ret != NULL) {
ret[len-1] = '\0';
}
return ret;
}

char* listFileURI(char* uriCstr) {

size_t uriLength = strlen(uriCstr);

// Get file path from URI
size_t length = uriLength-7;// -7 for 'file://'
char* path = malloc(sizeof(char)*(length+1));// +1 for '\0'
memcpy(path, &uriCstr[7], length);
path[length] = '\0';

char *ret = NULL;
DIR *dfd;
if ((dfd = opendir(path)) != NULL) {
struct dirent *dp;
int len = 0;
while ((dp = readdir(dfd)) != NULL) {
if (strcmp(dp->d_name, ".") == 0) {
// Ignore current directory
continue;
}
if (strcmp(dp->d_name, "..") == 0) {
// Ignore parent directory
continue;
}
// append
char *old = ret;
len = len + uriLength + 1 /* / */ + strlen(dp->d_name) + 1 /* | */;
ret = malloc(sizeof(char)*(len+1));
if (old != NULL) {
strcpy(ret, old);
free(old);
} else {
ret[0] = '\0';
}
strcat(ret, uriCstr);
strcat(ret, "/");
strcat(ret, dp->d_name);
strcat(ret, "|");
}
if (ret != NULL) {
ret[len-1] = '\0';
}
}

free(path);

return ret;
}
}

char* listURI(uintptr_t jni_env, uintptr_t ctx, char* uriCstr) {
if (hasPrefix(uriCstr, "file://")) {
return listFileURI(uriCstr);
} else if (hasPrefix(uriCstr, "content://")) {
return listContentURI(jni_env, ctx, uriCstr);
}
LOG_FATAL("Unrecognized scheme: %s", uriCstr);
return "";
}
4 changes: 2 additions & 2 deletions internal/driver/gomobile/folder.go
Expand Up @@ -11,11 +11,11 @@ type lister struct {
}

func (l *lister) List() ([]fyne.URI, error) {
return uriList(l)
return listURI(l)
}

func (d *mobileDriver) ListerForURI(uri fyne.URI) (fyne.ListableURI, error) {
if !uriCanList(uri) {
if !canListURI(uri) {
return nil, fmt.Errorf("specified URI is not listable")
}

Expand Down
12 changes: 6 additions & 6 deletions internal/driver/gomobile/folder_android.go
Expand Up @@ -8,8 +8,8 @@ package gomobile
#include <stdbool.h>
#include <stdlib.h>
bool uriCanList(uintptr_t jni_env, uintptr_t ctx, char* uriCstr);
char *uriList(uintptr_t jni_env, uintptr_t ctx, char* uriCstr);
bool canListURI(uintptr_t jni_env, uintptr_t ctx, char* uriCstr);
char *listURI(uintptr_t jni_env, uintptr_t ctx, char* uriCstr);
*/
import "C"
import (
Expand All @@ -22,26 +22,26 @@ import (
"fyne.io/fyne/storage"
)

func uriCanList(uri fyne.URI) bool {
func canListURI(uri fyne.URI) bool {
uriStr := C.CString(uri.String())
defer C.free(unsafe.Pointer(uriStr))
listable := false

app.RunOnJVM(func(_, env, ctx uintptr) error {
listable = bool(C.uriCanList(C.uintptr_t(env), C.uintptr_t(ctx), uriStr))
listable = bool(C.canListURI(C.uintptr_t(env), C.uintptr_t(ctx), uriStr))
return nil
})

return listable
}

func uriList(uri fyne.URI) ([]fyne.URI, error) {
func listURI(uri fyne.URI) ([]fyne.URI, error) {
uriStr := C.CString(uri.String())
defer C.free(unsafe.Pointer(uriStr))

var str *C.char
app.RunOnJVM(func(_, env, ctx uintptr) error {
str = C.uriList(C.uintptr_t(env), C.uintptr_t(ctx), uriStr)
str = C.listURI(C.uintptr_t(env), C.uintptr_t(ctx), uriStr)
return nil
})

Expand Down
4 changes: 2 additions & 2 deletions internal/driver/gomobile/folder_desktop.go
Expand Up @@ -4,10 +4,10 @@ package gomobile

import "fyne.io/fyne"

func uriCanList(fyne.URI) bool {
func canListURI(fyne.URI) bool {
return false
}

func uriList(fyne.URI) ([]fyne.URI, error) {
func listURI(fyne.URI) ([]fyne.URI, error) {
return nil, nil
}
4 changes: 2 additions & 2 deletions internal/driver/gomobile/folder_ios.go
Expand Up @@ -21,14 +21,14 @@ import (
"fyne.io/fyne/storage"
)

func uriCanList(uri fyne.URI) bool {
func canListURI(uri fyne.URI) bool {
uriStr := C.CString(uri.String())
defer C.free(unsafe.Pointer(uriStr))

return bool(C.iosCanList(uriStr))
}

func uriList(uri fyne.URI) ([]fyne.URI, error) {
func listURI(uri fyne.URI) ([]fyne.URI, error) {
uriStr := C.CString(uri.String())
defer C.free(unsafe.Pointer(uriStr))

Expand Down

0 comments on commit 4566b50

Please sign in to comment.