mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-14 18:12:09 +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 appModule from '@nativescript/core/application';
|
||||
import { isIOS, Device, platformNames } from '@nativescript/core';
|
||||
import { isIOS, Device, platformNames, isAndroid } from '@nativescript/core';
|
||||
|
||||
export var testPathNormalize = function () {
|
||||
// >> file-system-normalize
|
||||
@ -719,3 +719,62 @@ export function test_FileCopy(done) {
|
||||
.then(() => 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;
|
||||
|
||||
@ -217,3 +217,103 @@ function getFileNameFromContent(content: string) {
|
||||
const file = getFileAccess().getFile(content);
|
||||
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 Multiple Files" tap="pickFiles" />
|
||||
<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>
|
||||
</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 writeBufferAsync(path: string, buffer: ArrayBuffer | Uint8Array | Uint8ClampedArray): Promise<void> {
|
||||
@ -538,6 +613,56 @@ export class FileSystemAccess implements IFileSystemAccess {
|
||||
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 writeTextAsync(path: string, content: string, encoding?: any): Promise<void> {
|
||||
@ -879,6 +1004,124 @@ export class FileSystemAccess29 extends FileSystemAccess {
|
||||
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);
|
||||
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
* @param src The path to the source file.
|
||||
@ -282,6 +331,24 @@ export interface 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;
|
||||
|
||||
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 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';
|
||||
|
||||
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
|
||||
@ -55,10 +70,27 @@ export class FileSystemEntity {
|
||||
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.
|
||||
*/
|
||||
export class File extends FileSystemEntity {
|
||||
static readonly android: Android;
|
||||
|
||||
static readonly ios: iOS;
|
||||
|
||||
/**
|
||||
* Checks whether a File with the specified path already exists.
|
||||
* @param path The path to check for.
|
||||
@ -80,6 +112,34 @@ export class File extends FileSystemEntity {
|
||||
*/
|
||||
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.
|
||||
* @param dest The path to the destination file.
|
||||
|
@ -190,7 +190,136 @@ function getApplicationContext() {
|
||||
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 {
|
||||
public static get ios() {
|
||||
return ios;
|
||||
}
|
||||
|
||||
public static get android() {
|
||||
return ad;
|
||||
}
|
||||
|
||||
public static fromPath(path: string, copy: boolean = false) {
|
||||
const onError = function (error) {
|
||||
throw error;
|
||||
@ -239,6 +368,100 @@ export class File extends FileSystemEntity {
|
||||
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> {
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
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 { VirtualArray } 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';
|
||||
import { getFile, getImage, getJSON, getString as httpGetString } from './http';
|
||||
export declare const Http: {
|
||||
|
@ -105,7 +105,7 @@ export { Observable, WrappedValue, fromObject, fromObjectRecursive } from './dat
|
||||
export type { PropertyChangeData, EventData } from './data/observable';
|
||||
export { VirtualArray } 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 type { HttpRequestOptions, HttpResponse, Headers, HttpResponseEncoding, HttpContent } from './http';
|
||||
|
Binary file not shown.
@ -4,6 +4,23 @@
|
||||
<dict>
|
||||
<key>AvailableLibraries</key>
|
||||
<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>
|
||||
<key>DebugSymbolsPath</key>
|
||||
<string>dSYMs</string>
|
||||
@ -35,23 +52,6 @@
|
||||
<key>SupportedPlatformVariant</key>
|
||||
<string>simulator</string>
|
||||
</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>
|
||||
<key>CFBundlePackageType</key>
|
||||
<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 "NSString+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 "NSString+Async.h"
|
||||
#import "NSData+Async.h"
|
||||
#import "NSFileHandle+Async.h"
|
||||
|
@ -3,7 +3,7 @@
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BuildMachineOSBuild</key>
|
||||
<string>21G72</string>
|
||||
<string>22D68</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
@ -29,7 +29,7 @@
|
||||
<key>DTCompiler</key>
|
||||
<string>com.apple.compilers.llvm.clang.1_0</string>
|
||||
<key>DTPlatformBuild</key>
|
||||
<string>13F100</string>
|
||||
<string>14A309</string>
|
||||
<key>DTPlatformName</key>
|
||||
<string>macosx</string>
|
||||
<key>DTPlatformVersion</key>
|
||||
@ -39,9 +39,9 @@
|
||||
<key>DTSDKName</key>
|
||||
<string>macosx12.3</string>
|
||||
<key>DTXcode</key>
|
||||
<string>1341</string>
|
||||
<string>1400</string>
|
||||
<key>DTXcodeBuild</key>
|
||||
<string>13F100</string>
|
||||
<string>14A309</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.15</string>
|
||||
<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 "NSString+Async.h"
|
||||
#import "NSData+Async.h"
|
||||
#import "NSFileHandle+Async.h"
|
||||
|
Binary file not shown.
Binary file not shown.
@ -8,6 +8,10 @@
|
||||
<data>
|
||||
9BsuK8QsA57YnHHgpWIgaHygSEk=
|
||||
</data>
|
||||
<key>Headers/NSFileHandle+Async.h</key>
|
||||
<data>
|
||||
iQtzsDHw/VTnFG27yW+QvGCeXGw=
|
||||
</data>
|
||||
<key>Headers/NSString+Async.h</key>
|
||||
<data>
|
||||
o8366y9zYMOVyObC3vtKKy/8jxA=
|
||||
@ -22,7 +26,7 @@
|
||||
</data>
|
||||
<key>Headers/TNSWidgets.h</key>
|
||||
<data>
|
||||
gUvu5bjZg5Aie5iJ1krxFmDrHwk=
|
||||
ZFbCov7mFiXa4ZA/gAxqYHzRX7Q=
|
||||
</data>
|
||||
<key>Headers/UIImage+TNSBlocks.h</key>
|
||||
<data>
|
||||
@ -34,7 +38,7 @@
|
||||
</data>
|
||||
<key>Info.plist</key>
|
||||
<data>
|
||||
RCfacSfbjEiH19hn34SSJzGdiFo=
|
||||
myeIggkwNuTIqt7xlMgf8VGsKZ8=
|
||||
</data>
|
||||
<key>Modules/module.modulemap</key>
|
||||
<data>
|
||||
@ -58,6 +62,13 @@
|
||||
JdjuVUBed00Ged4cSDzYLXONUlESu+dae9KN0PYJ/nM=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Headers/NSFileHandle+Async.h</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
1IqS81dD1dbKccHZ0lYPMMF1zBPdp3InM+rdOFjBo+k=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Headers/NSString+Async.h</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
@ -83,7 +94,7 @@
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
V99t2zLwRPOs90tbGiQbhbdAFJlW7mp7X2R5337ewUA=
|
||||
6L3xStgKRHkPvDZAHAUF+EMI3Skf5jvnoA4ED/WbB1w=
|
||||
</data>
|
||||
</dict>
|
||||
<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 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 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 {
|
||||
@ -646,7 +649,13 @@ declare module org {
|
||||
export module widgets {
|
||||
export class 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 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 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;
|
||||
|
@ -1,6 +1,9 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
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
|
||||
android:allowBackup="true">
|
||||
|
||||
|
@ -1,11 +1,13 @@
|
||||
package org.nativescript.widgets;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Looper;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
@ -599,19 +601,87 @@ public class Async {
|
||||
|
||||
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 {
|
||||
InputStream is;
|
||||
OutputStream os;
|
||||
|
||||
boolean requiresUpdate = false;
|
||||
if(src.startsWith("content://")){
|
||||
is = context.getContentResolver().openInputStream(Uri.parse(src));
|
||||
}else is = new FileInputStream(new java.io.File(src));
|
||||
|
||||
if(dest.startsWith("content://")){
|
||||
os = context.getContentResolver().openOutputStream(Uri.parse(dest));
|
||||
requiresUpdate = true;
|
||||
}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 {
|
||||
@ -630,15 +700,33 @@ public class Async {
|
||||
InputStream is;
|
||||
OutputStream os;
|
||||
|
||||
boolean requiresUpdate = false;
|
||||
|
||||
if(src.startsWith("content://")){
|
||||
is = context.getContentResolver().openInputStream(Uri.parse(src));
|
||||
}else is = new FileInputStream(new java.io.File(src));
|
||||
|
||||
if(dest.startsWith("content://")){
|
||||
requiresUpdate = true;
|
||||
os = context.getContentResolver().openOutputStream(Uri.parse(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){
|
||||
callback.onError(exception.getMessage(), context);
|
||||
}
|
||||
@ -666,11 +754,12 @@ public class Async {
|
||||
return written;
|
||||
}
|
||||
|
||||
|
||||
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());
|
||||
threadPoolExecutor().execute((Runnable) () -> {
|
||||
|
||||
boolean done = false;
|
||||
Exception error = null;
|
||||
try (InputStream is = src; OutputStream os = dest){
|
||||
ReadableByteChannel isc = java.nio.channels.Channels.newChannel(is);
|
||||
WritableByteChannel osc = java.nio.channels.Channels.newChannel(os);
|
||||
@ -679,10 +768,19 @@ public class Async {
|
||||
|
||||
int written = fastChannelCopy(isc, osc);
|
||||
|
||||
mHandler.post(() -> callback.onComplete(size == written, context));
|
||||
done = size == written;
|
||||
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -19,6 +19,8 @@
|
||||
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, ); }; };
|
||||
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 */; };
|
||||
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, ); }; };
|
||||
@ -51,6 +53,8 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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; };
|
||||
@ -121,6 +125,8 @@
|
||||
B8E76F5D212C3134009CFCE2 /* UIView+PassThroughParent.m */,
|
||||
D004031122FA27D60089EAD8 /* NSData+Async.h */,
|
||||
D004031222FA27D60089EAD8 /* NSData+Async.m */,
|
||||
F11DE2172A02F3B500B70DC5 /* NSFileHandle+Async.h */,
|
||||
F11DE2152A02F39A00B70DC5 /* NSFileHandle+Async.m */,
|
||||
);
|
||||
path = TNSWidgets;
|
||||
sourceTree = "<group>";
|
||||
@ -147,6 +153,7 @@
|
||||
D004031322FA27D60089EAD8 /* NSData+Async.h in Headers */,
|
||||
F98F5CCB1CD0F09E00978308 /* UIImage+TNSBlocks.h in Headers */,
|
||||
B8E76F52212C2DA2009CFCE2 /* NSObject+Swizzling.h in Headers */,
|
||||
F11DE2182A02F3B500B70DC5 /* NSFileHandle+Async.h in Headers */,
|
||||
B8E76F5A212C2F4E009CFCE2 /* UIView+PropertyBag.h in Headers */,
|
||||
D004030F22F781A50089EAD8 /* NSString+Async.h in Headers */,
|
||||
8B7321CF1D097ECD00884AC6 /* TNSLabel.h in Headers */,
|
||||
@ -252,6 +259,7 @@
|
||||
files = (
|
||||
8B7321D01D097ECD00884AC6 /* TNSLabel.m in Sources */,
|
||||
F915D3551EC9EF5E00071914 /* TNSProcess.m in Sources */,
|
||||
F11DE2162A02F39A00B70DC5 /* NSFileHandle+Async.m in Sources */,
|
||||
F98F5CCC1CD0F09E00978308 /* UIImage+TNSBlocks.m in Sources */,
|
||||
B8E76F53212C2DA2009CFCE2 /* NSObject+Swizzling.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 "NSString+Async.h"
|
||||
#import "NSData+Async.h"
|
||||
#import "NSFileHandle+Async.h"
|
||||
|
Reference in New Issue
Block a user