Skip to content

Commit 75d3592

Browse files
author
Laks Castro
authored
Merge branch 'master' into chore/set-plugin-version-v0.5.0
2 parents 74e8700 + 13dbaf9 commit 75d3592

File tree

8 files changed

+242
-3
lines changed

8 files changed

+242
-3
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ These are the brilliant minds behind the development of this plugin!
4949
<td align="center"><a href="https://github.com/dangilbert"><img src="https://avatars.githubusercontent.com/u/6799566?v=4?s=100" width="100px;" alt=""/><br /><sub><b>dangilbert</b></sub></a><br /><a href="https://github.com/lakscastro/shared-storage/commits?author=dangilbert" title="Code">💻</a> <a href="https://github.com/lakscastro/shared-storage/issues?q=author%3Adangilbert" title="Bug reports">🐛</a></td>
5050
<td align="center"><a href="https://github.com/dhaval-k-simformsolutions"><img src="https://avatars.githubusercontent.com/u/90894202?v=4?s=100" width="100px;" alt=""/><br /><sub><b>dhaval-k-simformsolutions</b></sub></a><br /><a href="https://github.com/lakscastro/shared-storage/issues?q=author%3Adhaval-k-simformsolutions" title="Bug reports">🐛</a> <a href="#ideas-dhaval-k-simformsolutions" title="Ideas, Planning, & Feedback">🤔</a></td>
5151
<td align="center"><a href="https://eternityforest.com"><img src="https://avatars.githubusercontent.com/u/758047?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Daniel Dunn</b></sub></a><br /><a href="https://github.com/lakscastro/shared-storage/issues?q=author%3AEternityForest" title="Bug reports">🐛</a> <a href="https://github.com/lakscastro/shared-storage/commits?author=EternityForest" title="Code">💻</a> <a href="https://github.com/lakscastro/shared-storage/commits?author=EternityForest" title="Documentation">📖</a></td>
52-
<td align="center"><a href="http://jfaltis.de"><img src="https://avatars.githubusercontent.com/u/45465572?v=4?s=100" width="100px;" alt=""/><br /><sub><b>jfaltis</b></sub></a><br /><a href="https://github.com/lakscastro/shared-storage/issues?q=author%3Ajfaltis" title="Bug reports">🐛</a> <a href="https://github.com/lakscastro/shared-storage/commits?author=jfaltis" title="Code">💻</a> <a href="https://github.com/lakscastro/shared-storage/commits?author=jfaltis" title="Documentation">📖</a></td>
52+
<td align="center"><a href="https://jfaltis.de"><img src="https://avatars.githubusercontent.com/u/45465572?v=4?s=100" width="100px;" alt=""/><br /><sub><b>jfaltis</b></sub></a><br /><a href="https://github.com/lakscastro/shared-storage/issues?q=author%3Ajfaltis" title="Bug reports">🐛</a> <a href="https://github.com/lakscastro/shared-storage/commits?author=jfaltis" title="Code">💻</a> <a href="https://github.com/lakscastro/shared-storage/commits?author=jfaltis" title="Documentation">📖</a></td>
5353
</tr>
5454
</table>
5555

android/src/main/kotlin/io/lakscastro/sharedstorage/storageaccessframework/DocumentFileApi.kt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,13 @@ internal class DocumentFileApi(private val plugin: SharedStoragePlugin) :
7474
call.argument<ByteArray>("content")!!
7575
)
7676
}
77+
WRITE_TO_FILE ->
78+
writeToFile(
79+
result,
80+
call.argument<String>("uri")!!,
81+
call.argument<ByteArray>("content")!!,
82+
call.argument<String>("mode")!!
83+
)
7784
PERSISTED_URI_PERMISSIONS ->
7885
persistedUriPermissions(result)
7986
RELEASE_PERSISTABLE_URI_PERMISSION ->
@@ -312,6 +319,25 @@ internal class DocumentFileApi(private val plugin: SharedStoragePlugin) :
312319
}
313320
}
314321

322+
private fun writeToFile(
323+
result: MethodChannel.Result,
324+
uri: String,
325+
content: ByteArray,
326+
mode: String
327+
) {
328+
try {
329+
plugin.context.contentResolver.openOutputStream(Uri.parse(uri), mode)?.apply {
330+
write(content)
331+
flush()
332+
close()
333+
334+
result.success(true)
335+
}
336+
} catch (e: Exception) {
337+
result.success(false)
338+
}
339+
}
340+
315341
@RequiresApi(API_19)
316342
private fun persistedUriPermissions(result: MethodChannel.Result) {
317343
val persistedUriPermissions = plugin.context.contentResolver.persistedUriPermissions

android/src/main/kotlin/io/lakscastro/sharedstorage/storageaccessframework/lib/StorageAccessFrameworkConstant.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const val OPEN_DOCUMENT_TREE = "openDocumentTree"
2323
const val PERSISTED_URI_PERMISSIONS = "persistedUriPermissions"
2424
const val RELEASE_PERSISTABLE_URI_PERMISSION = "releasePersistableUriPermission"
2525
const val CREATE_FILE = "createFile"
26+
const val WRITE_TO_FILE = "writeToFile"
2627
const val FROM_TREE_URI = "fromTreeUri"
2728
const val CAN_WRITE = "canWrite"
2829
const val CAN_READ = "canRead"

docs/Usage/Storage Access Framework.md

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,33 @@ final DocumentFile? createdFile = createFileAsBytes(
217217
);
218218
```
219219

220+
### <samp>writeToFileAsBytes</samp>
221+
222+
Write to a file using raw bytes `Uint8List`.
223+
224+
Given the document uri, opens the file in the specified `mode` and writes the `bytes` to it.
225+
226+
`mode` represents the mode in which the file will be opened for writing. Use `FileMode.write` for truncating (overwrite) and `FileMode.append` for appending to the file.
227+
228+
```dart
229+
final Uri documentUri = ...
230+
final String fileContent = 'My File Content';
231+
232+
/// Write to a file using a [Uint8List] as file contents [bytes]
233+
final bool? success = writeToFileAsBytes(
234+
documentUri,
235+
bytes: Uint8List.fromList(fileContent.codeUnits),
236+
mode: FileMode.write,
237+
);
238+
239+
/// Append to a file using a [Uint8List] as file contents [bytes]
240+
final bool? success = writeToFileAsBytes(
241+
documentUri,
242+
bytes: Uint8List.fromList(fileContent.codeUnits),
243+
mode: FileMode.write,
244+
);
245+
```
246+
220247
### <samp>canRead</samp>
221248

222249
<samp>Mirror of [`DocumentFile.canRead`](<https://developer.android.com/reference/androidx/documentfile/provider/DocumentFile#canRead()>)</samp>
@@ -485,6 +512,31 @@ final DocumentFile? createdFile = createFileAsString(
485512
);
486513
```
487514

515+
### <samp>writeToFileAsString</samp>
516+
517+
<samp>Alias for `writeToFileAsBytes`</samp>
518+
519+
Convenient method to write to a file using `content` as `String` instead `Uint8List`.
520+
521+
```dart
522+
final Uri documentUri = ...
523+
final String fileContent = 'My File Content';
524+
525+
/// Write to a file using a [Uint8List] as file contents [bytes]
526+
final bool? success = writeToFileAsString(
527+
documentUri,
528+
content: fileContent,
529+
mode: FileMode.write,
530+
);
531+
532+
/// Append to a file using a [Uint8List] as file contents [bytes]
533+
final bool? success = writeToFileAsBytes(
534+
documentUri,
535+
content: fileContent,
536+
mode: FileMode.write,
537+
);
538+
```
539+
488540
### <samp>createFile</samp>
489541

490542
<samp>Alias for `createFileAsBytes` and `createFileAsString`</samp>
@@ -514,6 +566,49 @@ final DocumentFile? createdFile = createFile(
514566
);
515567
```
516568

569+
### <samp>writeToFile</samp>
570+
571+
<samp>Alias for `writeToFileAsBytes` and `writeToFileAsString`</samp>
572+
573+
Convenient method to write to a file using `content` as `String` **or** `bytes` as `Uint8List`.
574+
575+
You should provide either `content` or `bytes`, if both `bytes` will be used.
576+
577+
`mode` represents the mode in which the file will be opened for writing. Use `FileMode.write` for truncating and `FileMode.append` for appending to the file.
578+
579+
```dart
580+
final Uri documentUri = ...
581+
final String fileContent = 'My File Content';
582+
583+
/// Write to a file using a [String] as file contents [content]
584+
final bool? success = writeToFile(
585+
documentUri,
586+
content: fileContent,
587+
mode: FileMode.write,
588+
);
589+
590+
/// Append to a file using a [String] as file contents [content]
591+
final bool? success = writeToFile(
592+
documentUri,
593+
content: fileContent,
594+
mode: FileMode.append,
595+
);
596+
597+
/// Write to a file using a [Uint8List] as file contents [bytes]
598+
final bool? success = writeToFile(
599+
documentUri,
600+
content: Uint8List.fromList(fileContent.codeUnits),
601+
mode: FileMode.write,
602+
);
603+
604+
/// Append to a file using a [Uint8List] as file contents [bytes]
605+
final bool? success = writeToFile(
606+
documentUri,
607+
content: Uint8List.fromList(fileContent.codeUnits),
608+
mode: FileMode.append,
609+
);
610+
```
611+
517612
## External APIs (deprecated)
518613

519614
These APIs are from external Android libraries.

example/lib/screens/file_explorer/file_explorer_card.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,13 @@ class _FileExplorerCardState extends State<FileExplorerCard> {
198198
}
199199
},
200200
),
201+
if (!_isDirectory)
202+
DangerButton(
203+
'Write to File',
204+
onTap: () async {
205+
await writeToFile(widget.partialFile.metadata!.uri!, content: 'Hello World!');
206+
},
207+
),
201208
],
202209
),
203210
],

lib/src/saf/document_file.dart

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'dart:io';
12
import 'dart:typed_data';
23

34
import '../common/functional_extender.dart';
@@ -155,6 +156,41 @@ class DocumentFile {
155156
displayName: displayName,
156157
content: content,
157158
);
159+
160+
/// {@macro sharedstorage.saf.writeToFileAsBytes}
161+
Future<bool?> writeToFileAsBytes({
162+
required Uint8List bytes,
163+
FileMode? mode,
164+
}) =>
165+
saf.writeToFileAsBytes(
166+
uri,
167+
bytes: bytes,
168+
mode: mode,
169+
);
170+
171+
/// {@macro sharedstorage.saf.writeToFile}
172+
Future<bool?> writeToFile({
173+
String? content,
174+
Uint8List? bytes,
175+
FileMode? mode,
176+
}) =>
177+
saf.writeToFile(
178+
uri,
179+
content: content,
180+
bytes: bytes,
181+
mode: mode,
182+
);
183+
184+
/// Alias for [writeToFile] with [content] param
185+
Future<bool?> writeToFileAsString({
186+
required String content,
187+
FileMode? mode,
188+
}) =>
189+
saf.writeToFile(
190+
uri,
191+
content: content,
192+
mode: mode,
193+
);
158194

159195
/// {@macro sharedstorage.saf.length}
160196
Future<int?> get length => saf.documentLength(uri);

lib/src/saf/saf.dart

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'dart:async';
2+
import 'dart:io';
23
import 'dart:typed_data';
34

45
import '../../saf.dart';
@@ -201,7 +202,7 @@ Future<DocumentFile?> createDirectory(Uri parentUri, String displayName) async {
201202
}
202203

203204
/// {@template sharedstorage.saf.createFile}
204-
/// Convenient method to create files using either String or raw bytes.
205+
/// Convenient method to create files using either [String] or raw bytes [Uint8List].
205206
///
206207
/// Under the hood this method calls `createFileAsString` or `createFileAsBytes`
207208
/// depending on which argument is passed.
@@ -281,6 +282,79 @@ Future<DocumentFile?> createFileAsString(
281282
);
282283
}
283284

285+
/// {@template sharedstorage.saf.writeToFile}
286+
/// Convenient method to write to a file using either [String] or raw bytes [Uint8List].
287+
///
288+
/// Under the hood this method calls `writeToFileAsString` or `writeToFileAsBytes`
289+
/// depending on which argument is passed.
290+
///
291+
/// If both (bytes and content) are passed, the bytes will be used and the content will be ignored.
292+
/// {@endtemplate}
293+
Future<bool?> writeToFile(
294+
Uri uri, {
295+
Uint8List? bytes,
296+
String? content,
297+
FileMode? mode,
298+
}) {
299+
assert(
300+
bytes != null || content != null,
301+
'''Either [bytes] or [content] should be provided''',
302+
);
303+
304+
return bytes != null
305+
? writeToFileAsBytes(
306+
uri,
307+
bytes: bytes,
308+
mode: mode,
309+
)
310+
: writeToFileAsString(
311+
uri,
312+
content: content!,
313+
mode: mode,
314+
);
315+
}
316+
317+
/// {@template sharedstorage.saf.writeToFileAsBytes}
318+
/// Write to a file.
319+
/// - `uri` is the URI of the file.
320+
/// - `bytes` is the content of the document as a list of bytes `Uint8List`.
321+
/// - `mode` is the mode in which the file will be opened for writing. Use `FileMode.write` for truncating and `FileMode.append` for appending to the file.
322+
///
323+
/// Returns `true` if the file was successfully written to.
324+
/// {@endtemplate}
325+
Future<bool?> writeToFileAsBytes(
326+
Uri uri, {
327+
required Uint8List bytes,
328+
FileMode? mode,
329+
}) async {
330+
final writeMode =
331+
mode == FileMode.append || mode == FileMode.writeOnlyAppend ? 'wa' : 'wt';
332+
333+
final args = <String, dynamic>{
334+
'uri': '$uri',
335+
'content': bytes,
336+
'mode': writeMode,
337+
};
338+
339+
return kDocumentFileChannel.invokeMethod<bool>('writeToFile', args);
340+
}
341+
342+
/// {@template sharedstorage.saf.writeToFileAsString}
343+
/// Convenient method to write to a file.
344+
/// using `content` as [String] instead [Uint8List].
345+
/// {@endtemplate}
346+
Future<bool?> writeToFileAsString(
347+
Uri uri, {
348+
required String content,
349+
FileMode? mode,
350+
}) {
351+
return writeToFileAsBytes(
352+
uri,
353+
bytes: Uint8List.fromList(content.codeUnits),
354+
mode: mode,
355+
);
356+
}
357+
284358
/// {@template sharedstorage.saf.length}
285359
/// Equivalent to `DocumentFile.length`.
286360
///

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: shared_storage
2-
description: "Flutter plugin to work with external storage."
2+
description: "Flutter plugin to work with external storage and privacy-friendly APIs."
33
version: 0.5.0
44
homepage: https://github.com/lakscastro/shared-storage
55
repository: https://github.com/lakscastro/shared-storage

0 commit comments

Comments
 (0)