mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-15 02:54:11 +08:00
feat(android): content uri support for File (#9807)
This commit is contained in:
@ -17,6 +17,7 @@
|
|||||||
<Button text="touch-animations" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo" />
|
<Button text="touch-animations" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo" />
|
||||||
<Button text="vector-image" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo" />
|
<Button text="vector-image" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo" />
|
||||||
<Button text="visibility-vs-hidden" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo" />
|
<Button text="visibility-vs-hidden" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo" />
|
||||||
|
<Button text="fs-helper" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
|
73
apps/toolbox/src/pages/fs-helper.ts
Normal file
73
apps/toolbox/src/pages/fs-helper.ts
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import { Page, EventData, Application, File } from '@nativescript/core';
|
||||||
|
|
||||||
|
let page: Page;
|
||||||
|
|
||||||
|
export function navigatingTo(args: EventData) {
|
||||||
|
page = <Page>args.object;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createRandom(args) {
|
||||||
|
if (global.isAndroid) {
|
||||||
|
try {
|
||||||
|
const activity = Application.android.foregroundActivity as androidx.appcompat.app.AppCompatActivity;
|
||||||
|
const selection = [android.provider.MediaStore.MediaColumns.DISPLAY_NAME, android.provider.MediaStore.MediaColumns._ID];
|
||||||
|
// testing with downloads as rename only works with a well know collection downloads/audio/photos/videos API 29+
|
||||||
|
let cursor = activity.getContentResolver().query(android.provider.MediaStore.Downloads.getContentUri('external'), selection, null, null);
|
||||||
|
|
||||||
|
let uri;
|
||||||
|
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
const index = cursor.getColumnIndex(selection[0]);
|
||||||
|
const name = cursor.getString(index);
|
||||||
|
if (name === 'ns_tmp.txt') {
|
||||||
|
const idIndex = cursor.getColumnIndex(selection[1]);
|
||||||
|
const id = cursor.getLong(idIndex);
|
||||||
|
uri = android.net.Uri.parse(`${android.provider.MediaStore.Downloads.getContentUri('external').toString()}/${id}`);
|
||||||
|
cursor.close();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!uri) {
|
||||||
|
const values = new android.content.ContentValues();
|
||||||
|
values.put(android.provider.MediaStore.MediaColumns.DISPLAY_NAME, 'ns_tmp.txt');
|
||||||
|
values.put(android.provider.MediaStore.MediaColumns.MIME_TYPE, 'text/plain');
|
||||||
|
uri = activity.getContentResolver().insert(android.provider.MediaStore.Downloads.getContentUri('external'), values);
|
||||||
|
}
|
||||||
|
|
||||||
|
doWork(uri.toString());
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function doWork(path: string) {
|
||||||
|
try {
|
||||||
|
const file = File.fromPath(path) as File;
|
||||||
|
console.log('name: ', file.name);
|
||||||
|
console.log('path: ', file.path);
|
||||||
|
console.log('parent: ', file.parent);
|
||||||
|
console.log('size: ', file.size);
|
||||||
|
console.log('lastModified: ', file.lastModified);
|
||||||
|
console.log('extension: ', file.extension);
|
||||||
|
if (file.size > 0) {
|
||||||
|
console.log('current text: ', file.readTextSync());
|
||||||
|
} else {
|
||||||
|
file.writeTextSync('Hello World');
|
||||||
|
console.log('after write: ', file.readTextSync());
|
||||||
|
console.log('after write size: ', file.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
file.renameSync(`ns_temp_${Date.now()}.txt`);
|
||||||
|
|
||||||
|
console.log('rename: ', file.name);
|
||||||
|
console.log('rename lastModified: ', file.lastModified);
|
||||||
|
|
||||||
|
file.removeSync();
|
||||||
|
|
||||||
|
console.log('deleted ?', !File.exists(path));
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
6
apps/toolbox/src/pages/fs-helper.xml
Normal file
6
apps/toolbox/src/pages/fs-helper.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo" class="page">
|
||||||
|
|
||||||
|
<StackLayout>
|
||||||
|
<Button text="Create Random" tap="createRandom" />
|
||||||
|
</StackLayout>
|
||||||
|
</Page>
|
@ -1,6 +1,8 @@
|
|||||||
import * as textModule from '../text';
|
import * as textModule from '../text';
|
||||||
import { getNativeApplication } from '../application';
|
import { getNativeApplication } from '../application';
|
||||||
|
|
||||||
|
import type { IFileSystemAccess } from './file-system-access';
|
||||||
|
|
||||||
let applicationContext: android.content.Context;
|
let applicationContext: android.content.Context;
|
||||||
function getApplicationContext() {
|
function getApplicationContext() {
|
||||||
if (!applicationContext) {
|
if (!applicationContext) {
|
||||||
@ -10,7 +12,18 @@ function getApplicationContext() {
|
|||||||
return applicationContext;
|
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 = '/';
|
private _pathSeparator = '/';
|
||||||
|
|
||||||
public getLastModified(path: string): Date {
|
public getLastModified(path: string): Date {
|
||||||
@ -592,3 +605,313 @@ export class FileSystemAccess {
|
|||||||
return result;
|
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.
|
* 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.
|
* Gets the last modified date of a file with a given path.
|
||||||
* @param path Path to the file.
|
* @param path Path to the file.
|
||||||
@ -248,3 +248,75 @@ export class FileSystemAccess {
|
|||||||
*/
|
*/
|
||||||
joinPaths(paths: string[]): string;
|
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,16 +1,20 @@
|
|||||||
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.
|
// 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, 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
|
* @returns FileSystemAccess
|
||||||
*/
|
*/
|
||||||
export function getFileAccess(): FileSystemAccess {
|
export function getFileAccess(): IFileSystemAccess {
|
||||||
if (!fileAccess) {
|
if (!fileAccess) {
|
||||||
|
if (global.isAndroid && parseInt(Device.sdkVersion) >= 29) {
|
||||||
|
fileAccess = new FileSystemAccess29();
|
||||||
|
} else {
|
||||||
fileAccess = new FileSystemAccess();
|
fileAccess = new FileSystemAccess();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return fileAccess;
|
return fileAccess;
|
||||||
}
|
}
|
||||||
@ -161,12 +165,7 @@ export class FileSystemEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get lastModified(): Date {
|
get lastModified(): Date {
|
||||||
let value = this._lastModified;
|
return getFileAccess().getLastModified(this.path);
|
||||||
if (!this._lastModified) {
|
|
||||||
value = this._lastModified = getFileAccess().getLastModified(this.path);
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,7 +203,7 @@ export class File extends FileSystemEntity {
|
|||||||
public read(): Promise<any> {
|
public read(): Promise<any> {
|
||||||
return new Promise<any>((resolve, reject) => {
|
return new Promise<any>((resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
this.checkAccess();
|
this._checkAccess();
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
reject(ex);
|
reject(ex);
|
||||||
|
|
||||||
@ -229,7 +228,7 @@ export class File extends FileSystemEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public readSync(onError?: (error: any) => any): any {
|
public readSync(onError?: (error: any) => any): any {
|
||||||
this.checkAccess();
|
this._checkAccess();
|
||||||
|
|
||||||
this._locked = true;
|
this._locked = true;
|
||||||
|
|
||||||
@ -251,7 +250,7 @@ export class File extends FileSystemEntity {
|
|||||||
public write(content: any): Promise<void> {
|
public write(content: any): Promise<void> {
|
||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
this.checkAccess();
|
this._checkAccess();
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
reject(ex);
|
reject(ex);
|
||||||
|
|
||||||
@ -276,7 +275,7 @@ export class File extends FileSystemEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public writeSync(content: any, onError?: (error: any) => any): void {
|
public writeSync(content: any, onError?: (error: any) => any): void {
|
||||||
this.checkAccess();
|
this._checkAccess();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this._locked = true;
|
this._locked = true;
|
||||||
@ -298,7 +297,7 @@ export class File extends FileSystemEntity {
|
|||||||
public readText(encoding?: string): Promise<string> {
|
public readText(encoding?: string): Promise<string> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
this.checkAccess();
|
this._checkAccess();
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
reject(ex);
|
reject(ex);
|
||||||
|
|
||||||
@ -323,7 +322,7 @@ export class File extends FileSystemEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public readTextSync(onError?: (error: any) => any, encoding?: string): string {
|
public readTextSync(onError?: (error: any) => any, encoding?: string): string {
|
||||||
this.checkAccess();
|
this._checkAccess();
|
||||||
|
|
||||||
this._locked = true;
|
this._locked = true;
|
||||||
|
|
||||||
@ -344,7 +343,7 @@ export class File extends FileSystemEntity {
|
|||||||
public writeText(content: string, encoding?: string): Promise<any> {
|
public writeText(content: string, encoding?: string): Promise<any> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
this.checkAccess();
|
this._checkAccess();
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
reject(ex);
|
reject(ex);
|
||||||
|
|
||||||
@ -369,7 +368,7 @@ export class File extends FileSystemEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public writeTextSync(content: string, onError?: (error: any) => any, encoding?: string): void {
|
public writeTextSync(content: string, onError?: (error: any) => any, encoding?: string): void {
|
||||||
this.checkAccess();
|
this._checkAccess();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this._locked = true;
|
this._locked = true;
|
||||||
@ -388,7 +387,7 @@ export class File extends FileSystemEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private checkAccess() {
|
_checkAccess() {
|
||||||
if (this.isLocked) {
|
if (this.isLocked) {
|
||||||
throw new Error('Cannot access a locked file.');
|
throw new Error('Cannot access a locked file.');
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
@ -635,6 +635,53 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare module org {
|
||||||
|
export module nativescript {
|
||||||
|
export module widgets {
|
||||||
|
export class FileHelper {
|
||||||
|
public static class: java.lang.Class<org.nativescript.widgets.FileHelper>;
|
||||||
|
public readText(param0: globalAndroid.content.Context, param1: string, param2: org.nativescript.widgets.FileHelper.Callback): void;
|
||||||
|
public writeSync(param0: globalAndroid.content.Context, param1: androidNative.Array<number>, param2: org.nativescript.widgets.FileHelper.Callback): void;
|
||||||
|
public static fromString(param1: globalAndroid.content.Context, param0: string): org.nativescript.widgets.FileHelper;
|
||||||
|
public writeText(param0: globalAndroid.content.Context, param1: string, param2: string, param3: org.nativescript.widgets.FileHelper.Callback): void;
|
||||||
|
public writeTextSync(param0: globalAndroid.content.Context, param1: string, param2: string, param3: org.nativescript.widgets.FileHelper.Callback): void;
|
||||||
|
public copyToFileSync(param0: globalAndroid.content.Context, param1: java.io.File, param2: org.nativescript.widgets.FileHelper.Callback): boolean;
|
||||||
|
public getName(): string;
|
||||||
|
public read(param0: globalAndroid.content.Context, param1: org.nativescript.widgets.FileHelper.Callback): void;
|
||||||
|
public copyToFile(param0: globalAndroid.content.Context, param1: java.io.File, param2: org.nativescript.widgets.FileHelper.Callback): void;
|
||||||
|
public static fromUri(param0: globalAndroid.content.Context, param1: globalAndroid.net.Uri): org.nativescript.widgets.FileHelper;
|
||||||
|
public readSync(param0: globalAndroid.content.Context, param1: org.nativescript.widgets.FileHelper.Callback): androidNative.Array<number>;
|
||||||
|
public write(param0: globalAndroid.content.Context, param1: androidNative.Array<number>, param2: org.nativescript.widgets.FileHelper.Callback): void;
|
||||||
|
public getSize(): number;
|
||||||
|
public getMime(): string;
|
||||||
|
public readTextSync(param0: globalAndroid.content.Context, param1: string, param2: org.nativescript.widgets.FileHelper.Callback): string;
|
||||||
|
public delete(param0: globalAndroid.content.Context): boolean;
|
||||||
|
public static exists(param0: globalAndroid.content.Context, param1: string): boolean;
|
||||||
|
public static exists(param0: globalAndroid.content.Context, param1: globalAndroid.net.Uri): boolean;
|
||||||
|
public getExtension(): string;
|
||||||
|
public getLastModified(): number;
|
||||||
|
public renameSync(param0: globalAndroid.content.Context, param1: string, param2: org.nativescript.widgets.FileHelper.Callback): string;
|
||||||
|
public rename(param0: globalAndroid.content.Context, param1: string, param2: org.nativescript.widgets.FileHelper.Callback): string;
|
||||||
|
}
|
||||||
|
export module FileHelper {
|
||||||
|
export class Callback {
|
||||||
|
public static class: java.lang.Class<org.nativescript.widgets.FileHelper.Callback>;
|
||||||
|
/**
|
||||||
|
* Constructs a new instance of the org.nativescript.widgets.FileHelper$Callback interface with the provided implementation. An empty constructor exists calling super() when extending the interface class.
|
||||||
|
*/
|
||||||
|
public constructor(implementation: {
|
||||||
|
onError(param0: java.lang.Exception): void;
|
||||||
|
onSuccess(param0: any): void;
|
||||||
|
});
|
||||||
|
public constructor();
|
||||||
|
public onError(param0: java.lang.Exception): void;
|
||||||
|
public onSuccess(param0: any): void;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
declare module org {
|
declare module org {
|
||||||
export module nativescript {
|
export module nativescript {
|
||||||
export module widgets {
|
export module widgets {
|
||||||
|
@ -0,0 +1,388 @@
|
|||||||
|
package org.nativescript.widgets;
|
||||||
|
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
import android.webkit.MimeTypeMap;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
|
public class FileHelper {
|
||||||
|
private Uri uri;
|
||||||
|
private long size;
|
||||||
|
private String name;
|
||||||
|
private String mime;
|
||||||
|
private long lastModified;
|
||||||
|
private ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||||
|
private Handler handler;
|
||||||
|
|
||||||
|
public interface Callback {
|
||||||
|
void onError(Exception exception);
|
||||||
|
|
||||||
|
void onSuccess(@Nullable Object result);
|
||||||
|
}
|
||||||
|
|
||||||
|
FileHelper(Uri uri) {
|
||||||
|
handler = new Handler(Looper.getMainLooper());
|
||||||
|
this.uri = uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean exists(Context context, String string) {
|
||||||
|
try {
|
||||||
|
return exists(context, Uri.parse(string));
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean exists(Context context, Uri uri) {
|
||||||
|
Cursor cursor = context.getContentResolver()
|
||||||
|
.query(uri, null, null, null, null);
|
||||||
|
|
||||||
|
boolean exists = cursor.moveToFirst();
|
||||||
|
cursor.close();
|
||||||
|
return exists;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @Nullable
|
||||||
|
FileHelper fromString(Context context, String string) {
|
||||||
|
try {
|
||||||
|
return fromUri(context, Uri.parse(string));
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @Nullable
|
||||||
|
FileHelper fromUri(Context context, Uri uri) {
|
||||||
|
Cursor cursor = context.getContentResolver()
|
||||||
|
.query(uri, null, null, null, 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) {
|
||||||
|
helper = new FileHelper(uri);
|
||||||
|
helper.size = cursor.getLong(sizeIndex);
|
||||||
|
helper.name = cursor.getString(nameIndex);
|
||||||
|
helper.mime = context.getContentResolver().getType(uri);
|
||||||
|
helper.lastModified = cursor.getLong(lastModifiedIndex);
|
||||||
|
}
|
||||||
|
cursor.close();
|
||||||
|
return helper;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateInternal(Context context) {
|
||||||
|
updateInternal(context, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateInternal(Context context, boolean force) {
|
||||||
|
|
||||||
|
if (force) {
|
||||||
|
// trigger db update
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
|
context.getContentResolver().update(uri, values, null);
|
||||||
|
} else {
|
||||||
|
context.getContentResolver().update(uri, values, null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Cursor cursor = context.getContentResolver()
|
||||||
|
.query(uri, null, null, null, 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();
|
||||||
|
|
||||||
|
if (moved) {
|
||||||
|
size = cursor.getLong(sizeIndex);
|
||||||
|
name = cursor.getString(nameIndex);
|
||||||
|
mime = context.getContentResolver().getType(uri);
|
||||||
|
lastModified = cursor.getLong(lastModifiedIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getSize() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMime() {
|
||||||
|
if (mime == null) {
|
||||||
|
return "application/octet-stream";
|
||||||
|
}
|
||||||
|
return mime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getExtension() {
|
||||||
|
if (mime == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return MimeTypeMap.getSingleton().getExtensionFromMimeType(mime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getLastModified() {
|
||||||
|
return lastModified;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] readSyncInternal(Context context) throws Exception {
|
||||||
|
InputStream is = context.getContentResolver().openInputStream(uri);
|
||||||
|
byte[] array = new byte[(int) size];
|
||||||
|
is.read(array);
|
||||||
|
is.close();
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable
|
||||||
|
byte[] readSync(Context context, @Nullable Callback callback) {
|
||||||
|
try {
|
||||||
|
return readSyncInternal(context);
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (callback != null) {
|
||||||
|
callback.onError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void read(Context context, Callback callback) {
|
||||||
|
executor.execute(() -> {
|
||||||
|
try {
|
||||||
|
byte[] result = readSyncInternal(context);
|
||||||
|
handler.post(() -> callback.onSuccess(result));
|
||||||
|
} catch (Exception e) {
|
||||||
|
handler.post(() -> callback.onError(e));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private String readTextSyncInternal(Context context, @Nullable String encoding) throws Exception {
|
||||||
|
String characterSet = encoding;
|
||||||
|
if (characterSet == null) {
|
||||||
|
characterSet = "UTF-8";
|
||||||
|
}
|
||||||
|
|
||||||
|
InputStream is = context.getContentResolver().openInputStream(uri);
|
||||||
|
InputStreamReader isr = new InputStreamReader(is, characterSet);
|
||||||
|
BufferedReader reader = new BufferedReader(isr);
|
||||||
|
char[] buf = new char[is.available()];
|
||||||
|
reader.read(buf);
|
||||||
|
reader.close();
|
||||||
|
return new String(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String readTextSync(Context context, @Nullable String encoding, @Nullable Callback callback) {
|
||||||
|
try {
|
||||||
|
return readTextSyncInternal(context, encoding);
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (callback != null) {
|
||||||
|
callback.onError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void readText(Context context, @Nullable String encoding, Callback callback) {
|
||||||
|
executor.execute(() -> {
|
||||||
|
try {
|
||||||
|
String result = readTextSyncInternal(context, encoding);
|
||||||
|
handler.post(() -> callback.onSuccess(result));
|
||||||
|
} catch (Exception e) {
|
||||||
|
handler.post(() -> callback.onError(e));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeSyncInternal(Context context, byte[] content) throws Exception {
|
||||||
|
OutputStream os = context.getContentResolver().openOutputStream(uri);
|
||||||
|
os.write(content, 0, content.length);
|
||||||
|
os.flush();
|
||||||
|
os.close();
|
||||||
|
updateInternal(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeSync(Context context, byte[] content, @Nullable Callback callback) {
|
||||||
|
try {
|
||||||
|
writeSyncInternal(context, content);
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (callback != null) {
|
||||||
|
callback.onError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void write(Context context, byte[] content, Callback callback) {
|
||||||
|
executor.execute(() -> {
|
||||||
|
try {
|
||||||
|
writeSyncInternal(context, content);
|
||||||
|
handler.post(() -> callback.onSuccess(null));
|
||||||
|
} catch (Exception e) {
|
||||||
|
handler.post(() -> callback.onError(e));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeTextSyncInternal(Context context, String content, @Nullable String encoding) throws Exception {
|
||||||
|
OutputStream os = context.getContentResolver().openOutputStream(uri);
|
||||||
|
String characterSet = encoding;
|
||||||
|
if (characterSet == null) {
|
||||||
|
characterSet = "UTF-8";
|
||||||
|
}
|
||||||
|
OutputStreamWriter osw = new OutputStreamWriter(os, characterSet);
|
||||||
|
osw.write(content);
|
||||||
|
osw.flush();
|
||||||
|
osw.close();
|
||||||
|
updateInternal(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeTextSync(Context context, String content, @Nullable String encoding, @Nullable Callback callback) {
|
||||||
|
try {
|
||||||
|
writeTextSyncInternal(context, content, encoding);
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (callback != null) {
|
||||||
|
callback.onError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeText(Context context, String content, @Nullable String encoding, Callback callback) {
|
||||||
|
executor.execute(() -> {
|
||||||
|
try {
|
||||||
|
writeTextSyncInternal(context, content, encoding);
|
||||||
|
handler.post(() -> callback.onSuccess(null));
|
||||||
|
} catch (Exception e) {
|
||||||
|
handler.post(() -> callback.onError(e));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void copyToFileInternal(InputStream is, OutputStream os) throws Exception {
|
||||||
|
int read;
|
||||||
|
byte[] buf = new byte[1024];
|
||||||
|
while ((read = is.read(buf)) != -1) {
|
||||||
|
os.write(buf, 0, read);
|
||||||
|
}
|
||||||
|
is.close();
|
||||||
|
os.flush();
|
||||||
|
os.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void copyToFileInternal(Context context, File file) throws Exception {
|
||||||
|
InputStream is = context.getContentResolver().openInputStream(uri);
|
||||||
|
FileOutputStream os = new FileOutputStream(file);
|
||||||
|
copyToFileInternal(is, os);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean copyToFileSync(Context context, File file, @Nullable Callback callback) {
|
||||||
|
boolean completed = false;
|
||||||
|
try {
|
||||||
|
copyToFileInternal(context, file);
|
||||||
|
completed = true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (callback != null) {
|
||||||
|
callback.onError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return completed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void copyToFile(Context context, File file, Callback callback) {
|
||||||
|
executor.execute(() -> {
|
||||||
|
try {
|
||||||
|
copyToFileInternal(context, file);
|
||||||
|
handler.post(() -> callback.onSuccess(true));
|
||||||
|
} catch (Exception e) {
|
||||||
|
handler.post(() -> callback.onError(e));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean delete(Context context) {
|
||||||
|
try {
|
||||||
|
return context.getContentResolver().delete(uri, null, null) > 0;
|
||||||
|
} catch (SecurityException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renameInternal(Context context, String newName) throws Exception {
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(MediaStore.MediaColumns.DISPLAY_NAME, newName);
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
|
context.getContentResolver().update(uri, values, null);
|
||||||
|
} else {
|
||||||
|
context.getContentResolver().update(uri, values, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateInternal(context, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void renameSync(Context context, String newName, @Nullable Callback callback) {
|
||||||
|
try {
|
||||||
|
renameInternal(context, newName);
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (callback != null) {
|
||||||
|
callback.onError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void rename(Context context, String newName, Callback callback) {
|
||||||
|
executor.execute(() -> {
|
||||||
|
try {
|
||||||
|
renameInternal(context, newName);
|
||||||
|
handler.post(() -> callback.onSuccess(null));
|
||||||
|
} catch (Exception e) {
|
||||||
|
handler.post(() -> callback.onError(e));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Reference in New Issue
Block a user