diff --git a/apps/automated/src/file-system/file-system-tests.ts b/apps/automated/src/file-system/file-system-tests.ts index 7913fc820..44dcd3658 100644 --- a/apps/automated/src/file-system/file-system-tests.ts +++ b/apps/automated/src/file-system/file-system-tests.ts @@ -698,3 +698,24 @@ export function test_FolderClear_RemovesEmptySubfolders(done) { }) .catch(done); } + +export function test_FileCopy(done) { + const now = Date.now(); + const tempFile = fs.File.fromPath(fs.path.join(fs.knownFolders.temp().path, `${now}.txt`)); + const content = 'Hello World: ' + now; + tempFile.writeTextSync(content); + const tempCopy = fs.File.fromPath(fs.path.join(fs.knownFolders.temp().path, `${now}-copy.txt`)); + tempFile + .copy(tempCopy.path) + .then(() => { + TKUnit.assert(tempCopy.size === tempFile.size); + return tempCopy.readText(); + }) + .then((value) => { + TKUnit.assert(value === content); + + return Promise.allSettled([tempFile.remove(), tempCopy.remove()]); + }) + .then(() => done()) + .catch(done); +} diff --git a/apps/toolbox/src/pages/fs-helper.ts b/apps/toolbox/src/pages/fs-helper.ts index a4ca20d51..0c66d8e04 100644 --- a/apps/toolbox/src/pages/fs-helper.ts +++ b/apps/toolbox/src/pages/fs-helper.ts @@ -110,7 +110,12 @@ export function pickFile() { //const file = File.fromPath(args.intent.getData().toString()); //console.log(file); //readFile(file); - copyFile(file); + //copyFile(file); + console.time('fromPath: copy'); + const f = File.fromPath(file, true); + console.timeEnd('fromPath: copy'); + console.log('old path: ', file); + console.log('new path: ', f.path, f.extension, f.size); } }); const Intent = android.content.Intent; diff --git a/packages/core/file-system/file-system-access.android.ts b/packages/core/file-system/file-system-access.android.ts index 7b0c8682b..a5f2d8e63 100644 --- a/packages/core/file-system/file-system-access.android.ts +++ b/packages/core/file-system/file-system-access.android.ts @@ -15,7 +15,7 @@ function getApplicationContext() { } function getOrSetHelper(path: string): org.nativescript.widgets.FileHelper { - return org.nativescript.widgets.FileHelper.fromString(applicationContext, path); + return org.nativescript.widgets.FileHelper.fromString(getApplicationContext(), path); } function isContentUri(path: string): boolean { @@ -766,6 +766,7 @@ export class FileSystemAccess29 extends FileSystemAccess { if (isContentUri(path)) { try { const file = getOrSetHelper(path); + return { path, name: file.getName(), diff --git a/packages/core/file-system/index.d.ts b/packages/core/file-system/index.d.ts index 80c1a55e1..d010a2c0b 100644 --- a/packages/core/file-system/index.d.ts +++ b/packages/core/file-system/index.d.ts @@ -80,11 +80,27 @@ export class File extends FileSystemEntity { */ isLocked: boolean; + /** + * Copies a file to a given path. + * @param dest The path to the destination file. + * Returns a Promise with a boolean. + */ + copy(dest: string): Promise; + + /** + * Copies a file to a given path. + * @param dest The path to the destination file. + * @param onError (optional) A callback function to use if any error occurs. + * Returns a Promise with a boolean. + */ + copySync(dest: string, onError?: (error: any) => any): any; + /** * Gets or creates a File entity at the specified path. * @param path The path to get/create the file at. + * @param copy An optional value when set, copies the content-uri to a temp file enabling the legacy behaviour */ - static fromPath(path: string): File; + static fromPath(path: string, copy?: boolean): File; /** * Reads the content of the file as a string using the specified encoding (defaults to UTF-8). diff --git a/packages/core/file-system/index.ts b/packages/core/file-system/index.ts index ad11ae806..4e5982552 100644 --- a/packages/core/file-system/index.ts +++ b/packages/core/file-system/index.ts @@ -1,5 +1,6 @@ import { IFileSystemAccess, FileSystemAccess, FileSystemAccess29 } from './file-system-access'; import { SDK_VERSION } from '../utils/constants'; +import { getNativeApplication } from '../application'; // The FileSystemAccess implementation, used through all the APIs. let fileAccess: IFileSystemAccess; @@ -180,12 +181,39 @@ export class FileSystemEntity { } } +let applicationContext; +function getApplicationContext() { + if (!applicationContext) { + applicationContext = (getNativeApplication()).getApplicationContext(); + } + + return applicationContext; +} + export class File extends FileSystemEntity { - public static fromPath(path: string) { + public static fromPath(path: string, copy: boolean = false) { const onError = function (error) { throw error; }; + if (global.isAndroid && copy) { + if (path.startsWith('content:')) { + const fileInfo = getFileAccess().getFile(path, onError); + // falls back to creating a temp file without a known extension. + if (!fileInfo) { + const tempFile = `${knownFolders.temp().path}/${java.util.UUID.randomUUID().toString()}`; + org.nativescript.widgets.Async.File.copySync(path, tempFile, getApplicationContext()); + path = tempFile; + } else { + const ext = fileInfo.extension; + const name = `${fileInfo.name.replace(`.${ext}`, '')}.${ext}`; + const tempFile = `${knownFolders.temp().path}/${name}`; + getFileAccess().copySync(path, tempFile); + path = tempFile; + } + } + } + const fileInfo = getFileAccess().getFile(path, onError); if (!fileInfo) { return undefined; diff --git a/packages/core/platforms/android/widgets-release.aar b/packages/core/platforms/android/widgets-release.aar index 946770ab9..6a6422504 100644 Binary files a/packages/core/platforms/android/widgets-release.aar and b/packages/core/platforms/android/widgets-release.aar differ diff --git a/packages/ui-mobile-base/android/widgets/src/main/java/org/nativescript/widgets/FileHelper.java b/packages/ui-mobile-base/android/widgets/src/main/java/org/nativescript/widgets/FileHelper.java index 67d2b254b..10096dfcb 100644 --- a/packages/ui-mobile-base/android/widgets/src/main/java/org/nativescript/widgets/FileHelper.java +++ b/packages/ui-mobile-base/android/widgets/src/main/java/org/nativescript/widgets/FileHelper.java @@ -53,9 +53,8 @@ public class FileHelper { } private static boolean isExternalStorageDocument(Uri uri) { - return false; -// return "com.android.externalstorage.documents".equals(uri -// .getAuthority()); + return "com.android.externalstorage.documents".equals(uri + .getAuthority()); } private static @Nullable @@ -132,7 +131,9 @@ public class FileHelper { String path = split[1]; if ("primary".equals(type)) { - String[] parts = Uri.decode(uri.toString()).split(":" + path + "/"); + int nameIndex = path.lastIndexOf("/"); + String seg = path.substring(0, nameIndex); + String[] parts = Uri.decode(uri.toString()).split(":" + seg); String file = Environment.getExternalStorageDirectory() + "/" + path + "/" + parts[1]; return new File(file); } else { @@ -178,22 +179,21 @@ public class FileHelper { return null; } - int sizeIndex = cursor.getColumnIndex( - MediaStore.MediaColumns.SIZE - ); - - int nameIndex = cursor.getColumnIndex( - MediaStore.MediaColumns.DISPLAY_NAME - ); - - int lastModifiedIndex = cursor.getColumnIndex( - MediaStore.MediaColumns.DATE_MODIFIED - ); - - boolean moved = cursor.moveToFirst(); FileHelper helper = null; if (moved) { + int sizeIndex = cursor.getColumnIndex( + MediaStore.MediaColumns.SIZE + ); + + int nameIndex = cursor.getColumnIndex( + MediaStore.MediaColumns.DISPLAY_NAME + ); + + int lastModifiedIndex = cursor.getColumnIndex( + MediaStore.MediaColumns.DATE_MODIFIED + ); + helper = new FileHelper(uri); helper.size = cursor.getLong(sizeIndex); helper.name = cursor.getString(nameIndex); @@ -244,22 +244,21 @@ public class FileHelper { return; } - int sizeIndex = cursor.getColumnIndex( - MediaStore.MediaColumns.SIZE - ); - - int nameIndex = cursor.getColumnIndex( - MediaStore.MediaColumns.DISPLAY_NAME - ); - - int lastModifiedIndex = cursor.getColumnIndex( - MediaStore.MediaColumns.DATE_MODIFIED - ); - - boolean moved = cursor.moveToFirst(); if (moved) { + int sizeIndex = cursor.getColumnIndex( + MediaStore.MediaColumns.SIZE + ); + + int nameIndex = cursor.getColumnIndex( + MediaStore.MediaColumns.DISPLAY_NAME + ); + + int lastModifiedIndex = cursor.getColumnIndex( + MediaStore.MediaColumns.DATE_MODIFIED + ); + size = cursor.getLong(sizeIndex); name = cursor.getString(nameIndex); mime = context.getContentResolver().getType(uri);