fix(android): content uri handling improvements (#9936)

This commit is contained in:
Osei Fortune
2022-06-19 13:46:50 -04:00
committed by Nathan Walker
parent 464ea18737
commit 99480c06db
15 changed files with 164 additions and 92 deletions

View File

@ -1,4 +1,4 @@
import { Page, EventData, Application, File } from '@nativescript/core';
import { Page, EventData, Application, File, Folder, knownFolders, path, getFileAccess, Utils } from '@nativescript/core';
let page: Page;
@ -71,3 +71,108 @@ function doWork(path: string) {
console.error(e);
}
}
export function pickFiles() {
if (!global.isAndroid) {
return;
}
const Intent = android.content.Intent;
Application.android.on('activityResult', (args) => {
if (args.requestCode === 1000) {
const intent: android.content.Intent = args.intent;
const clip = intent.getClipData();
if (clip) {
const count = clip.getItemCount();
for (let i = 0; i < count; i++) {
const item = clip.getItemAt(i);
readFile(item.getUri().toString());
}
} else {
readFile(intent.getData().toString());
}
}
});
const picker = new Intent(Intent.ACTION_OPEN_DOCUMENT);
picker.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
picker.addCategory(Intent.CATEGORY_OPENABLE);
picker.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
picker.setType('*/*');
Application.android.foregroundActivity.startActivityForResult(picker, 1000);
}
export function pickFile() {
if (!global.isAndroid) {
return;
}
Application.android.on('activityResult', (args) => {
if (args.requestCode === 1000) {
const file = args.intent.getData().toString();
//const file = File.fromPath(args.intent.getData().toString());
//console.log(file);
readFile(file);
}
});
const Intent = android.content.Intent;
const picker = new Intent(Intent.ACTION_OPEN_DOCUMENT);
picker.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
picker.addCategory(Intent.CATEGORY_OPENABLE);
picker.setType('*/*');
Application.android.foregroundActivity.startActivityForResult(picker, 1000);
}
function readFile(selected: string) {
let applicationContext = Utils.android.getApplicationContext();
const getOrSetHelper: org.nativescript.widgets.FileHelper = org.nativescript.widgets.FileHelper.fromString(applicationContext, selected);
console.log('==== CONTEXT START =========');
console.log('applicationContext', applicationContext);
console.log('selected: ', decodeURIComponent(selected));
console.log('getOrSetHelper: ', getOrSetHelper);
console.log('==== CONTEXT END =========');
console.log('==== READ START =========');
const fileRead = getFileAccess().read(selected);
console.log('# read: ', fileRead);
const readSync = getFileAccess().readSync(selected);
console.log('# readSync: ', readSync);
const readText = getFileAccess().readText(selected);
console.log('# readText: ', readText);
getFileAccess()
.readAsync(selected)
.then(
(readAsyncResolve) => {
console.log('# readAsynccResolve: ', readAsyncResolve);
},
(readAsyncRejected) => {
console.log('# readAsyncRejected: ', readAsyncRejected);
}
);
console.log('==== READ END =========');
saveFile(selected, readSync);
}
function saveFile(selected, readSync) {
const folder: Folder = <Folder>knownFolders.documents();
let newPath: string = path.join(folder.path, getFileNameFromContent(selected));
File.fromPath(newPath).writeSync(readSync, (error) => {
console.log(error);
});
let savedFile = folder.getFile(getFileNameFromContent(selected));
console.log('==== SAVE START =========');
console.log('# saved file: ', savedFile);
console.log('# save size: ', savedFile.size);
console.log('==== SAVE END =========');
}
function getFileNameFromContent(content: string) {
const file = getFileAccess().getFile(content);
return decodeURIComponent(file.name).split('/').pop().toLowerCase();
}

View File

@ -2,5 +2,7 @@
<StackLayout>
<Button text="Create Random" tap="createRandom" />
<Button text="Pick File" tap="pickFile" />
<Button text="Pick Multiple Files" tap="pickFiles" />
</StackLayout>
</Page>

View File

@ -1,6 +1,6 @@
{
"name": "@nativescript/core",
"version": "8.2.5",
"version": "8.2.6-alpha.0",
"description": "A JavaScript library providing an easy to use api for interacting with iOS and Android platform APIs.",
"main": "index",
"types": "index.d.ts",

View File

@ -34,7 +34,7 @@
</data>
<key>Info.plist</key>
<data>
xwF1juLfCl48xVJYOqCb8tG0WhQ=
0Rmccf4SQGyUrsxYOyGPzKsCAqU=
</data>
<key>Modules/module.modulemap</key>
<data>

View File

@ -48,44 +48,35 @@ public class FileHelper {
}
private static boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri
.getAuthority());
return false;
// return "com.android.externalstorage.documents".equals(uri
// .getAuthority());
}
private static @Nullable
Cursor getCursor(Context context, Uri uri) {
Cursor cursor = null;
String[] projections = {
MediaStore.MediaColumns.SIZE,
MediaStore.MediaColumns.DISPLAY_NAME,
MediaStore.MediaColumns.DATE_MODIFIED
};
try {
if (Build.VERSION.SDK_INT >= 19) {
if (DocumentsContract.isDocumentUri(context, uri)) {
String docId = DocumentsContract.getDocumentId(uri);
String[] split = docId.split(":");
String type = split[0];
Uri contentUri;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
} else {
contentUri = MediaStore.Files.getContentUri("external");
if (Build.VERSION.SDK_INT >= 29) {
if (!uri.toString().startsWith("content://com.android.providers.downloads.documents")) {
cursor = context.getContentResolver().query(
MediaStore.getMediaUri(context, uri), projections, null, null, null, null
);
}
}
String selection = "_id=?";
String[] selectionArgs = {split[1]};
cursor = context.getContentResolver().query(
contentUri, null, selection, selectionArgs, null, null
);
}
}
if (cursor == null) {
cursor = context.getContentResolver().query(uri, null, null, null, null);
cursor = context.getContentResolver().query(uri, projections, null, null, null);
}
} catch (Exception e) {
cursor = null;
} catch (Exception ignored) {
}
return cursor;
@ -120,7 +111,7 @@ public class FileHelper {
public static @Nullable
FileHelper fromString(Context context, String string) {
try {
return fromUri(context, Uri.parse(Uri.decode(string)), false);
return fromUri(context, Uri.parse(string));
} catch (Exception e) {
return null;
}
@ -158,20 +149,10 @@ public class FileHelper {
return null;
}
public static @Nullable
FileHelper fromUri(Context context, Uri uri) {
return fromUri(context, uri, true);
}
private static @Nullable
FileHelper fromUri(Context context, Uri contentUri, boolean parseUri) {
Uri uri;
if (parseUri) {
uri = Uri.parse(Uri.decode(contentUri.toString()));
} else {
uri = contentUri;
}
FileHelper fromUri(Context context, Uri contentUri) {
Uri uri = contentUri;
if (Build.VERSION.SDK_INT >= 19 && isExternalStorageDocument(uri)) {
File file = getFile(context, uri);
@ -338,10 +319,17 @@ public class FileHelper {
private byte[] readSyncInternal(Context context) throws Exception {
InputStream is = getInputStream(context, uri);
byte[] array = new byte[(int) size];
is.read(array);
Async.Http.RequestResult.ByteArrayOutputStream2 ret = new Async.Http.RequestResult.ByteArrayOutputStream2();
byte[] buff = new byte[4096];
int read;
while ((read = is.read(buff, 0, buff.length)) != -1) {
ret.write(buff, 0, read);
}
is.close();
return array;
return ret.buf();
}
public @Nullable
@ -517,40 +505,28 @@ public class FileHelper {
return file.delete();
}
return false;
}
} else {
if (DocumentsContract.isDocumentUri(context, uri)) {
if (Build.VERSION.SDK_INT >= 29) {
if (!uri.toString().startsWith("content://com.android.providers.downloads.documents")) {
return context.getContentResolver().delete(
MediaStore.getMediaUri(context, uri), null, null
) > 0;
}
if (DocumentsContract.isDocumentUri(context, uri)) {
String docId = DocumentsContract.getDocumentId(uri);
String[] split = docId.split(":");
String type = split[0];
Uri contentUri;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
} else {
contentUri = MediaStore.Files.getContentUri("external");
} else {
return DocumentsContract.deleteDocument(context.getContentResolver(), uri);
}
}
String selection = "_id=?";
String[] selectionArgs = {split[1]};
return context.getContentResolver().delete(
contentUri, selection, selectionArgs
) > 0;
}
}
return context.getContentResolver().delete(uri, null, null) > 0;
} catch (SecurityException e) {
} catch (SecurityException | FileNotFoundException e) {
return false;
}
}
private void renameInternal(Context context, String newName) throws Exception {
ContentValues values = new ContentValues();
values.put(MediaStore.MediaColumns.DISPLAY_NAME, newName);
@ -566,29 +542,18 @@ public class FileHelper {
}
if (DocumentsContract.isDocumentUri(context, uri)) {
String docId = DocumentsContract.getDocumentId(uri);
String[] split = docId.split(":");
String type = split[0];
Uri contentUri;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
} else {
contentUri = MediaStore.Files.getContentUri("external");
if (Build.VERSION.SDK_INT >= 29) {
if (!uri.toString().startsWith("content://com.android.providers.downloads.documents")) {
context.getContentResolver().update(
uri, values, null, null
);
return;
}
DocumentsContract.renameDocument(context.getContentResolver(), uri, newName);
} else if (Build.VERSION.SDK_INT >= 21) {
DocumentsContract.renameDocument(context.getContentResolver(), uri, newName);
return;
}
String selection = "_id=?";
String[] selectionArgs = {split[1]};
context.getContentResolver().update(
contentUri, values, selection, selectionArgs
);
return;
}
}