mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-15 02:54:11 +08:00
feat(file-system): append, appendText & createFile (#10285)
This commit is contained in:
@ -5,7 +5,7 @@ import * as fs from '@nativescript/core/file-system';
|
|||||||
|
|
||||||
import * as TKUnit from '../tk-unit';
|
import * as TKUnit from '../tk-unit';
|
||||||
import * as appModule from '@nativescript/core/application';
|
import * as appModule from '@nativescript/core/application';
|
||||||
import { isIOS, Device, platformNames } from '@nativescript/core';
|
import { isIOS, Device, platformNames, isAndroid } from '@nativescript/core';
|
||||||
|
|
||||||
export var testPathNormalize = function () {
|
export var testPathNormalize = function () {
|
||||||
// >> file-system-normalize
|
// >> file-system-normalize
|
||||||
@ -719,3 +719,62 @@ export function test_FileCopy(done) {
|
|||||||
.then(() => done())
|
.then(() => done())
|
||||||
.catch(done);
|
.catch(done);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function testAndroidCreate() {
|
||||||
|
let testFunc = function testFunc() {
|
||||||
|
const file = fs.File.android.createFile({
|
||||||
|
directory: fs.AndroidDirectory.DOWNLOADS,
|
||||||
|
name: `${Date.now()}.txt`,
|
||||||
|
mime: 'text/plain',
|
||||||
|
relativePath: `NativeScript`,
|
||||||
|
});
|
||||||
|
|
||||||
|
file.writeTextSync('some text');
|
||||||
|
|
||||||
|
return file;
|
||||||
|
};
|
||||||
|
if (isAndroid) {
|
||||||
|
const file = testFunc();
|
||||||
|
TKUnit.assertEqual(file.readTextSync(), 'some text', `The contents of the new file created in the 'AndroidDirectory.DOWNLOADS' folder are not as expected.`);
|
||||||
|
file.removeSync();
|
||||||
|
TKUnit.assertTrue(!fs.File.exists(file.path));
|
||||||
|
} else {
|
||||||
|
TKUnit.assertThrows(testFunc, `Trying to retrieve createFile on a platform different from Android should throw!`, `createFile is available on Android only!`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function test_FileAppend(done) {
|
||||||
|
const content = 'Hello World';
|
||||||
|
const hello_world = global.isIOS ? NSString.stringWithString(content).dataUsingEncoding(NSUTF8StringEncoding) : new java.lang.String(content).getBytes('UTF-8');
|
||||||
|
const file = fs.File.fromPath(fs.path.join(fs.knownFolders.temp().path, `${Date.now()}-app.txt`));
|
||||||
|
file
|
||||||
|
.appendText('Hello')
|
||||||
|
.then(() => file.appendText(' World'))
|
||||||
|
.then(() => {
|
||||||
|
TKUnit.assert(file.size === hello_world.length);
|
||||||
|
return file.readText();
|
||||||
|
})
|
||||||
|
.then((value) => {
|
||||||
|
TKUnit.assert(value === content);
|
||||||
|
|
||||||
|
return Promise.allSettled([file.remove()]);
|
||||||
|
})
|
||||||
|
.then(() => done())
|
||||||
|
.catch(done);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function test_FileAppendText(done) {
|
||||||
|
const content = 'Hello World';
|
||||||
|
const file = fs.File.fromPath(fs.path.join(fs.knownFolders.temp().path, `${Date.now()}-app.txt`));
|
||||||
|
file
|
||||||
|
.appendText('Hello')
|
||||||
|
.then(() => file.appendText(' World'))
|
||||||
|
.then(() => file.readText())
|
||||||
|
.then((value) => {
|
||||||
|
TKUnit.assert(value === content);
|
||||||
|
|
||||||
|
return Promise.allSettled([file.remove()]);
|
||||||
|
})
|
||||||
|
.then(() => done())
|
||||||
|
.catch(done);
|
||||||
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Page, EventData, Application, File, Folder, knownFolders, path, getFileAccess, Utils } from '@nativescript/core';
|
import { Page, EventData, Application, File, Folder, knownFolders, path, getFileAccess, Utils, Screen, Http, AndroidDirectory, ImageSource, alert } from '@nativescript/core';
|
||||||
|
|
||||||
let page: Page;
|
let page: Page;
|
||||||
|
|
||||||
@ -217,3 +217,103 @@ function getFileNameFromContent(content: string) {
|
|||||||
const file = getFileAccess().getFile(content);
|
const file = getFileAccess().getFile(content);
|
||||||
return decodeURIComponent(file.name).split('/').pop().toLowerCase();
|
return decodeURIComponent(file.name).split('/').pop().toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let lastDownload: File;
|
||||||
|
export function createFileInDownloads() {
|
||||||
|
if (!global.isAndroid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const width = Screen.mainScreen.widthPixels;
|
||||||
|
const height = Screen.mainScreen.heightPixels;
|
||||||
|
const randomImageUrl = `https://picsum.photos/${width}/${height}.jpg?random=${Date.now()}`;
|
||||||
|
|
||||||
|
Http.getFile(randomImageUrl).then((result: File) => {
|
||||||
|
let file = File.android.createFile({
|
||||||
|
directory: AndroidDirectory.DOWNLOADS,
|
||||||
|
name: `${Date.now()}.jpg`,
|
||||||
|
mime: 'image/jpeg',
|
||||||
|
relativePath: `NativeScript`,
|
||||||
|
});
|
||||||
|
result
|
||||||
|
.copy(file.path)
|
||||||
|
.then((done) => {
|
||||||
|
lastDownload = file;
|
||||||
|
console.log('done: ' + done + '\n' + 'Original path: ' + result.path + '\n' + 'Copied to: ' + file.path + '\n' + 'Original size: ' + result.size + '\n' + 'Copy size: ' + file.size + '\n');
|
||||||
|
alert(`File saved in ${AndroidDirectory.DOWNLOADS}/NativeScript`);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createFileInGallery() {
|
||||||
|
if (!global.isAndroid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const width = Screen.mainScreen.widthPixels;
|
||||||
|
const height = Screen.mainScreen.heightPixels;
|
||||||
|
const randomImageUrl = `https://picsum.photos/${width}/${height}.jpg?random=${Date.now()}`;
|
||||||
|
|
||||||
|
Http.getFile(randomImageUrl).then((result: File) => {
|
||||||
|
let file = File.android.createFile({
|
||||||
|
directory: AndroidDirectory.PICTURES,
|
||||||
|
name: `${Date.now()}.jpg`,
|
||||||
|
mime: 'image/jpeg',
|
||||||
|
relativePath: `NativeScript`,
|
||||||
|
});
|
||||||
|
result
|
||||||
|
.copy(file.path)
|
||||||
|
.then((done) => {
|
||||||
|
console.log('done: ' + done + '\n' + 'Original path: ' + result + '\n' + 'Copied to: ' + file.path + '\n' + 'Original size: ' + result.size + '\n' + 'Copy size: ' + file.size + '\n');
|
||||||
|
alert(`File saved in ${AndroidDirectory.PICTURES}/NativeScript`);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createFileInMusic() {
|
||||||
|
if (!global.isAndroid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Http.getFile('https://github.com/rafaelreis-hotmart/Audio-Sample-files/raw/master/sample.mp3').then((result: File) => {
|
||||||
|
let file = File.android.createFile({
|
||||||
|
directory: AndroidDirectory.MUSIC,
|
||||||
|
name: `${Date.now()}.MP3`,
|
||||||
|
mime: 'audio/mp3',
|
||||||
|
relativePath: `NativeScript/MP3`,
|
||||||
|
});
|
||||||
|
|
||||||
|
result
|
||||||
|
.copy(file.path)
|
||||||
|
.then((done) => {
|
||||||
|
console.log('done: ' + done + '\n' + 'Original path: ' + result + '\n' + 'Copied to: ' + file.path + '\n' + 'Original size: ' + result.size + '\n' + 'Copy size: ' + file.size + '\n');
|
||||||
|
|
||||||
|
alert(`File saved in ${AndroidDirectory.MUSIC}/NativeScript/MP3`);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createAndAppendText() {
|
||||||
|
const file = File.fromPath(path.join(knownFolders.temp().path, `${Date.now()}-app.txt`));
|
||||||
|
file.appendTextSync('Hello');
|
||||||
|
file.appendTextSync(' World');
|
||||||
|
const data = file.readTextSync();
|
||||||
|
console.log('createAndAppend:', data === 'Hello World');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createAndAppendData() {
|
||||||
|
const file = File.fromPath(path.join(knownFolders.temp().path, `${Date.now()}-app.txt`));
|
||||||
|
const hello = global.isIOS ? NSString.stringWithString('Hello').dataUsingEncoding(NSUTF8StringEncoding) : new java.lang.String('Hello').getBytes('UTF-8');
|
||||||
|
const world = global.isIOS ? NSString.stringWithString(' World').dataUsingEncoding(NSUTF8StringEncoding) : new java.lang.String(' World').getBytes('UTF-8');
|
||||||
|
file.appendSync(hello);
|
||||||
|
file.appendSync(world);
|
||||||
|
const data = file.readTextSync();
|
||||||
|
console.log('createAndAppendData:', data === 'Hello World');
|
||||||
|
}
|
||||||
|
@ -5,5 +5,10 @@
|
|||||||
<Button text="Pick File" tap="pickFile" />
|
<Button text="Pick File" tap="pickFile" />
|
||||||
<Button text="Pick Multiple Files" tap="pickFiles" />
|
<Button text="Pick Multiple Files" tap="pickFiles" />
|
||||||
<Button text="Test Copy" tap="copyTest" />
|
<Button text="Test Copy" tap="copyTest" />
|
||||||
|
<Button text="External File Creation" tap="createFileInDownloads" />
|
||||||
|
<Button text="Add file to gallery" tap="createFileInGallery" />
|
||||||
|
<Button text="Add file to music" tap="createFileInMusic" />
|
||||||
|
<Button text="Append Text" tap="createAndAppendText" />
|
||||||
|
<Button text="Append Data" tap="createAndAppendData" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</Page>
|
</Page>
|
||||||
|
@ -375,6 +375,81 @@ export class FileSystemAccess implements IFileSystemAccess {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public appendBuffer = this.appendBufferSync.bind(this);
|
||||||
|
|
||||||
|
public appendBufferAsync(path: string, buffer: ArrayBuffer | Uint8Array | Uint8ClampedArray): Promise<void> {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
org.nativescript.widgets.Async.File.appendBuffer(
|
||||||
|
path,
|
||||||
|
FileSystemAccess.getBuffer(buffer),
|
||||||
|
new org.nativescript.widgets.Async.CompleteCallback({
|
||||||
|
onComplete: () => {
|
||||||
|
resolve();
|
||||||
|
},
|
||||||
|
onError: (err) => {
|
||||||
|
reject(new Error(err));
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
} catch (ex) {
|
||||||
|
reject(ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public appendBufferSync(path: string, buffer: ArrayBuffer | Uint8Array | Uint8ClampedArray, onError?: (error: any) => any) {
|
||||||
|
try {
|
||||||
|
const javaFile = new java.io.File(path);
|
||||||
|
const stream = new java.io.FileOutputStream(javaFile);
|
||||||
|
const channel = stream.getChannel();
|
||||||
|
channel.write(FileSystemAccess.getBuffer(buffer));
|
||||||
|
stream.close();
|
||||||
|
} catch (exception) {
|
||||||
|
if (onError) {
|
||||||
|
onError(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public append = this.appendSync.bind(this);
|
||||||
|
|
||||||
|
public appendAsync(path: string, bytes: androidNative.Array<number>): Promise<void> {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
org.nativescript.widgets.Async.File.append(
|
||||||
|
path,
|
||||||
|
bytes,
|
||||||
|
new org.nativescript.widgets.Async.CompleteCallback({
|
||||||
|
onComplete: () => {
|
||||||
|
resolve();
|
||||||
|
},
|
||||||
|
onError: (err) => {
|
||||||
|
reject(new Error(err));
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
} catch (ex) {
|
||||||
|
reject(ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public appendSync(path: string, bytes: androidNative.Array<number>, onError?: (error: any) => any) {
|
||||||
|
try {
|
||||||
|
const javaFile = new java.io.File(path);
|
||||||
|
const stream = new java.io.FileOutputStream(javaFile, true);
|
||||||
|
stream.write(bytes, 0, bytes.length);
|
||||||
|
stream.close();
|
||||||
|
} catch (exception) {
|
||||||
|
if (onError) {
|
||||||
|
onError(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public writeBuffer = this.writeBufferSync.bind(this);
|
public writeBuffer = this.writeBufferSync.bind(this);
|
||||||
|
|
||||||
public writeBufferAsync(path: string, buffer: ArrayBuffer | Uint8Array | Uint8ClampedArray): Promise<void> {
|
public writeBufferAsync(path: string, buffer: ArrayBuffer | Uint8Array | Uint8ClampedArray): Promise<void> {
|
||||||
@ -538,6 +613,56 @@ export class FileSystemAccess implements IFileSystemAccess {
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public appendText = this.appendTextSync.bind(this);
|
||||||
|
|
||||||
|
public appendTextAsync(path: string, content: string, encoding?: any): Promise<void> {
|
||||||
|
let actualEncoding = encoding;
|
||||||
|
if (!actualEncoding) {
|
||||||
|
actualEncoding = textModule.encoding.UTF_8;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
org.nativescript.widgets.Async.File.appendText(
|
||||||
|
path,
|
||||||
|
content,
|
||||||
|
actualEncoding,
|
||||||
|
new org.nativescript.widgets.Async.CompleteCallback({
|
||||||
|
onComplete: () => {
|
||||||
|
resolve();
|
||||||
|
},
|
||||||
|
onError: (err) => {
|
||||||
|
reject(new Error(err));
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
} catch (ex) {
|
||||||
|
reject(ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public appendTextSync(path: string, content: string, onError?: (error: any) => any, encoding?: any) {
|
||||||
|
try {
|
||||||
|
const javaFile = new java.io.File(path);
|
||||||
|
const stream = new java.io.FileOutputStream(javaFile, true);
|
||||||
|
|
||||||
|
let actualEncoding = encoding;
|
||||||
|
if (!actualEncoding) {
|
||||||
|
actualEncoding = textModule.encoding.UTF_8;
|
||||||
|
}
|
||||||
|
const writer = new java.io.OutputStreamWriter(stream, actualEncoding);
|
||||||
|
|
||||||
|
writer.write(content);
|
||||||
|
writer.close();
|
||||||
|
} catch (exception) {
|
||||||
|
if (onError) {
|
||||||
|
onError(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public writeText = this.writeTextSync.bind(this);
|
public writeText = this.writeTextSync.bind(this);
|
||||||
|
|
||||||
public writeTextAsync(path: string, content: string, encoding?: any): Promise<void> {
|
public writeTextAsync(path: string, content: string, encoding?: any): Promise<void> {
|
||||||
@ -879,6 +1004,124 @@ export class FileSystemAccess29 extends FileSystemAccess {
|
|||||||
return super.getCurrentAppPath();
|
return super.getCurrentAppPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
appendBuffer = this.appendBufferSync.bind(this);
|
||||||
|
|
||||||
|
appendBufferAsync(path: string, content: any): Promise<void> {
|
||||||
|
if (isContentUri(path)) {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
getOrSetHelper(path).appendBuffer(
|
||||||
|
applicationContext,
|
||||||
|
FileSystemAccess.getBuffer(content),
|
||||||
|
new org.nativescript.widgets.FileHelper.Callback({
|
||||||
|
onSuccess(result) {
|
||||||
|
resolve();
|
||||||
|
},
|
||||||
|
onError(error) {
|
||||||
|
reject(error);
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return super.appendAsync(path, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
appendBufferSync(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).appendSync(applicationContext, FileSystemAccess.getBuffer(content), callback);
|
||||||
|
} else {
|
||||||
|
super.appendSync(path, content, onError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
append = this.appendSync.bind(this);
|
||||||
|
|
||||||
|
appendAsync(path: string, content: any): Promise<void> {
|
||||||
|
if (isContentUri(path)) {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
getOrSetHelper(path).append(
|
||||||
|
applicationContext,
|
||||||
|
content,
|
||||||
|
new org.nativescript.widgets.FileHelper.Callback({
|
||||||
|
onSuccess(result) {
|
||||||
|
resolve();
|
||||||
|
},
|
||||||
|
onError(error) {
|
||||||
|
reject(error);
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return super.appendAsync(path, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
appendSync(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).appendSync(applicationContext, content, callback);
|
||||||
|
} else {
|
||||||
|
super.appendSync(path, content, onError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
appendText = this.appendTextSync.bind(this);
|
||||||
|
|
||||||
|
appendTextAsync(path: string, content: string, encoding?: any): Promise<void> {
|
||||||
|
if (isContentUri(path)) {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
getOrSetHelper(path).appendText(
|
||||||
|
applicationContext,
|
||||||
|
content,
|
||||||
|
encoding ?? null,
|
||||||
|
new org.nativescript.widgets.FileHelper.Callback({
|
||||||
|
onSuccess(result) {
|
||||||
|
resolve();
|
||||||
|
},
|
||||||
|
onError(error) {
|
||||||
|
reject(error);
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return super.appendTextAsync(path, content, encoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
appendTextSync(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).appendTextSync(applicationContext, content, encoding ?? null, callback);
|
||||||
|
} else {
|
||||||
|
super.appendTextSync(path, content, onError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public readText = this.readTextSync.bind(this);
|
public readText = this.readTextSync.bind(this);
|
||||||
|
|
||||||
readTextAsync(path: string, encoding?: any): Promise<string> {
|
readTextAsync(path: string, encoding?: any): Promise<string> {
|
||||||
|
@ -2,6 +2,55 @@
|
|||||||
* 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 interface IFileSystemAccess {
|
export interface IFileSystemAccess {
|
||||||
|
/**
|
||||||
|
* Appends binary to a file with a given path.
|
||||||
|
* @param path The path to the source file.
|
||||||
|
* @param content The content which will be written to the file.
|
||||||
|
* @param onError (optional) A callback function to use if any error occurs.
|
||||||
|
*/
|
||||||
|
append(path: string, content: any, onError?: (error: any) => any);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends binary to a file with a given path.
|
||||||
|
* @param path The path to the source file.
|
||||||
|
* @param content The content which will be written to the file.
|
||||||
|
*/
|
||||||
|
appendAsync(path: string, content: any): Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends a binary to a file with a given path.
|
||||||
|
* @param path The path to the source file.
|
||||||
|
* @param content The content which will be written to the file.
|
||||||
|
* @param onError (optional) A callback function to use if any error occurs.
|
||||||
|
*/
|
||||||
|
appendSync(path: string, content: any, onError?: (error: any) => any);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends text to a file with a given path.
|
||||||
|
* @param path The path to the source file.
|
||||||
|
* @param content The content which will be written to the file.
|
||||||
|
* @param onError (optional) A callback function to use if any error occurs.
|
||||||
|
* @param encoding (optional) If set writes the text with the specified encoding (default UTF-8).
|
||||||
|
*/
|
||||||
|
appendText(path: string, content: string, onError?: (error: any) => any, encoding?: any);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends text to a file with a given path.
|
||||||
|
* @param path The path to the source file.
|
||||||
|
* @param content The content which will be written to the file.
|
||||||
|
* @param encoding (optional) If set writes the text with the specified encoding (default UTF-8).
|
||||||
|
*/
|
||||||
|
appendTextAsync(path: string, content: string, encoding?: any): Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends text to a file with a given path.
|
||||||
|
* @param path The path to the source file.
|
||||||
|
* @param content The content which will be written to the file.
|
||||||
|
* @param onError (optional) A callback function to use if any error occurs.
|
||||||
|
* @param encoding (optional) If set writes the text with the specified encoding (default UTF-8).
|
||||||
|
*/
|
||||||
|
appendTextSync(path: string, content: string, onError?: (error: any) => any, encoding?: any);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copies a file to a given path.
|
* Copies a file to a given path.
|
||||||
* @param src The path to the source file.
|
* @param src The path to the source file.
|
||||||
@ -282,6 +331,24 @@ export interface IFileSystemAccess {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class FileSystemAccess implements IFileSystemAccess {
|
export class FileSystemAccess implements IFileSystemAccess {
|
||||||
|
appendBuffer(path: string, content: ArrayBuffer | Uint8Array | Uint8ClampedArray, onError?: (error: any) => any);
|
||||||
|
|
||||||
|
appendBufferAsync(path: string, content: ArrayBuffer | Uint8Array | Uint8ClampedArray): Promise<void>;
|
||||||
|
|
||||||
|
appendBufferSync(path: string, content: ArrayBuffer | Uint8Array | Uint8ClampedArray, onError?: (error: any) => any);
|
||||||
|
|
||||||
|
append(path: string, content: any, onError?: (error: any) => any);
|
||||||
|
|
||||||
|
appendAsync(path: string, content: any): Promise<void>;
|
||||||
|
|
||||||
|
appendSync(path: string, content: any, onError?: (error: any) => any);
|
||||||
|
|
||||||
|
appendText(path: string, content: string, onError?: (error: any) => any, encoding?: any);
|
||||||
|
|
||||||
|
appendTextAsync(path: string, content: string, encoding?: any): Promise<void>;
|
||||||
|
|
||||||
|
appendTextSync(path: string, content: string, onError?: (error: any) => any, encoding?: any);
|
||||||
|
|
||||||
copy(src: string, dest: string, onError?: (error: any) => any): boolean;
|
copy(src: string, dest: string, onError?: (error: any) => any): boolean;
|
||||||
|
|
||||||
copySync(src: string, dest: string, onError?: (error: any) => any): boolean;
|
copySync(src: string, dest: string, onError?: (error: any) => any): boolean;
|
||||||
|
@ -445,6 +445,115 @@ export class FileSystemAccess {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public appendBuffer = this.appendBufferSync.bind(this);
|
||||||
|
|
||||||
|
public appendBufferAsync(path: string, content: ArrayBuffer | Uint8Array | Uint8ClampedArray): Promise<void> {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const handle = NSFileHandle.fileHandleForWritingAtPath(path);
|
||||||
|
(handle as any).appendDataCompletion(FileSystemAccess.getBuffer(content), (error) => {
|
||||||
|
if (error) {
|
||||||
|
reject(error);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
handle.closeFile();
|
||||||
|
});
|
||||||
|
} catch (ex) {
|
||||||
|
reject(new Error("Failed to write file at path '" + path + "': " + ex));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public appendBufferSync(path: string, content: ArrayBuffer | Uint8Array | Uint8ClampedArray, onError?: (error: any) => any) {
|
||||||
|
try {
|
||||||
|
const handle = NSFileHandle.fileHandleForWritingAtPath(path);
|
||||||
|
handle.seekToEndOfFile();
|
||||||
|
handle.writeData(FileSystemAccess.getBuffer(content));
|
||||||
|
handle.closeFile();
|
||||||
|
} catch (ex) {
|
||||||
|
if (onError) {
|
||||||
|
onError(new Error("Failed to write to file '" + path + "': " + ex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public append = this.appendSync.bind(this);
|
||||||
|
|
||||||
|
public appendAsync(path: string, content: NSData): Promise<void> {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const handle = NSFileHandle.fileHandleForWritingAtPath(path);
|
||||||
|
(handle as any).appendDataCompletion(content, (error) => {
|
||||||
|
if (error) {
|
||||||
|
reject(error);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
handle.closeFile();
|
||||||
|
});
|
||||||
|
} catch (ex) {
|
||||||
|
reject(new Error("Failed to write file at path '" + path + "': " + ex));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public appendSync(path: string, content: NSData, onError?: (error: any) => any) {
|
||||||
|
try {
|
||||||
|
const handle = NSFileHandle.fileHandleForWritingAtPath(path);
|
||||||
|
handle.seekToEndOfFile();
|
||||||
|
handle.writeData(content);
|
||||||
|
handle.closeFile();
|
||||||
|
} catch (ex) {
|
||||||
|
if (onError) {
|
||||||
|
onError(new Error("Failed to write to file '" + path + "': " + ex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public appendText = this.appendTextSync.bind(this);
|
||||||
|
|
||||||
|
public appendTextAsync(path: string, content: string, encoding?: any): Promise<void> {
|
||||||
|
const nsString = NSString.stringWithString(content);
|
||||||
|
const actualEncoding = encoding || textEncoding.UTF_8;
|
||||||
|
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const data = nsString.dataUsingEncoding(actualEncoding);
|
||||||
|
const handle = NSFileHandle.fileHandleForWritingAtPath(path);
|
||||||
|
(handle as any).appendDataCompletion(data, (error) => {
|
||||||
|
if (error) {
|
||||||
|
reject(error);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
handle.closeFile();
|
||||||
|
});
|
||||||
|
} catch (ex) {
|
||||||
|
reject(new Error("Failed to append file at path '" + path + "': " + ex));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public appendTextSync(path: string, content: string, onError?: (error: any) => any, encoding?: any) {
|
||||||
|
const nsString = NSString.stringWithString(content);
|
||||||
|
|
||||||
|
const actualEncoding = encoding || textEncoding.UTF_8;
|
||||||
|
|
||||||
|
// TODO: verify the useAuxiliaryFile parameter should be false
|
||||||
|
try {
|
||||||
|
const data = nsString.dataUsingEncoding(actualEncoding);
|
||||||
|
const handle = NSFileHandle.fileHandleForWritingAtPath(path);
|
||||||
|
handle.seekToEndOfFile();
|
||||||
|
handle.writeData(data);
|
||||||
|
handle.closeFile();
|
||||||
|
} catch (ex) {
|
||||||
|
if (onError) {
|
||||||
|
onError(new Error("Failed to append to file '" + path + "': " + ex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public writeBuffer = this.writeBufferSync.bind(this);
|
public writeBuffer = this.writeBufferSync.bind(this);
|
||||||
|
|
||||||
public writeBufferAsync(path: string, content: ArrayBuffer | Uint8Array | Uint8ClampedArray): Promise<void> {
|
public writeBufferAsync(path: string, content: ArrayBuffer | Uint8Array | Uint8ClampedArray): Promise<void> {
|
||||||
|
60
packages/core/file-system/index.d.ts
vendored
60
packages/core/file-system/index.d.ts
vendored
@ -1,5 +1,20 @@
|
|||||||
import { FileSystemAccess } from './file-system-access';
|
import { FileSystemAccess } from './file-system-access';
|
||||||
|
|
||||||
|
export enum AndroidDirectory {
|
||||||
|
ALARMS,
|
||||||
|
AUDIOBOOKS,
|
||||||
|
DCIM,
|
||||||
|
DOCUMENTS,
|
||||||
|
DOWNLOADS,
|
||||||
|
MOVIES,
|
||||||
|
MUSIC,
|
||||||
|
NOTIFICATIONS,
|
||||||
|
PICTURES,
|
||||||
|
PODCASTS,
|
||||||
|
RINGTONES,
|
||||||
|
SCREENSHOTS,
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
@ -55,10 +70,27 @@ export class FileSystemEntity {
|
|||||||
renameSync(newName: string, onError?: (error: any) => any): void;
|
renameSync(newName: string, onError?: (error: any) => any): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains Android-specific the file system helpers.
|
||||||
|
*/
|
||||||
|
class Android {
|
||||||
|
createFile(options: { relativePath?: string; name: string; mime: string; directory: AndroidDirectory }): File;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains iOS-specific the file system helpers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class iOS {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a File entity on the file system.
|
* Represents a File entity on the file system.
|
||||||
*/
|
*/
|
||||||
export class File extends FileSystemEntity {
|
export class File extends FileSystemEntity {
|
||||||
|
static readonly android: Android;
|
||||||
|
|
||||||
|
static readonly ios: iOS;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether a File with the specified path already exists.
|
* Checks whether a File with the specified path already exists.
|
||||||
* @param path The path to check for.
|
* @param path The path to check for.
|
||||||
@ -80,6 +112,34 @@ export class File extends FileSystemEntity {
|
|||||||
*/
|
*/
|
||||||
isLocked: boolean;
|
isLocked: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends the provided string to the file, using the specified encoding (defaults to UTF-8).
|
||||||
|
* @param content The content to be saved to the file.
|
||||||
|
* @param encoding An optional value specifying the preferred encoding (defaults to UTF-8).
|
||||||
|
*/
|
||||||
|
appendText(content: string, encoding?: string): Promise<any>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends the provided string to the file synchronously, using the specified encoding (defaults to UTF-8).
|
||||||
|
* @param content The content to be saved to the file.
|
||||||
|
* @param onError An optional function to be called if some IO-error occurs.
|
||||||
|
* @param encoding An optional value specifying the preferred encoding (defaults to UTF-8).
|
||||||
|
*/
|
||||||
|
appendTextSync(content: string, onError?: (error: any) => any, encoding?: string): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends the provided binary content to the file.
|
||||||
|
* @param content The binary content to be saved to the file.
|
||||||
|
*/
|
||||||
|
append(content: any): Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends the provided binary content to the file synchronously.
|
||||||
|
* @param content The binary content to be saved to the file.
|
||||||
|
* @param onError An optional function to be called if some IO-error occurs.
|
||||||
|
*/
|
||||||
|
appendSync(content: any, onError?: (error: any) => any): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copies a file to a given path.
|
* Copies a file to a given path.
|
||||||
* @param dest The path to the destination file.
|
* @param dest The path to the destination file.
|
||||||
|
@ -190,7 +190,136 @@ function getApplicationContext() {
|
|||||||
return applicationContext;
|
return applicationContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum AndroidDirectory {
|
||||||
|
ALARMS = 'alarms',
|
||||||
|
AUDIOBOOKS = 'audiobooks',
|
||||||
|
DCIM = 'dcim',
|
||||||
|
DOCUMENTS = 'documents',
|
||||||
|
DOWNLOADS = 'downloads',
|
||||||
|
MOVIES = 'movies',
|
||||||
|
MUSIC = 'music',
|
||||||
|
NOTIFICATIONS = 'notifications',
|
||||||
|
PICTURES = 'pictures',
|
||||||
|
PODCASTS = 'podcasts',
|
||||||
|
RINGTONES = 'ringtones',
|
||||||
|
SCREENSHOTS = 'screenshots',
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAndroidDirectory(value: AndroidDirectory): { path: string; column: android.net.Uri } | null {
|
||||||
|
switch (value) {
|
||||||
|
case AndroidDirectory.ALARMS:
|
||||||
|
return {
|
||||||
|
path: android.os.Environment.DIRECTORY_ALARMS,
|
||||||
|
column: android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
|
||||||
|
};
|
||||||
|
case AndroidDirectory.AUDIOBOOKS:
|
||||||
|
return {
|
||||||
|
path: android.os.Environment.DIRECTORY_AUDIOBOOKS,
|
||||||
|
column: android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
|
||||||
|
};
|
||||||
|
case AndroidDirectory.DCIM:
|
||||||
|
return {
|
||||||
|
path: android.os.Environment.DIRECTORY_DCIM,
|
||||||
|
column: android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
|
||||||
|
};
|
||||||
|
case AndroidDirectory.DOCUMENTS:
|
||||||
|
return {
|
||||||
|
path: android.os.Environment.DIRECTORY_DOCUMENTS,
|
||||||
|
column: android.provider.MediaStore.Files.getContentUri('external'),
|
||||||
|
};
|
||||||
|
case AndroidDirectory.DOWNLOADS:
|
||||||
|
return {
|
||||||
|
path: android.os.Environment.DIRECTORY_DOWNLOADS,
|
||||||
|
column: android.provider.MediaStore.Downloads.EXTERNAL_CONTENT_URI,
|
||||||
|
};
|
||||||
|
case AndroidDirectory.MOVIES:
|
||||||
|
return {
|
||||||
|
path: android.os.Environment.DIRECTORY_MOVIES,
|
||||||
|
column: android.provider.MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
|
||||||
|
};
|
||||||
|
case AndroidDirectory.MUSIC:
|
||||||
|
return {
|
||||||
|
path: android.os.Environment.DIRECTORY_MUSIC,
|
||||||
|
column: android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
|
||||||
|
};
|
||||||
|
case AndroidDirectory.NOTIFICATIONS:
|
||||||
|
return {
|
||||||
|
path: android.os.Environment.DIRECTORY_NOTIFICATIONS,
|
||||||
|
column: android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
|
||||||
|
};
|
||||||
|
case AndroidDirectory.PICTURES:
|
||||||
|
return {
|
||||||
|
path: android.os.Environment.DIRECTORY_PICTURES,
|
||||||
|
column: android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
|
||||||
|
};
|
||||||
|
case AndroidDirectory.PODCASTS:
|
||||||
|
return {
|
||||||
|
path: android.os.Environment.DIRECTORY_PODCASTS,
|
||||||
|
column: android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
|
||||||
|
};
|
||||||
|
case AndroidDirectory.RINGTONES:
|
||||||
|
return {
|
||||||
|
path: android.os.Environment.DIRECTORY_RINGTONES,
|
||||||
|
column: android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
|
||||||
|
};
|
||||||
|
case AndroidDirectory.SCREENSHOTS:
|
||||||
|
return {
|
||||||
|
path: android.os.Environment.DIRECTORY_SCREENSHOTS,
|
||||||
|
column: android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Android {
|
||||||
|
createFile(options: { relativePath?: string; name: string; mime: string; directory: AndroidDirectory }): File {
|
||||||
|
if (!global.isAndroid) {
|
||||||
|
throw new Error(`createFile is available on Android only!`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const context = getApplicationContext() as android.content.Context;
|
||||||
|
|
||||||
|
const meta = new android.content.ContentValues();
|
||||||
|
meta.put(android.provider.MediaStore.MediaColumns.DISPLAY_NAME, options.name);
|
||||||
|
meta.put(android.provider.MediaStore.MediaColumns.MIME_TYPE, options.mime);
|
||||||
|
//meta.put(android.provider.MediaStore.MediaColumns.DATE_ADDED, java.lang.System.currentTimeMillis() as any);
|
||||||
|
|
||||||
|
const externalDirectory = getAndroidDirectory(options.directory);
|
||||||
|
|
||||||
|
if (SDK_VERSION >= 29) {
|
||||||
|
const relativePath = options?.relativePath ? `/${options.relativePath}` : '';
|
||||||
|
meta.put(android.provider.MediaStore.MediaColumns.RELATIVE_PATH, `${externalDirectory.path}${relativePath}`);
|
||||||
|
// todo
|
||||||
|
// meta.put(android.provider.MediaStore.MediaColumns.IS_PENDING, java.lang.Integer.valueOf(1));
|
||||||
|
} else {
|
||||||
|
const relativePath = options?.relativePath ? `${options.relativePath}/` : '';
|
||||||
|
const directory = android.os.Environment.getExternalStoragePublicDirectory(externalDirectory.path);
|
||||||
|
const file = new java.io.File(directory, `${relativePath}${options.name}`);
|
||||||
|
meta.put(android.provider.MediaStore.MediaColumns.DATA, file.getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
const uri = context.getContentResolver().insert(externalDirectory.column, meta);
|
||||||
|
|
||||||
|
return File.fromPath(uri.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ad = new Android();
|
||||||
|
|
||||||
|
class iOS {}
|
||||||
|
|
||||||
|
const ios = new iOS();
|
||||||
|
|
||||||
export class File extends FileSystemEntity {
|
export class File extends FileSystemEntity {
|
||||||
|
public static get ios() {
|
||||||
|
return ios;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static get android() {
|
||||||
|
return ad;
|
||||||
|
}
|
||||||
|
|
||||||
public static fromPath(path: string, copy: boolean = false) {
|
public static fromPath(path: string, copy: boolean = false) {
|
||||||
const onError = function (error) {
|
const onError = function (error) {
|
||||||
throw error;
|
throw error;
|
||||||
@ -239,6 +368,100 @@ export class File extends FileSystemEntity {
|
|||||||
return getFileAccess().getFileSize(this.path);
|
return getFileAccess().getFileSize(this.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public append(content: any): Promise<void> {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
this._checkAccess();
|
||||||
|
} catch (ex) {
|
||||||
|
reject(ex);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._locked = true;
|
||||||
|
|
||||||
|
getFileAccess()
|
||||||
|
.appendAsync(this.path, content)
|
||||||
|
.then(
|
||||||
|
() => {
|
||||||
|
resolve();
|
||||||
|
this._locked = false;
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
reject(error);
|
||||||
|
this._locked = false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public appendSync(content: any, onError?: (error: any) => any): void {
|
||||||
|
this._checkAccess();
|
||||||
|
|
||||||
|
try {
|
||||||
|
this._locked = true;
|
||||||
|
|
||||||
|
const that = this;
|
||||||
|
const localError = function (error) {
|
||||||
|
that._locked = false;
|
||||||
|
if (onError) {
|
||||||
|
onError(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
getFileAccess().appendSync(this.path, content, localError);
|
||||||
|
} finally {
|
||||||
|
this._locked = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public appendText(content: string, encoding?: string): Promise<any> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
this._checkAccess();
|
||||||
|
} catch (ex) {
|
||||||
|
reject(ex);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._locked = true;
|
||||||
|
|
||||||
|
getFileAccess()
|
||||||
|
.appendTextAsync(this.path, content, encoding)
|
||||||
|
.then(
|
||||||
|
() => {
|
||||||
|
resolve(true);
|
||||||
|
this._locked = false;
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
reject(error);
|
||||||
|
this._locked = false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public appendTextSync(content: string, onError?: (error: any) => any, encoding?: string): void {
|
||||||
|
this._checkAccess();
|
||||||
|
|
||||||
|
try {
|
||||||
|
this._locked = true;
|
||||||
|
|
||||||
|
const that = this;
|
||||||
|
const localError = function (error) {
|
||||||
|
that._locked = false;
|
||||||
|
if (onError) {
|
||||||
|
onError(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
getFileAccess().appendTextSync(this.path, content, localError, encoding);
|
||||||
|
} finally {
|
||||||
|
this._locked = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public copy(dest: string): Promise<boolean> {
|
public copy(dest: string): Promise<boolean> {
|
||||||
return new Promise<boolean>((resolve, reject) => {
|
return new Promise<boolean>((resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
|
2
packages/core/index.d.ts
vendored
2
packages/core/index.d.ts
vendored
@ -85,7 +85,7 @@ export { Observable, WrappedValue, fromObject, fromObjectRecursive } from './dat
|
|||||||
export type { PropertyChangeData, EventData } from './data/observable';
|
export type { PropertyChangeData, EventData } from './data/observable';
|
||||||
export { VirtualArray } from './data/virtual-array';
|
export { VirtualArray } from './data/virtual-array';
|
||||||
export type { ItemsLoading } from './data/virtual-array';
|
export type { ItemsLoading } from './data/virtual-array';
|
||||||
export { File, FileSystemEntity, Folder, knownFolders, path, getFileAccess } from './file-system';
|
export { File, FileSystemEntity, Folder, knownFolders, path, getFileAccess, AndroidDirectory } from './file-system';
|
||||||
export type { HttpRequestOptions, HttpResponse, Headers, HttpResponseEncoding, HttpContent } from './http';
|
export type { HttpRequestOptions, HttpResponse, Headers, HttpResponseEncoding, HttpContent } from './http';
|
||||||
import { getFile, getImage, getJSON, getString as httpGetString } from './http';
|
import { getFile, getImage, getJSON, getString as httpGetString } from './http';
|
||||||
export declare const Http: {
|
export declare const Http: {
|
||||||
|
@ -105,7 +105,7 @@ export { Observable, WrappedValue, fromObject, fromObjectRecursive } from './dat
|
|||||||
export type { PropertyChangeData, EventData } from './data/observable';
|
export type { PropertyChangeData, EventData } from './data/observable';
|
||||||
export { VirtualArray } from './data/virtual-array';
|
export { VirtualArray } from './data/virtual-array';
|
||||||
export type { ItemsLoading } from './data/virtual-array';
|
export type { ItemsLoading } from './data/virtual-array';
|
||||||
export { File, FileSystemEntity, Folder, knownFolders, path, getFileAccess } from './file-system';
|
export { File, FileSystemEntity, Folder, knownFolders, path, getFileAccess, AndroidDirectory } from './file-system';
|
||||||
|
|
||||||
// Export all interfaces from "http" module
|
// Export all interfaces from "http" module
|
||||||
export type { HttpRequestOptions, HttpResponse, Headers, HttpResponseEncoding, HttpContent } from './http';
|
export type { HttpRequestOptions, HttpResponse, Headers, HttpResponseEncoding, HttpContent } from './http';
|
||||||
|
Binary file not shown.
@ -4,6 +4,23 @@
|
|||||||
<dict>
|
<dict>
|
||||||
<key>AvailableLibraries</key>
|
<key>AvailableLibraries</key>
|
||||||
<array>
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>DebugSymbolsPath</key>
|
||||||
|
<string>dSYMs</string>
|
||||||
|
<key>LibraryIdentifier</key>
|
||||||
|
<string>ios-arm64_x86_64-maccatalyst</string>
|
||||||
|
<key>LibraryPath</key>
|
||||||
|
<string>TNSWidgets.framework</string>
|
||||||
|
<key>SupportedArchitectures</key>
|
||||||
|
<array>
|
||||||
|
<string>arm64</string>
|
||||||
|
<string>x86_64</string>
|
||||||
|
</array>
|
||||||
|
<key>SupportedPlatform</key>
|
||||||
|
<string>ios</string>
|
||||||
|
<key>SupportedPlatformVariant</key>
|
||||||
|
<string>maccatalyst</string>
|
||||||
|
</dict>
|
||||||
<dict>
|
<dict>
|
||||||
<key>DebugSymbolsPath</key>
|
<key>DebugSymbolsPath</key>
|
||||||
<string>dSYMs</string>
|
<string>dSYMs</string>
|
||||||
@ -35,23 +52,6 @@
|
|||||||
<key>SupportedPlatformVariant</key>
|
<key>SupportedPlatformVariant</key>
|
||||||
<string>simulator</string>
|
<string>simulator</string>
|
||||||
</dict>
|
</dict>
|
||||||
<dict>
|
|
||||||
<key>DebugSymbolsPath</key>
|
|
||||||
<string>dSYMs</string>
|
|
||||||
<key>LibraryIdentifier</key>
|
|
||||||
<string>ios-arm64_x86_64-maccatalyst</string>
|
|
||||||
<key>LibraryPath</key>
|
|
||||||
<string>TNSWidgets.framework</string>
|
|
||||||
<key>SupportedArchitectures</key>
|
|
||||||
<array>
|
|
||||||
<string>arm64</string>
|
|
||||||
<string>x86_64</string>
|
|
||||||
</array>
|
|
||||||
<key>SupportedPlatform</key>
|
|
||||||
<string>ios</string>
|
|
||||||
<key>SupportedPlatformVariant</key>
|
|
||||||
<string>maccatalyst</string>
|
|
||||||
</dict>
|
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>XFWK</string>
|
<string>XFWK</string>
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
//
|
||||||
|
// NSFileHandle+Async.h
|
||||||
|
// TNSWidgets
|
||||||
|
//
|
||||||
|
// Created by Osei Fortune on 03/05/2023.
|
||||||
|
// Copyright © 2023 Telerik A D. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface NSFileHandle (Async)
|
||||||
|
|
||||||
|
- (void)appendData:(nonnull NSData*) data
|
||||||
|
completion:(void (^) (NSError*))callback;
|
||||||
|
|
||||||
|
+ (void)fileHandleWith:(NSString *)path data:(NSData *)data completion:(void (^)(NSFileHandle*,NSError*))callback;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
@ -22,3 +22,4 @@ FOUNDATION_EXPORT const unsigned char TNSWidgetsVersionString[];
|
|||||||
#import "TNSProcess.h"
|
#import "TNSProcess.h"
|
||||||
#import "NSString+Async.h"
|
#import "NSString+Async.h"
|
||||||
#import "NSData+Async.h"
|
#import "NSData+Async.h"
|
||||||
|
#import "NSFileHandle+Async.h"
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,22 @@
|
|||||||
|
//
|
||||||
|
// NSFileHandle+Async.h
|
||||||
|
// TNSWidgets
|
||||||
|
//
|
||||||
|
// Created by Osei Fortune on 03/05/2023.
|
||||||
|
// Copyright © 2023 Telerik A D. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface NSFileHandle (Async)
|
||||||
|
|
||||||
|
- (void)appendData:(nonnull NSData*) data
|
||||||
|
completion:(void (^) (NSError*))callback;
|
||||||
|
|
||||||
|
+ (void)fileHandleWith:(NSString *)path data:(NSData *)data completion:(void (^)(NSFileHandle*,NSError*))callback;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
@ -22,3 +22,4 @@ FOUNDATION_EXPORT const unsigned char TNSWidgetsVersionString[];
|
|||||||
#import "TNSProcess.h"
|
#import "TNSProcess.h"
|
||||||
#import "NSString+Async.h"
|
#import "NSString+Async.h"
|
||||||
#import "NSData+Async.h"
|
#import "NSData+Async.h"
|
||||||
|
#import "NSFileHandle+Async.h"
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>BuildMachineOSBuild</key>
|
<key>BuildMachineOSBuild</key>
|
||||||
<string>21G72</string>
|
<string>22D68</string>
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>en</string>
|
<string>en</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
@ -29,7 +29,7 @@
|
|||||||
<key>DTCompiler</key>
|
<key>DTCompiler</key>
|
||||||
<string>com.apple.compilers.llvm.clang.1_0</string>
|
<string>com.apple.compilers.llvm.clang.1_0</string>
|
||||||
<key>DTPlatformBuild</key>
|
<key>DTPlatformBuild</key>
|
||||||
<string>13F100</string>
|
<string>14A309</string>
|
||||||
<key>DTPlatformName</key>
|
<key>DTPlatformName</key>
|
||||||
<string>macosx</string>
|
<string>macosx</string>
|
||||||
<key>DTPlatformVersion</key>
|
<key>DTPlatformVersion</key>
|
||||||
@ -39,9 +39,9 @@
|
|||||||
<key>DTSDKName</key>
|
<key>DTSDKName</key>
|
||||||
<string>macosx12.3</string>
|
<string>macosx12.3</string>
|
||||||
<key>DTXcode</key>
|
<key>DTXcode</key>
|
||||||
<string>1341</string>
|
<string>1400</string>
|
||||||
<key>DTXcodeBuild</key>
|
<key>DTXcodeBuild</key>
|
||||||
<string>13F100</string>
|
<string>14A309</string>
|
||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
<string>10.15</string>
|
<string>10.15</string>
|
||||||
<key>UIDeviceFamily</key>
|
<key>UIDeviceFamily</key>
|
||||||
|
Binary file not shown.
Binary file not shown.
@ -0,0 +1,22 @@
|
|||||||
|
//
|
||||||
|
// NSFileHandle+Async.h
|
||||||
|
// TNSWidgets
|
||||||
|
//
|
||||||
|
// Created by Osei Fortune on 03/05/2023.
|
||||||
|
// Copyright © 2023 Telerik A D. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface NSFileHandle (Async)
|
||||||
|
|
||||||
|
- (void)appendData:(nonnull NSData*) data
|
||||||
|
completion:(void (^) (NSError*))callback;
|
||||||
|
|
||||||
|
+ (void)fileHandleWith:(NSString *)path data:(NSData *)data completion:(void (^)(NSFileHandle*,NSError*))callback;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
@ -22,3 +22,4 @@ FOUNDATION_EXPORT const unsigned char TNSWidgetsVersionString[];
|
|||||||
#import "TNSProcess.h"
|
#import "TNSProcess.h"
|
||||||
#import "NSString+Async.h"
|
#import "NSString+Async.h"
|
||||||
#import "NSData+Async.h"
|
#import "NSData+Async.h"
|
||||||
|
#import "NSFileHandle+Async.h"
|
||||||
|
Binary file not shown.
Binary file not shown.
@ -8,6 +8,10 @@
|
|||||||
<data>
|
<data>
|
||||||
9BsuK8QsA57YnHHgpWIgaHygSEk=
|
9BsuK8QsA57YnHHgpWIgaHygSEk=
|
||||||
</data>
|
</data>
|
||||||
|
<key>Headers/NSFileHandle+Async.h</key>
|
||||||
|
<data>
|
||||||
|
iQtzsDHw/VTnFG27yW+QvGCeXGw=
|
||||||
|
</data>
|
||||||
<key>Headers/NSString+Async.h</key>
|
<key>Headers/NSString+Async.h</key>
|
||||||
<data>
|
<data>
|
||||||
o8366y9zYMOVyObC3vtKKy/8jxA=
|
o8366y9zYMOVyObC3vtKKy/8jxA=
|
||||||
@ -22,7 +26,7 @@
|
|||||||
</data>
|
</data>
|
||||||
<key>Headers/TNSWidgets.h</key>
|
<key>Headers/TNSWidgets.h</key>
|
||||||
<data>
|
<data>
|
||||||
gUvu5bjZg5Aie5iJ1krxFmDrHwk=
|
ZFbCov7mFiXa4ZA/gAxqYHzRX7Q=
|
||||||
</data>
|
</data>
|
||||||
<key>Headers/UIImage+TNSBlocks.h</key>
|
<key>Headers/UIImage+TNSBlocks.h</key>
|
||||||
<data>
|
<data>
|
||||||
@ -34,7 +38,7 @@
|
|||||||
</data>
|
</data>
|
||||||
<key>Info.plist</key>
|
<key>Info.plist</key>
|
||||||
<data>
|
<data>
|
||||||
RCfacSfbjEiH19hn34SSJzGdiFo=
|
myeIggkwNuTIqt7xlMgf8VGsKZ8=
|
||||||
</data>
|
</data>
|
||||||
<key>Modules/module.modulemap</key>
|
<key>Modules/module.modulemap</key>
|
||||||
<data>
|
<data>
|
||||||
@ -58,6 +62,13 @@
|
|||||||
JdjuVUBed00Ged4cSDzYLXONUlESu+dae9KN0PYJ/nM=
|
JdjuVUBed00Ged4cSDzYLXONUlESu+dae9KN0PYJ/nM=
|
||||||
</data>
|
</data>
|
||||||
</dict>
|
</dict>
|
||||||
|
<key>Headers/NSFileHandle+Async.h</key>
|
||||||
|
<dict>
|
||||||
|
<key>hash2</key>
|
||||||
|
<data>
|
||||||
|
1IqS81dD1dbKccHZ0lYPMMF1zBPdp3InM+rdOFjBo+k=
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
<key>Headers/NSString+Async.h</key>
|
<key>Headers/NSString+Async.h</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>hash2</key>
|
<key>hash2</key>
|
||||||
@ -83,7 +94,7 @@
|
|||||||
<dict>
|
<dict>
|
||||||
<key>hash2</key>
|
<key>hash2</key>
|
||||||
<data>
|
<data>
|
||||||
V99t2zLwRPOs90tbGiQbhbdAFJlW7mp7X2R5337ewUA=
|
6L3xStgKRHkPvDZAHAUF+EMI3Skf5jvnoA4ED/WbB1w=
|
||||||
</data>
|
</data>
|
||||||
</dict>
|
</dict>
|
||||||
<key>Headers/UIImage+TNSBlocks.h</key>
|
<key>Headers/UIImage+TNSBlocks.h</key>
|
||||||
|
Binary file not shown.
@ -39,6 +39,9 @@
|
|||||||
export function writeText(path: string, content: string, encoding: string, callback: CompleteCallback, context: any);
|
export function writeText(path: string, content: string, encoding: string, callback: CompleteCallback, context: any);
|
||||||
export function write(path: string, content: androidNative.Array<number>, callback: CompleteCallback, context: any);
|
export function write(path: string, content: androidNative.Array<number>, callback: CompleteCallback, context: any);
|
||||||
export function writeBuffer(param0: string, param1: java.nio.ByteBuffer, param2: org.nativescript.widgets.Async.CompleteCallback, param3: any): void;
|
export function writeBuffer(param0: string, param1: java.nio.ByteBuffer, param2: org.nativescript.widgets.Async.CompleteCallback, param3: any): void;
|
||||||
|
export function append(path: string, content: androidNative.Array<number>, callback: CompleteCallback, context: any);
|
||||||
|
export function appendBuffer(param0: string, param1: java.nio.ByteBuffer, param2: org.nativescript.widgets.Async.CompleteCallback, param3: any): void;
|
||||||
|
export function appendText(path: string, content: string, encoding: string, callback: CompleteCallback, context: any);
|
||||||
}
|
}
|
||||||
|
|
||||||
export module Http {
|
export module Http {
|
||||||
@ -646,7 +649,13 @@ declare module org {
|
|||||||
export module widgets {
|
export module widgets {
|
||||||
export class FileHelper {
|
export class FileHelper {
|
||||||
public static class: java.lang.Class<org.nativescript.widgets.FileHelper>;
|
public static class: java.lang.Class<org.nativescript.widgets.FileHelper>;
|
||||||
|
public appendTextSync(param0: globalAndroid.content.Context, param1: string, param2: string, param3: org.nativescript.widgets.FileHelper.Callback): void;
|
||||||
|
public appendText(param0: globalAndroid.content.Context, param1: string, param2: string, param3: org.nativescript.widgets.FileHelper.Callback): void;
|
||||||
public readText(param0: globalAndroid.content.Context, param1: string, param2: org.nativescript.widgets.FileHelper.Callback): void;
|
public readText(param0: globalAndroid.content.Context, param1: string, param2: org.nativescript.widgets.FileHelper.Callback): void;
|
||||||
|
public appendSync(param0: globalAndroid.content.Context, param1: androidNative.Array<number>, param2: org.nativescript.widgets.FileHelper.Callback): void;
|
||||||
|
public append(param0: globalAndroid.content.Context, param1: androidNative.Array<number>, param2: org.nativescript.widgets.FileHelper.Callback): void;
|
||||||
|
public appendBufferSync(param0: globalAndroid.content.Context, param1: java.nio.ByteBuffer, param2: org.nativescript.widgets.FileHelper.Callback): void;
|
||||||
|
public appendBuffer(param0: globalAndroid.content.Context, param1: java.nio.ByteBuffer, param2: org.nativescript.widgets.FileHelper.Callback): void;
|
||||||
public writeBufferSync(param0: globalAndroid.content.Context, param1: java.nio.ByteBuffer, param2: org.nativescript.widgets.FileHelper.Callback): void;
|
public writeBufferSync(param0: globalAndroid.content.Context, param1: java.nio.ByteBuffer, param2: org.nativescript.widgets.FileHelper.Callback): void;
|
||||||
public writeTextSync(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 copyToFileSync(param0: globalAndroid.content.Context, param1: java.io.File, param2: org.nativescript.widgets.FileHelper.Callback): boolean;
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="org.nativescript.widgets">
|
package="org.nativescript.widgets">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="28"/>
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28"/>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true">
|
android:allowBackup="true">
|
||||||
|
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
package org.nativescript.widgets;
|
package org.nativescript.widgets;
|
||||||
|
|
||||||
|
import android.content.ContentValues;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
import android.graphics.drawable.BitmapDrawable;
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.util.Base64;
|
import android.util.Base64;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@ -599,19 +601,87 @@ public class Async {
|
|||||||
|
|
||||||
public static class File {
|
public static class File {
|
||||||
|
|
||||||
|
static void updateValue(Context context, Uri uri) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void append(final String path, final byte[] content, final CompleteCallback callback, final Object context) {
|
||||||
|
final android.os.Handler mHandler = new android.os.Handler(Looper.myLooper());
|
||||||
|
threadPoolExecutor().execute(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
final AppendTask task = new AppendTask(callback, context);
|
||||||
|
final boolean result = task.doInBackground(path, content);
|
||||||
|
mHandler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
task.onPostExecute(result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void appendBuffer(final String path, final ByteBuffer content, final CompleteCallback callback, final Object context) {
|
||||||
|
final android.os.Handler mHandler = new android.os.Handler(Looper.myLooper());
|
||||||
|
threadPoolExecutor().execute(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
final AppendBufferTask task = new AppendBufferTask(callback, context);
|
||||||
|
final boolean result = task.doInBackground(path, content);
|
||||||
|
mHandler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
task.onPostExecute(result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void appendText(final String path, final String content, final String encoding, final CompleteCallback callback, final Object context) {
|
||||||
|
final android.os.Handler mHandler = new android.os.Handler(Looper.myLooper());
|
||||||
|
threadPoolExecutor().execute(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
final AppendTextTask task = new AppendTextTask(callback, context);
|
||||||
|
final boolean result = task.doInBackground(path, content, encoding);
|
||||||
|
mHandler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
task.onPostExecute(result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean copySync(final String src, final String dest, final Context context) throws Exception {
|
public static boolean copySync(final String src, final String dest, final Context context) throws Exception {
|
||||||
InputStream is;
|
InputStream is;
|
||||||
OutputStream os;
|
OutputStream os;
|
||||||
|
boolean requiresUpdate = false;
|
||||||
if(src.startsWith("content://")){
|
if(src.startsWith("content://")){
|
||||||
is = context.getContentResolver().openInputStream(Uri.parse(src));
|
is = context.getContentResolver().openInputStream(Uri.parse(src));
|
||||||
}else is = new FileInputStream(new java.io.File(src));
|
}else is = new FileInputStream(new java.io.File(src));
|
||||||
|
|
||||||
if(dest.startsWith("content://")){
|
if(dest.startsWith("content://")){
|
||||||
os = context.getContentResolver().openOutputStream(Uri.parse(dest));
|
os = context.getContentResolver().openOutputStream(Uri.parse(dest));
|
||||||
|
requiresUpdate = true;
|
||||||
}else os = new FileOutputStream(new java.io.File(dest));
|
}else os = new FileOutputStream(new java.io.File(dest));
|
||||||
|
|
||||||
return copySync(is, os, context);
|
boolean ret = copySync(is, os, context);
|
||||||
|
|
||||||
|
if(ret && requiresUpdate){
|
||||||
|
updateValue(context, Uri.parse(dest));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean copySync(final InputStream src, final OutputStream dest, final Object context) throws Exception {
|
public static boolean copySync(final InputStream src, final OutputStream dest, final Object context) throws Exception {
|
||||||
@ -630,15 +700,33 @@ public class Async {
|
|||||||
InputStream is;
|
InputStream is;
|
||||||
OutputStream os;
|
OutputStream os;
|
||||||
|
|
||||||
|
boolean requiresUpdate = false;
|
||||||
|
|
||||||
if(src.startsWith("content://")){
|
if(src.startsWith("content://")){
|
||||||
is = context.getContentResolver().openInputStream(Uri.parse(src));
|
is = context.getContentResolver().openInputStream(Uri.parse(src));
|
||||||
}else is = new FileInputStream(new java.io.File(src));
|
}else is = new FileInputStream(new java.io.File(src));
|
||||||
|
|
||||||
if(dest.startsWith("content://")){
|
if(dest.startsWith("content://")){
|
||||||
|
requiresUpdate = true;
|
||||||
os = context.getContentResolver().openOutputStream(Uri.parse(dest));
|
os = context.getContentResolver().openOutputStream(Uri.parse(dest));
|
||||||
}else os = new FileOutputStream(new java.io.File(dest));
|
}else os = new FileOutputStream(new java.io.File(dest));
|
||||||
|
|
||||||
copy(is, os, callback, context);
|
boolean finalRequiresUpdate = requiresUpdate;
|
||||||
|
copy(is, os, new CompleteCallback() {
|
||||||
|
@Override
|
||||||
|
public void onComplete(Object result, Object tag) {
|
||||||
|
if(finalRequiresUpdate){
|
||||||
|
updateValue(context, Uri.parse(dest));
|
||||||
|
}
|
||||||
|
callback.onComplete(result, tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(String error, Object tag) {
|
||||||
|
callback.onError(error, tag);
|
||||||
|
}
|
||||||
|
}, context);
|
||||||
|
|
||||||
}catch (Exception exception){
|
}catch (Exception exception){
|
||||||
callback.onError(exception.getMessage(), context);
|
callback.onError(exception.getMessage(), context);
|
||||||
}
|
}
|
||||||
@ -666,11 +754,12 @@ public class Async {
|
|||||||
return written;
|
return written;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void copy(final InputStream src, final OutputStream dest, final CompleteCallback callback, final Object context) {
|
public static void copy(final InputStream src, final OutputStream dest, final CompleteCallback callback, final Object context) {
|
||||||
final android.os.Handler mHandler = new android.os.Handler(Looper.myLooper());
|
final android.os.Handler mHandler = new android.os.Handler(Looper.myLooper());
|
||||||
threadPoolExecutor().execute((Runnable) () -> {
|
threadPoolExecutor().execute((Runnable) () -> {
|
||||||
|
|
||||||
|
boolean done = false;
|
||||||
|
Exception error = null;
|
||||||
try (InputStream is = src; OutputStream os = dest){
|
try (InputStream is = src; OutputStream os = dest){
|
||||||
ReadableByteChannel isc = java.nio.channels.Channels.newChannel(is);
|
ReadableByteChannel isc = java.nio.channels.Channels.newChannel(is);
|
||||||
WritableByteChannel osc = java.nio.channels.Channels.newChannel(os);
|
WritableByteChannel osc = java.nio.channels.Channels.newChannel(os);
|
||||||
@ -679,10 +768,19 @@ public class Async {
|
|||||||
|
|
||||||
int written = fastChannelCopy(isc, osc);
|
int written = fastChannelCopy(isc, osc);
|
||||||
|
|
||||||
mHandler.post(() -> callback.onComplete(size == written, context));
|
done = size == written;
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
mHandler.post(() -> callback.onError(e.getMessage(), context));
|
error = e;
|
||||||
|
|
||||||
|
}finally {
|
||||||
|
if (error != null){
|
||||||
|
Exception finalError = error;
|
||||||
|
mHandler.post(() -> callback.onError(finalError.getMessage(), context));
|
||||||
|
}else {
|
||||||
|
boolean finalDone = done;
|
||||||
|
mHandler.post(() -> callback.onComplete(finalDone, context));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1086,5 +1184,146 @@ public class Async {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class AppendTask {
|
||||||
|
private final CompleteCallback callback;
|
||||||
|
private final Object context;
|
||||||
|
|
||||||
|
public AppendTask(CompleteCallback callback, Object context) {
|
||||||
|
this.callback = callback;
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean doInBackground(Object... params) {
|
||||||
|
java.io.File javaFile = new java.io.File((String) params[0]);
|
||||||
|
FileOutputStream stream = null;
|
||||||
|
byte[] content = (byte[]) params[1];
|
||||||
|
|
||||||
|
try {
|
||||||
|
stream = new FileOutputStream(javaFile, true);
|
||||||
|
stream.write(content, 0, content.length);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
Log.e(TAG, "Failed to append file, FileNotFoundException: " + e.getMessage());
|
||||||
|
return false;
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "Failed to write file, IOException: " + e.getMessage());
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
if (stream != null) {
|
||||||
|
try {
|
||||||
|
stream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "Failed to close stream, IOException: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onPostExecute(final boolean result) {
|
||||||
|
if (result) {
|
||||||
|
this.callback.onComplete(null, this.context);
|
||||||
|
} else {
|
||||||
|
this.callback.onError("AppendTask returns no result.", this.context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class AppendBufferTask {
|
||||||
|
private final CompleteCallback callback;
|
||||||
|
private final Object context;
|
||||||
|
|
||||||
|
public AppendBufferTask(CompleteCallback callback, Object context) {
|
||||||
|
this.callback = callback;
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean doInBackground(Object... params) {
|
||||||
|
java.io.File javaFile = new java.io.File((String) params[0]);
|
||||||
|
FileOutputStream stream = null;
|
||||||
|
ByteBuffer content = (ByteBuffer) params[1];
|
||||||
|
|
||||||
|
try {
|
||||||
|
stream = new FileOutputStream(javaFile, true);
|
||||||
|
FileChannel channel = stream.getChannel();
|
||||||
|
content.rewind();
|
||||||
|
channel.write(content);
|
||||||
|
content.rewind();
|
||||||
|
return true;
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
Log.e(TAG, "Failed to append to file, FileNotFoundException: " + e.getMessage());
|
||||||
|
return false;
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "Failed to append file, IOException: " + e.getMessage());
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
if (stream != null) {
|
||||||
|
try {
|
||||||
|
stream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "Failed to close stream, IOException: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onPostExecute(final boolean result) {
|
||||||
|
if (result) {
|
||||||
|
this.callback.onComplete(null, this.context);
|
||||||
|
} else {
|
||||||
|
this.callback.onError("AppendTask returns no result.", this.context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class AppendTextTask {
|
||||||
|
private final CompleteCallback callback;
|
||||||
|
private final Object context;
|
||||||
|
|
||||||
|
public AppendTextTask(CompleteCallback callback, Object context) {
|
||||||
|
this.callback = callback;
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean doInBackground(String... params) {
|
||||||
|
java.io.File javaFile = new java.io.File(params[0]);
|
||||||
|
FileOutputStream stream = null;
|
||||||
|
try {
|
||||||
|
stream = new FileOutputStream(javaFile, true);
|
||||||
|
|
||||||
|
OutputStreamWriter writer = new OutputStreamWriter(stream, params[2]);
|
||||||
|
writer.write(params[1]);
|
||||||
|
writer.close();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
Log.e(TAG, "Failed to append file, FileNotFoundException: " + e.getMessage());
|
||||||
|
return false;
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
Log.e(TAG, "Failed to append file, UnsupportedEncodingException: " + e.getMessage());
|
||||||
|
return false;
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "Failed to append file, IOException: " + e.getMessage());
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
if (stream != null) {
|
||||||
|
try {
|
||||||
|
stream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "Failed to close stream, IOException: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onPostExecute(final boolean result) {
|
||||||
|
if (result) {
|
||||||
|
this.callback.onComplete(null, this.context);
|
||||||
|
} else {
|
||||||
|
this.callback.onError("AppendTextTask returns no result.", this.context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ import java.io.InputStream;
|
|||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.channels.Channels;
|
import java.nio.channels.Channels;
|
||||||
import java.nio.channels.FileChannel;
|
import java.nio.channels.FileChannel;
|
||||||
@ -34,12 +35,13 @@ import java.util.concurrent.Executors;
|
|||||||
|
|
||||||
public class FileHelper {
|
public class FileHelper {
|
||||||
private Uri uri;
|
private Uri uri;
|
||||||
private long size;
|
|
||||||
private String name;
|
private String name;
|
||||||
private String mime;
|
private String mime;
|
||||||
private long lastModified;
|
private long lastModified;
|
||||||
private ExecutorService executor = Executors.newSingleThreadExecutor();
|
private final ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||||
private Handler handler;
|
private final Handler handler;
|
||||||
|
|
||||||
|
private WeakReference<Context> context;
|
||||||
|
|
||||||
public interface Callback {
|
public interface Callback {
|
||||||
void onError(Exception exception);
|
void onError(Exception exception);
|
||||||
@ -59,12 +61,15 @@ public class FileHelper {
|
|||||||
|
|
||||||
private static @Nullable
|
private static @Nullable
|
||||||
Cursor getCursor(Context context, Uri uri) {
|
Cursor getCursor(Context context, Uri uri) {
|
||||||
Cursor cursor = null;
|
return getCursor(context, uri, new String[]{
|
||||||
String[] projections = {
|
|
||||||
MediaStore.MediaColumns.SIZE,
|
|
||||||
MediaStore.MediaColumns.DISPLAY_NAME,
|
MediaStore.MediaColumns.DISPLAY_NAME,
|
||||||
MediaStore.MediaColumns.DATE_MODIFIED
|
MediaStore.MediaColumns.DATE_MODIFIED
|
||||||
};
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @Nullable
|
||||||
|
Cursor getCursor(Context context, Uri uri, String[] projections) {
|
||||||
|
Cursor cursor = null;
|
||||||
try {
|
try {
|
||||||
if (Build.VERSION.SDK_INT >= 19) {
|
if (Build.VERSION.SDK_INT >= 19) {
|
||||||
if (DocumentsContract.isDocumentUri(context, uri)) {
|
if (DocumentsContract.isDocumentUri(context, uri)) {
|
||||||
@ -115,7 +120,11 @@ public class FileHelper {
|
|||||||
public static @Nullable
|
public static @Nullable
|
||||||
FileHelper fromString(Context context, String string) {
|
FileHelper fromString(Context context, String string) {
|
||||||
try {
|
try {
|
||||||
return fromUri(context, Uri.parse(string));
|
FileHelper ret = fromUri(context, Uri.parse(string));
|
||||||
|
if (ret != null) {
|
||||||
|
ret.context = new WeakReference<>(context);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -166,10 +175,10 @@ public class FileHelper {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
FileHelper helper = new FileHelper(uri);
|
FileHelper helper = new FileHelper(uri);
|
||||||
helper.size = file.length();
|
|
||||||
helper.name = file.getName();
|
helper.name = file.getName();
|
||||||
helper.mime = context.getContentResolver().getType(uri);
|
helper.mime = context.getContentResolver().getType(uri);
|
||||||
helper.lastModified = file.lastModified() / 1000;
|
helper.lastModified = file.lastModified() / 1000;
|
||||||
|
helper.context = new WeakReference<>(context);
|
||||||
return helper;
|
return helper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,10 +191,6 @@ public class FileHelper {
|
|||||||
boolean moved = cursor.moveToFirst();
|
boolean moved = cursor.moveToFirst();
|
||||||
FileHelper helper = null;
|
FileHelper helper = null;
|
||||||
if (moved) {
|
if (moved) {
|
||||||
int sizeIndex = cursor.getColumnIndex(
|
|
||||||
MediaStore.MediaColumns.SIZE
|
|
||||||
);
|
|
||||||
|
|
||||||
int nameIndex = cursor.getColumnIndex(
|
int nameIndex = cursor.getColumnIndex(
|
||||||
MediaStore.MediaColumns.DISPLAY_NAME
|
MediaStore.MediaColumns.DISPLAY_NAME
|
||||||
);
|
);
|
||||||
@ -195,15 +200,51 @@ public class FileHelper {
|
|||||||
);
|
);
|
||||||
|
|
||||||
helper = new FileHelper(uri);
|
helper = new FileHelper(uri);
|
||||||
helper.size = cursor.getLong(sizeIndex);
|
|
||||||
helper.name = cursor.getString(nameIndex);
|
helper.name = cursor.getString(nameIndex);
|
||||||
helper.mime = context.getContentResolver().getType(uri);
|
helper.mime = context.getContentResolver().getType(uri);
|
||||||
helper.lastModified = cursor.getLong(lastModifiedIndex);
|
helper.lastModified = cursor.getLong(lastModifiedIndex);
|
||||||
|
helper.context = new WeakReference<>(context);
|
||||||
}
|
}
|
||||||
cursor.close();
|
cursor.close();
|
||||||
|
|
||||||
return helper;
|
return helper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private long getFileSize() {
|
||||||
|
Context context = this.context.get();
|
||||||
|
if (context == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (Build.VERSION.SDK_INT >= 19 && isExternalStorageDocument(uri)) {
|
||||||
|
File file = getFile(context, uri);
|
||||||
|
if (file == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return file.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
Cursor cursor = getCursor(context, uri, null);
|
||||||
|
|
||||||
|
if (cursor == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
long size = 0;
|
||||||
|
|
||||||
|
boolean moved = cursor.moveToFirst();
|
||||||
|
if (moved) {
|
||||||
|
int sizeIndex = cursor.getColumnIndex(
|
||||||
|
MediaStore.MediaColumns.SIZE
|
||||||
|
);
|
||||||
|
|
||||||
|
size = cursor.getLong(sizeIndex);
|
||||||
|
|
||||||
|
}
|
||||||
|
cursor.close();
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
private void updateInternal(Context context) {
|
private void updateInternal(Context context) {
|
||||||
updateInternal(context, true);
|
updateInternal(context, true);
|
||||||
}
|
}
|
||||||
@ -247,10 +288,6 @@ public class FileHelper {
|
|||||||
boolean moved = cursor.moveToFirst();
|
boolean moved = cursor.moveToFirst();
|
||||||
|
|
||||||
if (moved) {
|
if (moved) {
|
||||||
int sizeIndex = cursor.getColumnIndex(
|
|
||||||
MediaStore.MediaColumns.SIZE
|
|
||||||
);
|
|
||||||
|
|
||||||
int nameIndex = cursor.getColumnIndex(
|
int nameIndex = cursor.getColumnIndex(
|
||||||
MediaStore.MediaColumns.DISPLAY_NAME
|
MediaStore.MediaColumns.DISPLAY_NAME
|
||||||
);
|
);
|
||||||
@ -259,7 +296,6 @@ public class FileHelper {
|
|||||||
MediaStore.MediaColumns.DATE_MODIFIED
|
MediaStore.MediaColumns.DATE_MODIFIED
|
||||||
);
|
);
|
||||||
|
|
||||||
size = cursor.getLong(sizeIndex);
|
|
||||||
name = cursor.getString(nameIndex);
|
name = cursor.getString(nameIndex);
|
||||||
mime = context.getContentResolver().getType(uri);
|
mime = context.getContentResolver().getType(uri);
|
||||||
lastModified = cursor.getLong(lastModifiedIndex);
|
lastModified = cursor.getLong(lastModifiedIndex);
|
||||||
@ -269,7 +305,7 @@ public class FileHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public long getSize() {
|
public long getSize() {
|
||||||
return size;
|
return getFileSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
@ -308,16 +344,83 @@ public class FileHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private OutputStream getOutputStream(Context context, Uri uri) throws Exception {
|
private OutputStream getOutputStream(Context context, Uri uri) throws Exception {
|
||||||
|
return getOutputStream(context, uri, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private OutputStream getOutputStream(Context context, Uri uri, boolean append) throws Exception {
|
||||||
if (Build.VERSION.SDK_INT >= 19) {
|
if (Build.VERSION.SDK_INT >= 19) {
|
||||||
if (isExternalStorageDocument(uri)) {
|
if (isExternalStorageDocument(uri)) {
|
||||||
File file = getFile(context, uri);
|
File file = getFile(context, uri);
|
||||||
return new FileOutputStream(file);
|
return new FileOutputStream(file, append);
|
||||||
}
|
}
|
||||||
if (DocumentsContract.isDocumentUri(context, uri)) {
|
if (DocumentsContract.isDocumentUri(context, uri)) {
|
||||||
return context.getContentResolver().openOutputStream(DocumentFile.fromSingleUri(context, uri).getUri());
|
return context.getContentResolver().openOutputStream(DocumentFile.fromSingleUri(context, uri).getUri(), append ? "wa" : "w");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return context.getContentResolver().openOutputStream(uri);
|
return context.getContentResolver().openOutputStream(uri, append ? "wa" : "w");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void appendSync(Context context, byte[] content, @Nullable Callback callback) {
|
||||||
|
try {
|
||||||
|
writeSyncInternal(context, content, true);
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (callback != null) {
|
||||||
|
callback.onError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void append(Context context, byte[] content, Callback callback) {
|
||||||
|
executor.execute(() -> {
|
||||||
|
try {
|
||||||
|
writeSyncInternal(context, content, true);
|
||||||
|
handler.post(() -> callback.onSuccess(null));
|
||||||
|
} catch (Exception e) {
|
||||||
|
handler.post(() -> callback.onError(e));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void appendBufferSync(Context context, ByteBuffer content, @Nullable Callback callback) {
|
||||||
|
try {
|
||||||
|
writeBufferSyncInternal(context, content, true);
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (callback != null) {
|
||||||
|
callback.onError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void appendBuffer(Context context, ByteBuffer content, Callback callback) {
|
||||||
|
executor.execute(() -> {
|
||||||
|
try {
|
||||||
|
writeBufferSyncInternal(context, content, true);
|
||||||
|
handler.post(() -> callback.onSuccess(null));
|
||||||
|
} catch (Exception e) {
|
||||||
|
handler.post(() -> callback.onError(e));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void appendTextSync(Context context, String content, @Nullable String encoding, @Nullable Callback callback) {
|
||||||
|
try {
|
||||||
|
writeTextSyncInternal(context, content, encoding, true);
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (callback != null) {
|
||||||
|
callback.onError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void appendText(Context context, String content, @Nullable String encoding, Callback callback) {
|
||||||
|
executor.execute(() -> {
|
||||||
|
try {
|
||||||
|
writeTextSyncInternal(context, content, encoding, true);
|
||||||
|
handler.post(() -> callback.onSuccess(null));
|
||||||
|
} catch (Exception e) {
|
||||||
|
handler.post(() -> callback.onError(e));
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] readSyncInternal(Context context) throws Exception {
|
private byte[] readSyncInternal(Context context) throws Exception {
|
||||||
@ -429,15 +532,23 @@ public class FileHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void writeSyncInternal(Context context, byte[] content) throws Exception {
|
private void writeSyncInternal(Context context, byte[] content) throws Exception {
|
||||||
OutputStream os = getOutputStream(context, uri);
|
writeSyncInternal(context, content, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeSyncInternal(Context context, byte[] content, boolean append) throws Exception {
|
||||||
|
OutputStream os = getOutputStream(context, uri, append);
|
||||||
os.write(content, 0, content.length);
|
os.write(content, 0, content.length);
|
||||||
os.flush();
|
os.flush();
|
||||||
os.close();
|
os.close();
|
||||||
updateInternal(context);
|
updateInternal(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeBufferSyncInternal(Context context,ByteBuffer content) throws Exception {
|
private void writeBufferSyncInternal(Context context, ByteBuffer content) throws Exception {
|
||||||
OutputStream os = getOutputStream(context, uri);
|
writeBufferSyncInternal(context, content, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeBufferSyncInternal(Context context, ByteBuffer content, boolean append) throws Exception {
|
||||||
|
OutputStream os = getOutputStream(context, uri, append);
|
||||||
WritableByteChannel channel = Channels.newChannel(os);
|
WritableByteChannel channel = Channels.newChannel(os);
|
||||||
channel.write(content);
|
channel.write(content);
|
||||||
os.flush();
|
os.flush();
|
||||||
@ -488,7 +599,11 @@ public class FileHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void writeTextSyncInternal(Context context, String content, @Nullable String encoding) throws Exception {
|
private void writeTextSyncInternal(Context context, String content, @Nullable String encoding) throws Exception {
|
||||||
OutputStream os = getOutputStream(context, uri);
|
writeTextSyncInternal(context, content, encoding, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeTextSyncInternal(Context context, String content, @Nullable String encoding, boolean append) throws Exception {
|
||||||
|
OutputStream os = getOutputStream(context, uri, append);
|
||||||
String characterSet = encoding;
|
String characterSet = encoding;
|
||||||
if (characterSet == null) {
|
if (characterSet == null) {
|
||||||
characterSet = "UTF-8";
|
characterSet = "UTF-8";
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
D004031022F781A50089EAD8 /* NSString+Async.m in Sources */ = {isa = PBXBuildFile; fileRef = D004030E22F781A50089EAD8 /* NSString+Async.m */; };
|
D004031022F781A50089EAD8 /* NSString+Async.m in Sources */ = {isa = PBXBuildFile; fileRef = D004030E22F781A50089EAD8 /* NSString+Async.m */; };
|
||||||
D004031322FA27D60089EAD8 /* NSData+Async.h in Headers */ = {isa = PBXBuildFile; fileRef = D004031122FA27D60089EAD8 /* NSData+Async.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
D004031322FA27D60089EAD8 /* NSData+Async.h in Headers */ = {isa = PBXBuildFile; fileRef = D004031122FA27D60089EAD8 /* NSData+Async.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
D004031422FA27D60089EAD8 /* NSData+Async.m in Sources */ = {isa = PBXBuildFile; fileRef = D004031222FA27D60089EAD8 /* NSData+Async.m */; };
|
D004031422FA27D60089EAD8 /* NSData+Async.m in Sources */ = {isa = PBXBuildFile; fileRef = D004031222FA27D60089EAD8 /* NSData+Async.m */; };
|
||||||
|
F11DE2162A02F39A00B70DC5 /* NSFileHandle+Async.m in Sources */ = {isa = PBXBuildFile; fileRef = F11DE2152A02F39A00B70DC5 /* NSFileHandle+Async.m */; };
|
||||||
|
F11DE2182A02F3B500B70DC5 /* NSFileHandle+Async.h in Headers */ = {isa = PBXBuildFile; fileRef = F11DE2172A02F3B500B70DC5 /* NSFileHandle+Async.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
F915D3551EC9EF5E00071914 /* TNSProcess.m in Sources */ = {isa = PBXBuildFile; fileRef = F915D3531EC9EF5E00071914 /* TNSProcess.m */; };
|
F915D3551EC9EF5E00071914 /* TNSProcess.m in Sources */ = {isa = PBXBuildFile; fileRef = F915D3531EC9EF5E00071914 /* TNSProcess.m */; };
|
||||||
F915D3561EC9EF5E00071914 /* TNSProcess.h in Headers */ = {isa = PBXBuildFile; fileRef = F915D3541EC9EF5E00071914 /* TNSProcess.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
F915D3561EC9EF5E00071914 /* TNSProcess.h in Headers */ = {isa = PBXBuildFile; fileRef = F915D3541EC9EF5E00071914 /* TNSProcess.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
F98F5CB31CD0EFEA00978308 /* TNSWidgets.h in Headers */ = {isa = PBXBuildFile; fileRef = F98F5CB21CD0EFEA00978308 /* TNSWidgets.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
F98F5CB31CD0EFEA00978308 /* TNSWidgets.h in Headers */ = {isa = PBXBuildFile; fileRef = F98F5CB21CD0EFEA00978308 /* TNSWidgets.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
@ -51,6 +53,8 @@
|
|||||||
D004030E22F781A50089EAD8 /* NSString+Async.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSString+Async.m"; sourceTree = "<group>"; };
|
D004030E22F781A50089EAD8 /* NSString+Async.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSString+Async.m"; sourceTree = "<group>"; };
|
||||||
D004031122FA27D60089EAD8 /* NSData+Async.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSData+Async.h"; sourceTree = "<group>"; };
|
D004031122FA27D60089EAD8 /* NSData+Async.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSData+Async.h"; sourceTree = "<group>"; };
|
||||||
D004031222FA27D60089EAD8 /* NSData+Async.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSData+Async.m"; sourceTree = "<group>"; };
|
D004031222FA27D60089EAD8 /* NSData+Async.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSData+Async.m"; sourceTree = "<group>"; };
|
||||||
|
F11DE2152A02F39A00B70DC5 /* NSFileHandle+Async.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSFileHandle+Async.m"; sourceTree = "<group>"; };
|
||||||
|
F11DE2172A02F3B500B70DC5 /* NSFileHandle+Async.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSFileHandle+Async.h"; sourceTree = "<group>"; };
|
||||||
F915D3531EC9EF5E00071914 /* TNSProcess.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TNSProcess.m; path = ../TNSProcess.m; sourceTree = "<group>"; };
|
F915D3531EC9EF5E00071914 /* TNSProcess.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TNSProcess.m; path = ../TNSProcess.m; sourceTree = "<group>"; };
|
||||||
F915D3541EC9EF5E00071914 /* TNSProcess.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TNSProcess.h; path = ../TNSProcess.h; sourceTree = "<group>"; };
|
F915D3541EC9EF5E00071914 /* TNSProcess.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TNSProcess.h; path = ../TNSProcess.h; sourceTree = "<group>"; };
|
||||||
F98F5CAF1CD0EFEA00978308 /* TNSWidgets.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TNSWidgets.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
F98F5CAF1CD0EFEA00978308 /* TNSWidgets.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TNSWidgets.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
@ -121,6 +125,8 @@
|
|||||||
B8E76F5D212C3134009CFCE2 /* UIView+PassThroughParent.m */,
|
B8E76F5D212C3134009CFCE2 /* UIView+PassThroughParent.m */,
|
||||||
D004031122FA27D60089EAD8 /* NSData+Async.h */,
|
D004031122FA27D60089EAD8 /* NSData+Async.h */,
|
||||||
D004031222FA27D60089EAD8 /* NSData+Async.m */,
|
D004031222FA27D60089EAD8 /* NSData+Async.m */,
|
||||||
|
F11DE2172A02F3B500B70DC5 /* NSFileHandle+Async.h */,
|
||||||
|
F11DE2152A02F39A00B70DC5 /* NSFileHandle+Async.m */,
|
||||||
);
|
);
|
||||||
path = TNSWidgets;
|
path = TNSWidgets;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -147,6 +153,7 @@
|
|||||||
D004031322FA27D60089EAD8 /* NSData+Async.h in Headers */,
|
D004031322FA27D60089EAD8 /* NSData+Async.h in Headers */,
|
||||||
F98F5CCB1CD0F09E00978308 /* UIImage+TNSBlocks.h in Headers */,
|
F98F5CCB1CD0F09E00978308 /* UIImage+TNSBlocks.h in Headers */,
|
||||||
B8E76F52212C2DA2009CFCE2 /* NSObject+Swizzling.h in Headers */,
|
B8E76F52212C2DA2009CFCE2 /* NSObject+Swizzling.h in Headers */,
|
||||||
|
F11DE2182A02F3B500B70DC5 /* NSFileHandle+Async.h in Headers */,
|
||||||
B8E76F5A212C2F4E009CFCE2 /* UIView+PropertyBag.h in Headers */,
|
B8E76F5A212C2F4E009CFCE2 /* UIView+PropertyBag.h in Headers */,
|
||||||
D004030F22F781A50089EAD8 /* NSString+Async.h in Headers */,
|
D004030F22F781A50089EAD8 /* NSString+Async.h in Headers */,
|
||||||
8B7321CF1D097ECD00884AC6 /* TNSLabel.h in Headers */,
|
8B7321CF1D097ECD00884AC6 /* TNSLabel.h in Headers */,
|
||||||
@ -252,6 +259,7 @@
|
|||||||
files = (
|
files = (
|
||||||
8B7321D01D097ECD00884AC6 /* TNSLabel.m in Sources */,
|
8B7321D01D097ECD00884AC6 /* TNSLabel.m in Sources */,
|
||||||
F915D3551EC9EF5E00071914 /* TNSProcess.m in Sources */,
|
F915D3551EC9EF5E00071914 /* TNSProcess.m in Sources */,
|
||||||
|
F11DE2162A02F39A00B70DC5 /* NSFileHandle+Async.m in Sources */,
|
||||||
F98F5CCC1CD0F09E00978308 /* UIImage+TNSBlocks.m in Sources */,
|
F98F5CCC1CD0F09E00978308 /* UIImage+TNSBlocks.m in Sources */,
|
||||||
B8E76F53212C2DA2009CFCE2 /* NSObject+Swizzling.m in Sources */,
|
B8E76F53212C2DA2009CFCE2 /* NSObject+Swizzling.m in Sources */,
|
||||||
D004031022F781A50089EAD8 /* NSString+Async.m in Sources */,
|
D004031022F781A50089EAD8 /* NSString+Async.m in Sources */,
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
//
|
||||||
|
// NSFileHandle+Async.h
|
||||||
|
// TNSWidgets
|
||||||
|
//
|
||||||
|
// Created by Osei Fortune on 03/05/2023.
|
||||||
|
// Copyright © 2023 Telerik A D. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface NSFileHandle (Async)
|
||||||
|
|
||||||
|
- (void)appendData:(nonnull NSData*) data
|
||||||
|
completion:(void (^) (NSError*))callback;
|
||||||
|
|
||||||
|
+ (void)fileHandleWith:(NSString *)path data:(NSData *)data completion:(void (^)(NSFileHandle*,NSError*))callback;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
@ -0,0 +1,82 @@
|
|||||||
|
//
|
||||||
|
// NSFileHandle+Async.m
|
||||||
|
// TNSWidgets
|
||||||
|
//
|
||||||
|
// Created by Osei Fortune on 03/05/2023.
|
||||||
|
// Copyright © 2023 Telerik A D. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "NSFileHandle+Async.h"
|
||||||
|
|
||||||
|
@implementation NSFileHandle(Async)
|
||||||
|
|
||||||
|
- (void)appendData:(NSData *)data completion:(void (^)(NSError*))callback {
|
||||||
|
dispatch_queue_t asyncQueue = dispatch_queue_create("org.nativescript.TNSWidgets.fileHandle", NULL);
|
||||||
|
dispatch_async(asyncQueue, ^(void) {
|
||||||
|
NSError *error = nil;
|
||||||
|
|
||||||
|
if (@available(iOS 13.0, *)) {
|
||||||
|
[self seekToEndReturningOffset:nil error:&error];
|
||||||
|
[self writeData:data error:&error];
|
||||||
|
} else {
|
||||||
|
@try {
|
||||||
|
[self seekToEndOfFile];
|
||||||
|
[self writeData:data];
|
||||||
|
} @catch (NSException *exception) {
|
||||||
|
|
||||||
|
NSMutableDictionary * info = [NSMutableDictionary dictionary];
|
||||||
|
[info setValue:exception.name forKey:@"ExceptionName"];
|
||||||
|
[info setValue:exception.reason forKey:@"ExceptionReason"];
|
||||||
|
[info setValue:exception.callStackReturnAddresses forKey:@"ExceptionCallStackReturnAddresses"];
|
||||||
|
[info setValue:exception.callStackSymbols forKey:@"ExceptionCallStackSymbols"];
|
||||||
|
[info setValue:exception.userInfo forKey:@"ExceptionUserInfo"];
|
||||||
|
|
||||||
|
error = [[NSError alloc] initWithDomain:@"" code: 1 userInfo:info];
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
||||||
|
callback(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void)fileHandleWith:(NSString *)path data:(NSData *)data completion:(void (^)(NSFileHandle*, NSError*))callback {
|
||||||
|
dispatch_queue_t asyncQueue = dispatch_queue_create("org.nativescript.TNSWidgets.fileHandle", NULL);
|
||||||
|
dispatch_async(asyncQueue, ^(void) {
|
||||||
|
NSFileHandle* handle = [NSFileHandle fileHandleForWritingAtPath: path];
|
||||||
|
NSError *error = nil;
|
||||||
|
|
||||||
|
if (@available(iOS 13.0, *)) {
|
||||||
|
[handle seekToEndReturningOffset:nil error:&error];
|
||||||
|
[handle writeData:data error:&error];
|
||||||
|
} else {
|
||||||
|
@try {
|
||||||
|
[handle seekToEndOfFile];
|
||||||
|
[handle writeData:data];
|
||||||
|
} @catch (NSException *exception) {
|
||||||
|
|
||||||
|
NSMutableDictionary * info = [NSMutableDictionary dictionary];
|
||||||
|
[info setValue:exception.name forKey:@"ExceptionName"];
|
||||||
|
[info setValue:exception.reason forKey:@"ExceptionReason"];
|
||||||
|
[info setValue:exception.callStackReturnAddresses forKey:@"ExceptionCallStackReturnAddresses"];
|
||||||
|
[info setValue:exception.callStackSymbols forKey:@"ExceptionCallStackSymbols"];
|
||||||
|
[info setValue:exception.userInfo forKey:@"ExceptionUserInfo"];
|
||||||
|
|
||||||
|
error = [[NSError alloc] initWithDomain:@"" code: 1 userInfo:info];
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
||||||
|
callback(handle,error);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
@ -22,3 +22,4 @@ FOUNDATION_EXPORT const unsigned char TNSWidgetsVersionString[];
|
|||||||
#import "TNSProcess.h"
|
#import "TNSProcess.h"
|
||||||
#import "NSString+Async.h"
|
#import "NSString+Async.h"
|
||||||
#import "NSData+Async.h"
|
#import "NSData+Async.h"
|
||||||
|
#import "NSFileHandle+Async.h"
|
||||||
|
Reference in New Issue
Block a user