mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-17 12:57:42 +08:00
feat(android): content uri support for File (#9807)
This commit is contained in:
@ -1,6 +1,8 @@
|
||||
import * as textModule from '../text';
|
||||
import { getNativeApplication } from '../application';
|
||||
|
||||
import type { IFileSystemAccess } from './file-system-access';
|
||||
|
||||
let applicationContext: android.content.Context;
|
||||
function getApplicationContext() {
|
||||
if (!applicationContext) {
|
||||
@ -10,7 +12,18 @@ function getApplicationContext() {
|
||||
return applicationContext;
|
||||
}
|
||||
|
||||
export class FileSystemAccess {
|
||||
function getOrSetHelper(path: string): org.nativescript.widgets.FileHelper {
|
||||
return org.nativescript.widgets.FileHelper.fromString(applicationContext, path);
|
||||
}
|
||||
|
||||
function isContentUri(path: string): boolean {
|
||||
if (typeof path === 'string' && path.startsWith('content:')) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export class FileSystemAccess implements IFileSystemAccess {
|
||||
private _pathSeparator = '/';
|
||||
|
||||
public getLastModified(path: string): Date {
|
||||
@ -592,3 +605,313 @@ export class FileSystemAccess {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export class FileSystemAccess29 extends FileSystemAccess {
|
||||
getLastModified(path: string): Date {
|
||||
if (isContentUri(path)) {
|
||||
return new Date(getOrSetHelper(path).getLastModified() * 1000);
|
||||
}
|
||||
return super.getLastModified(path);
|
||||
}
|
||||
|
||||
getFileSize(path: string): number {
|
||||
if (isContentUri(path)) {
|
||||
return getOrSetHelper(path).getSize();
|
||||
}
|
||||
return super.getFileSize(path);
|
||||
}
|
||||
|
||||
getParent(path: string, onError?: (error: any) => any): { path: string; name: string } {
|
||||
if (isContentUri(path)) {
|
||||
return null;
|
||||
}
|
||||
return super.getParent(path, onError);
|
||||
}
|
||||
getFile(path: string, onError?: (error: any) => any): { path: string; name: string; extension: string } {
|
||||
if (isContentUri(path)) {
|
||||
try {
|
||||
const file = getOrSetHelper(path);
|
||||
return {
|
||||
path,
|
||||
name: file.getName(),
|
||||
extension: file.getExtension(),
|
||||
};
|
||||
} catch (e) {
|
||||
if (typeof onError === 'function') {
|
||||
onError(e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
return super.getFile(path, onError);
|
||||
}
|
||||
getFolder(path: string, onError?: (error: any) => any): { path: string; name: string } {
|
||||
if (isContentUri(path)) {
|
||||
return null;
|
||||
}
|
||||
return super.getFolder(path, onError);
|
||||
}
|
||||
getEntities(path: string, onError?: (error: any) => any): { path: string; name: string; extension: string }[] {
|
||||
if (isContentUri(path)) {
|
||||
return null;
|
||||
}
|
||||
return super.getEntities(path, onError);
|
||||
}
|
||||
eachEntity(path: string, onEntity: (entity: { path: string; name: string; extension: string }) => boolean, onError?: (error: any) => any) {
|
||||
if (isContentUri(path)) {
|
||||
return null;
|
||||
}
|
||||
super.eachEntity(path, onEntity);
|
||||
}
|
||||
fileExists(path: string): boolean {
|
||||
if (isContentUri(path)) {
|
||||
return org.nativescript.widgets.FileHelper.exists(applicationContext, path);
|
||||
}
|
||||
return super.fileExists(path);
|
||||
}
|
||||
folderExists(path: string): boolean {
|
||||
if (isContentUri(path)) {
|
||||
return null;
|
||||
}
|
||||
return super.folderExists(path);
|
||||
}
|
||||
deleteFile(path: string, onError?: (error: any) => any) {
|
||||
if (isContentUri(path)) {
|
||||
try {
|
||||
getOrSetHelper(path).delete(applicationContext);
|
||||
} catch (e) {
|
||||
onError?.(e);
|
||||
}
|
||||
} else {
|
||||
super.deleteFile(path, onError);
|
||||
}
|
||||
}
|
||||
deleteFolder(path: string, onError?: (error: any) => any) {
|
||||
if (!isContentUri(path)) {
|
||||
super.deleteFolder(path, onError);
|
||||
}
|
||||
}
|
||||
emptyFolder(path: string, onError?: (error: any) => any): void {
|
||||
if (!isContentUri(path)) {
|
||||
super.emptyFolder(path, onError);
|
||||
}
|
||||
}
|
||||
rename(path: string, newPath: string, onError?: (error: any) => any): void {
|
||||
if (isContentUri(path)) {
|
||||
let callback = null;
|
||||
if (typeof onError === 'function') {
|
||||
callback = new org.nativescript.widgets.FileHelper.Callback({
|
||||
onSuccess(result) {},
|
||||
onError(error) {
|
||||
onError(error);
|
||||
},
|
||||
});
|
||||
}
|
||||
getOrSetHelper(path).renameSync(applicationContext, newPath, callback);
|
||||
} else {
|
||||
super.rename(path, newPath, onError);
|
||||
}
|
||||
}
|
||||
|
||||
public renameAsync(path: string, newPath: string): Promise<any> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
getOrSetHelper(path).renameSync(
|
||||
applicationContext,
|
||||
newPath,
|
||||
new org.nativescript.widgets.FileHelper.Callback({
|
||||
onSuccess(result) {
|
||||
resolve();
|
||||
},
|
||||
onError(error) {
|
||||
reject(error);
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
getDocumentsFolderPath(): string {
|
||||
return super.getDocumentsFolderPath();
|
||||
}
|
||||
getTempFolderPath(): string {
|
||||
return super.getDocumentsFolderPath();
|
||||
}
|
||||
getLogicalRootPath(): string {
|
||||
return super.getDocumentsFolderPath();
|
||||
}
|
||||
getCurrentAppPath(): string {
|
||||
return super.getDocumentsFolderPath();
|
||||
}
|
||||
public readText = this.readTextSync.bind(this);
|
||||
|
||||
readTextAsync(path: string, encoding?: any): Promise<string> {
|
||||
if (isContentUri(path)) {
|
||||
return new Promise((resolve, reject) => {
|
||||
getOrSetHelper(path).readText(
|
||||
applicationContext,
|
||||
encoding ?? null,
|
||||
new org.nativescript.widgets.FileHelper.Callback({
|
||||
onSuccess(result) {
|
||||
resolve(result);
|
||||
},
|
||||
onError(error) {
|
||||
reject(error);
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
return super.readTextAsync(path, encoding);
|
||||
}
|
||||
readTextSync(path: string, onError?: (error: any) => any, encoding?: any): string {
|
||||
if (isContentUri(path)) {
|
||||
let callback = null;
|
||||
if (typeof onError === 'function') {
|
||||
callback = new org.nativescript.widgets.FileHelper.Callback({
|
||||
onSuccess(result) {},
|
||||
onError(error) {
|
||||
onError(error);
|
||||
},
|
||||
});
|
||||
}
|
||||
return getOrSetHelper(path).readTextSync(applicationContext, encoding ?? null, callback);
|
||||
} else {
|
||||
return super.readTextSync(path, onError, encoding);
|
||||
}
|
||||
}
|
||||
|
||||
read = this.readSync.bind(this);
|
||||
|
||||
readAsync(path: string): Promise<any> {
|
||||
if (isContentUri(path)) {
|
||||
return new Promise((resolve, reject) => {
|
||||
getOrSetHelper(path).read(
|
||||
applicationContext,
|
||||
new org.nativescript.widgets.FileHelper.Callback({
|
||||
onSuccess(result) {
|
||||
resolve(result);
|
||||
},
|
||||
onError(error) {
|
||||
reject(error);
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
return super.readAsync(path);
|
||||
}
|
||||
|
||||
readSync(path: string, onError?: (error: any) => any) {
|
||||
if (isContentUri(path)) {
|
||||
let callback = null;
|
||||
if (typeof onError === 'function') {
|
||||
callback = new org.nativescript.widgets.FileHelper.Callback({
|
||||
onSuccess(result) {},
|
||||
onError(error) {
|
||||
onError(error);
|
||||
},
|
||||
});
|
||||
}
|
||||
return getOrSetHelper(path).readSync(applicationContext, callback);
|
||||
}
|
||||
return super.readSync(path, onError);
|
||||
}
|
||||
|
||||
writeText = this.writeTextSync.bind(this);
|
||||
|
||||
writeTextAsync(path: string, content: string, encoding?: any): Promise<void> {
|
||||
if (isContentUri(path)) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
getOrSetHelper(path).writeText(
|
||||
applicationContext,
|
||||
content,
|
||||
encoding ?? null,
|
||||
new org.nativescript.widgets.FileHelper.Callback({
|
||||
onSuccess(result) {
|
||||
resolve();
|
||||
},
|
||||
onError(error) {
|
||||
reject(error);
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
return super.writeTextAsync(path, content, encoding);
|
||||
}
|
||||
|
||||
writeTextSync(path: string, content: string, onError?: (error: any) => any, encoding?: any) {
|
||||
if (isContentUri(path)) {
|
||||
let callback = null;
|
||||
if (typeof onError === 'function') {
|
||||
callback = new org.nativescript.widgets.FileHelper.Callback({
|
||||
onSuccess(result) {},
|
||||
onError(error) {
|
||||
onError(error);
|
||||
},
|
||||
});
|
||||
}
|
||||
getOrSetHelper(path).writeTextSync(applicationContext, content, encoding ?? null, callback);
|
||||
} else {
|
||||
super.writeTextSync(path, content, onError);
|
||||
}
|
||||
}
|
||||
|
||||
write = this.writeSync.bind(this);
|
||||
|
||||
writeAsync(path: string, content: any): Promise<void> {
|
||||
if (isContentUri(path)) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
getOrSetHelper(path).write(
|
||||
applicationContext,
|
||||
content,
|
||||
new org.nativescript.widgets.FileHelper.Callback({
|
||||
onSuccess(result) {
|
||||
resolve();
|
||||
},
|
||||
onError(error) {
|
||||
reject(error);
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
return super.writeAsync(path, content);
|
||||
}
|
||||
|
||||
writeSync(path: string, content: any, onError?: (error: any) => any) {
|
||||
if (isContentUri(path)) {
|
||||
let callback = null;
|
||||
if (typeof onError === 'function') {
|
||||
callback = new org.nativescript.widgets.FileHelper.Callback({
|
||||
onSuccess(result) {},
|
||||
onError(error) {
|
||||
onError(error);
|
||||
},
|
||||
});
|
||||
}
|
||||
getOrSetHelper(path).writeSync(applicationContext, content, callback);
|
||||
} else {
|
||||
super.writeSync(path, content, onError);
|
||||
}
|
||||
}
|
||||
|
||||
getFileExtension(path: string): string {
|
||||
if (isContentUri(path)) {
|
||||
return getOrSetHelper(path).getExtension();
|
||||
}
|
||||
return super.getFileExtension(path);
|
||||
}
|
||||
getPathSeparator(): string {
|
||||
return super.getPathSeparator();
|
||||
}
|
||||
normalizePath(path: string): string {
|
||||
return super.normalizePath(path);
|
||||
}
|
||||
joinPath(left: string, right: string): string {
|
||||
return super.joinPath(left, right);
|
||||
}
|
||||
joinPaths(paths: string[]): string {
|
||||
return super.joinPaths(paths);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* An utility class used to provide methods to access and work with the file system.
|
||||
*/
|
||||
export class FileSystemAccess {
|
||||
export interface IFileSystemAccess {
|
||||
/**
|
||||
* Gets the last modified date of a file with a given path.
|
||||
* @param path Path to the file.
|
||||
@ -248,3 +248,75 @@ export class FileSystemAccess {
|
||||
*/
|
||||
joinPaths(paths: string[]): string;
|
||||
}
|
||||
|
||||
export class FileSystemAccess implements IFileSystemAccess {
|
||||
getLastModified(path: string): Date;
|
||||
|
||||
getFileSize(path: string): number;
|
||||
|
||||
getParent(path: string, onError?: (error: any) => any): { path: string; name: string };
|
||||
|
||||
getFile(path: string, onError?: (error: any) => any): { path: string; name: string; extension: string };
|
||||
|
||||
getFolder(path: string, onError?: (error: any) => any): { path: string; name: string };
|
||||
|
||||
getEntities(path: string, onError?: (error: any) => any): Array<{ path: string; name: string; extension: string }>;
|
||||
|
||||
eachEntity(path: string, onEntity: (entity: { path: string; name: string; extension: string }) => boolean, onError?: (error: any) => any);
|
||||
|
||||
fileExists(path: string): boolean;
|
||||
|
||||
folderExists(path: string): boolean;
|
||||
|
||||
deleteFile(path: string, onError?: (error: any) => any);
|
||||
|
||||
deleteFolder(path: string, onError?: (error: any) => any);
|
||||
|
||||
emptyFolder(path: string, onError?: (error: any) => any): void;
|
||||
|
||||
rename(path: string, newPath: string, onError?: (error: any) => any): void;
|
||||
|
||||
getDocumentsFolderPath(): string;
|
||||
|
||||
getTempFolderPath(): string;
|
||||
|
||||
getLogicalRootPath(): string;
|
||||
|
||||
getCurrentAppPath(): string;
|
||||
|
||||
readText(path: string, onError?: (error: any) => any, encoding?: any): string;
|
||||
|
||||
readTextAsync(path: string, encoding?: any): Promise<string>;
|
||||
|
||||
readTextSync(path: string, onError?: (error: any) => any, encoding?: any): string;
|
||||
|
||||
read(path: string, onError?: (error: any) => any): any;
|
||||
|
||||
readAsync(path: string): Promise<any>;
|
||||
|
||||
readSync(path: string, onError?: (error: any) => any): any;
|
||||
|
||||
writeText(path: string, content: string, onError?: (error: any) => any, encoding?: any);
|
||||
|
||||
writeTextAsync(path: string, content: string, encoding?: any): Promise<void>;
|
||||
|
||||
writeTextSync(path: string, content: string, onError?: (error: any) => any, encoding?: any);
|
||||
|
||||
write(path: string, content: any, onError?: (error: any) => any);
|
||||
|
||||
writeAsync(path: string, content: any): Promise<void>;
|
||||
|
||||
writeSync(path: string, content: any, onError?: (error: any) => any);
|
||||
|
||||
getFileExtension(path: string): string;
|
||||
|
||||
getPathSeparator(): string;
|
||||
|
||||
normalizePath(path: string): string;
|
||||
|
||||
joinPath(left: string, right: string): string;
|
||||
|
||||
joinPaths(paths: string[]): string;
|
||||
}
|
||||
|
||||
export class FileSystemAccess29 extends FileSystemAccess {}
|
||||
|
@ -1,15 +1,19 @@
|
||||
import { FileSystemAccess } from './file-system-access';
|
||||
|
||||
import { IFileSystemAccess, FileSystemAccess, FileSystemAccess29 } from './file-system-access';
|
||||
import { Device } from '../platform';
|
||||
// The FileSystemAccess implementation, used through all the APIs.
|
||||
let fileAccess: FileSystemAccess;
|
||||
let fileAccess: IFileSystemAccess;
|
||||
|
||||
/**
|
||||
* Returns FileSystemAccess, a shared singleton utility class to provide methods to access and work with the file system. This is used under the hood of all the file system apis in @nativescript/core and provided as a lower level convenience if needed.
|
||||
* @returns FileSystemAccess
|
||||
*/
|
||||
export function getFileAccess(): FileSystemAccess {
|
||||
export function getFileAccess(): IFileSystemAccess {
|
||||
if (!fileAccess) {
|
||||
fileAccess = new FileSystemAccess();
|
||||
if (global.isAndroid && parseInt(Device.sdkVersion) >= 29) {
|
||||
fileAccess = new FileSystemAccess29();
|
||||
} else {
|
||||
fileAccess = new FileSystemAccess();
|
||||
}
|
||||
}
|
||||
|
||||
return fileAccess;
|
||||
@ -161,12 +165,7 @@ export class FileSystemEntity {
|
||||
}
|
||||
|
||||
get lastModified(): Date {
|
||||
let value = this._lastModified;
|
||||
if (!this._lastModified) {
|
||||
value = this._lastModified = getFileAccess().getLastModified(this.path);
|
||||
}
|
||||
|
||||
return value;
|
||||
return getFileAccess().getLastModified(this.path);
|
||||
}
|
||||
}
|
||||
|
||||
@ -204,7 +203,7 @@ export class File extends FileSystemEntity {
|
||||
public read(): Promise<any> {
|
||||
return new Promise<any>((resolve, reject) => {
|
||||
try {
|
||||
this.checkAccess();
|
||||
this._checkAccess();
|
||||
} catch (ex) {
|
||||
reject(ex);
|
||||
|
||||
@ -229,7 +228,7 @@ export class File extends FileSystemEntity {
|
||||
}
|
||||
|
||||
public readSync(onError?: (error: any) => any): any {
|
||||
this.checkAccess();
|
||||
this._checkAccess();
|
||||
|
||||
this._locked = true;
|
||||
|
||||
@ -251,7 +250,7 @@ export class File extends FileSystemEntity {
|
||||
public write(content: any): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
try {
|
||||
this.checkAccess();
|
||||
this._checkAccess();
|
||||
} catch (ex) {
|
||||
reject(ex);
|
||||
|
||||
@ -276,7 +275,7 @@ export class File extends FileSystemEntity {
|
||||
}
|
||||
|
||||
public writeSync(content: any, onError?: (error: any) => any): void {
|
||||
this.checkAccess();
|
||||
this._checkAccess();
|
||||
|
||||
try {
|
||||
this._locked = true;
|
||||
@ -298,7 +297,7 @@ export class File extends FileSystemEntity {
|
||||
public readText(encoding?: string): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
this.checkAccess();
|
||||
this._checkAccess();
|
||||
} catch (ex) {
|
||||
reject(ex);
|
||||
|
||||
@ -323,7 +322,7 @@ export class File extends FileSystemEntity {
|
||||
}
|
||||
|
||||
public readTextSync(onError?: (error: any) => any, encoding?: string): string {
|
||||
this.checkAccess();
|
||||
this._checkAccess();
|
||||
|
||||
this._locked = true;
|
||||
|
||||
@ -344,7 +343,7 @@ export class File extends FileSystemEntity {
|
||||
public writeText(content: string, encoding?: string): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
this.checkAccess();
|
||||
this._checkAccess();
|
||||
} catch (ex) {
|
||||
reject(ex);
|
||||
|
||||
@ -369,7 +368,7 @@ export class File extends FileSystemEntity {
|
||||
}
|
||||
|
||||
public writeTextSync(content: string, onError?: (error: any) => any, encoding?: string): void {
|
||||
this.checkAccess();
|
||||
this._checkAccess();
|
||||
|
||||
try {
|
||||
this._locked = true;
|
||||
@ -388,7 +387,7 @@ export class File extends FileSystemEntity {
|
||||
}
|
||||
}
|
||||
|
||||
private checkAccess() {
|
||||
_checkAccess() {
|
||||
if (this.isLocked) {
|
||||
throw new Error('Cannot access a locked file.');
|
||||
}
|
||||
|
Binary file not shown.
Reference in New Issue
Block a user