Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use file ops for file:// and document ops for content:// #1495

Merged
merged 3 commits into from Nov 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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