Skip to content

Commit 394cf26

Browse files
author
Laks Castro
authored
Merge pull request #100 from lakscastro/master
Rollup `v0.5.0`
2 parents 2d9a0c9 + e8a77fa commit 394cf26

31 files changed

+1280
-496
lines changed

CHANGELOG.md

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,39 @@
1+
## 0.5.0
2+
3+
This release contains:
4+
5+
- Major breaking changes.
6+
- New API to edit existing files.
7+
- Example project improvements.
8+
- Bug fixes.
9+
10+
To see details, refer to rollup PR [#100](https://github.com/lakscastro/shared-storage/pull/100).
11+
12+
### New
13+
14+
- Added `writeToFile`, `writeToFileAsString` and `writeToFileAsBytes` APIs to allow overwrite existing files by appending (`FileMode.append`) or truncating `FileMode.write` (@jfaltis).
15+
16+
### Breaking changes
17+
18+
- `listFiles` it's now returns a `Stream<DocumentFile>` instead of `Stream<PartialDocumentFile>`.
19+
- `DocumentFile.lastModified` it's now returns a `DateTime?` instead of `Future<DateTime?>` (removed the asynchronous plugin call).
20+
- All `DocumentFile` class fields are now nullable except by `DocumentFile.uri`.
21+
- `createFile` doesn't requires `content` or `bytes` anymore, it's now possible to just create the file reference without defining the file data, it'll be a empty `String` by default.
22+
23+
### Bug fixes
24+
25+
- `DocumentFile.canRead` it's now calling the right API (`canRead`) instead of the similar one (`canWrite`).
26+
- [Fix](https://github.com/lakscastro/shared-storage/pull/100/files#diff-6f516633fcc1095b16ad5e0cc2a2c9711ee903cb115835d703f3c0ccfd6e0d31R38-R62) infinite loading of `getDocumentThumbnail` API when thumbnail is not available.
27+
28+
### Example project
29+
30+
- The example project is no longer dependant of `permission_handler` plugin to request `storage` permission since it's already fully integrated with Storage Access Framework.
31+
- File cards have now a expanded and collapsed state instead of showing all data at once.
32+
- Icon thumbnails were added to `.apk` `image/*`, `video/*`, `text/plain` and `directories` to make easier to see what is the type of the file while navigating between the folders.
33+
- 4 new buttons were added related to `writeToFile` API: _Write to file_ (Overwrite file contents with a predefined string), _Append to file_ (Append a predefined string to the end of the file), _Ease file content_ (Self explanatory: erase it's data but do not delete the file) and _Edit file content_ (Prompt the user with a text field to define the new file content), all buttons requires confirmation since **it can cause data loss**.
34+
- It's now possible to create a file with a custom name through the UI (_Create a custom document_ action button on top center of the file list page).
35+
- File card now shows the decoded uris to fix the visual pollution.
36+
137
## 0.4.2
238

339
Minimal hotfix:
@@ -40,7 +76,7 @@ Minor improvements and bug fixes:
4076

4177
Major release focused on support for `Storage Access Framework`.
4278

43-
### Breaking Changes
79+
### Breaking changes
4480

4581
- `minSdkVersion` set to `19`.
4682
- `getMediaStoreContentDirectory` return type changed to `Uri`.
@@ -50,7 +86,7 @@ Major release focused on support for `Storage Access Framework`.
5086
- `import 'package:shared_storage/media_store.dart' as mediastore;` to enable **Media Store** API.
5187
- `import 'package:shared_storage/shared_storage' as sharedstorage;` if you want to import all above and as a single module (Not recommended because can conflict/override names/methods).
5288

53-
### New Features
89+
### New
5490

5591
See the label [reference here](/docs/Usage/API%20Labeling.md).
5692

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/DocumentsContractApi.kt

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -35,30 +35,31 @@ internal class DocumentsContractApi(private val plugin: SharedStoragePlugin) :
3535
val width = call.argument<Int>("width")!!
3636
val height = call.argument<Int>("height")!!
3737

38-
val bitmap =
39-
DocumentsContract.getDocumentThumbnail(
40-
plugin.context.contentResolver,
41-
uri,
42-
Point(width, height),
43-
null
44-
)
38+
val bitmap = DocumentsContract.getDocumentThumbnail(
39+
plugin.context.contentResolver,
40+
uri,
41+
Point(width, height),
42+
null
43+
)
4544

46-
CoroutineScope(Dispatchers.Default).launch {
47-
if (bitmap != null) {
45+
if (bitmap != null) {
46+
CoroutineScope(Dispatchers.Default).launch {
4847
val base64 = bitmapToBase64(bitmap)
4948

5049
val data =
51-
mapOf(
52-
"base64" to base64,
53-
"uri" to "$uri",
54-
"width" to bitmap.width,
55-
"height" to bitmap.height,
56-
"byteCount" to bitmap.byteCount,
57-
"density" to bitmap.density
58-
)
50+
mapOf(
51+
"base64" to base64,
52+
"uri" to "$uri",
53+
"width" to bitmap.width,
54+
"height" to bitmap.height,
55+
"byteCount" to bitmap.byteCount,
56+
"density" to bitmap.density
57+
)
5958

6059
launch(Dispatchers.Main) { result.success(data) }
6160
}
61+
} else {
62+
result.success(null)
6263
}
6364
} else {
6465
result.notSupported(call.method, API_21)

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

Lines changed: 60 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -39,74 +39,58 @@ fun documentFromUri(
3939

4040

4141
/**
42-
* Standard map encoding of a `DocumentFile` and must be used before returning any `DocumentFile`
43-
* from plugin results, like:
44-
* ```dart
45-
* result.success(createDocumentFileMap(documentFile))
46-
* ```
42+
* Convert a [DocumentFile] using the default method for map encoding
4743
*/
4844
fun createDocumentFileMap(documentFile: DocumentFile?): Map<String, Any?>? {
4945
if (documentFile == null) return null
5046

51-
return mapOf(
52-
"isDirectory" to documentFile.isDirectory,
53-
"isFile" to documentFile.isFile,
54-
"isVirtual" to documentFile.isVirtual,
55-
"name" to (documentFile.name ?: ""),
56-
"type" to (documentFile.type ?: ""),
57-
"uri" to "${documentFile.uri}",
58-
"exists" to "${documentFile.exists()}"
47+
return createDocumentFileMap(
48+
DocumentsContract.getDocumentId(documentFile.uri),
49+
parentUri = documentFile.parentFile?.uri,
50+
isDirectory = documentFile.isDirectory,
51+
isFile = documentFile.isFile,
52+
isVirtual = documentFile.isVirtual,
53+
name = documentFile.name,
54+
type = documentFile.type,
55+
uri = documentFile.uri,
56+
exists = documentFile.exists(),
57+
size = documentFile.length(),
58+
lastModified = documentFile.lastModified()
5959
)
6060
}
6161

62-
6362
/**
64-
* Standard map encoding of a row result of a `DocumentFile`
65-
* ```kt
63+
* Standard map encoding of a `DocumentFile` and must be used before returning any `DocumentFile`
64+
* from plugin results, like:
65+
* ```dart
6666
* result.success(createDocumentFileMap(documentFile))
6767
* ```
68-
*
69-
* Example:
70-
* ```py
71-
* input = {
72-
* "last_modified": 2939496, # Key from DocumentsContract.Document.COLUMN_LAST_MODIFIED
73-
* "_display_name": "MyFile" # Key from DocumentsContract.Document.COLUMN_DISPLAY_NAME
74-
* }
75-
*
76-
* output = createCursorRowMap(input)
77-
*
78-
* print(output)
79-
* {
80-
* "lastModified": 2939496,
81-
* "displayName": "MyFile"
82-
* }
83-
* ```
8468
*/
85-
fun createCursorRowMap(
86-
parentUri: Uri,
69+
fun createDocumentFileMap(
70+
id: String?,
71+
parentUri: Uri?,
72+
isDirectory: Boolean?,
73+
isFile: Boolean?,
74+
isVirtual: Boolean?,
75+
name: String?,
76+
type: String?,
8777
uri: Uri,
88-
data: Map<String, Any>,
89-
isDirectory: Boolean?
90-
): Map<String, Any> {
91-
val values = DocumentFileColumn.values()
92-
93-
val formattedData = mutableMapOf<String, Any>()
94-
95-
for (value in values) {
96-
val key = parseDocumentFileColumn(value)
97-
98-
if (data[key] != null) {
99-
formattedData[documentFileColumnToRawString(value)!!] = data[key]!!
100-
}
101-
}
102-
78+
exists: Boolean?,
79+
size: Long?,
80+
lastModified: Long?
81+
): Map<String, Any?> {
10382
return mapOf(
104-
"data" to formattedData,
105-
"metadata" to mapOf(
106-
"parentUri" to "$parentUri",
107-
"isDirectory" to isDirectory,
108-
"uri" to "$uri"
109-
)
83+
"id" to id,
84+
"parentUri" to "$parentUri",
85+
"isDirectory" to isDirectory,
86+
"isFile" to isFile,
87+
"isVirtual" to isVirtual,
88+
"name" to name,
89+
"type" to type,
90+
"uri" to "$uri",
91+
"exists" to exists,
92+
"size" to size,
93+
"lastModified" to lastModified
11094
)
11195
}
11296

@@ -130,7 +114,7 @@ fun traverseDirectoryEntries(
130114
targetUri: Uri,
131115
columns: Array<String>,
132116
rootOnly: Boolean,
133-
block: (data: Map<String, Any>, isLast: Boolean) -> Unit
117+
block: (data: Map<String, Any?>, isLast: Boolean) -> Unit
134118
): Boolean {
135119
val documentId = try {
136120
DocumentsContract.getDocumentId(targetUri)
@@ -158,7 +142,10 @@ fun traverseDirectoryEntries(
158142
if (rootOnly) emptyArray() else arrayOf(DocumentsContract.Document.COLUMN_MIME_TYPE)
159143

160144
val intrinsicColumns =
161-
arrayOf(DocumentsContract.Document.COLUMN_DOCUMENT_ID)
145+
arrayOf(
146+
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
147+
DocumentsContract.Document.COLUMN_FLAGS
148+
)
162149

163150
val projection = arrayOf(
164151
*columns,
@@ -215,11 +202,22 @@ fun traverseDirectoryEntries(
215202
}
216203

217204
block(
218-
createCursorRowMap(
219-
parent,
220-
uri,
221-
data,
222-
isDirectory = isDirectory
205+
createDocumentFileMap(
206+
parentUri = parent,
207+
uri = uri,
208+
name = data[DocumentsContract.Document.COLUMN_DISPLAY_NAME] as String?,
209+
exists = true,
210+
id = data[DocumentsContract.Document.COLUMN_DOCUMENT_ID] as String,
211+
isDirectory = isDirectory == true,
212+
isFile = isDirectory == false,
213+
isVirtual = if (Build.VERSION.SDK_INT >= API_24) {
214+
(data[DocumentsContract.Document.COLUMN_FLAGS] as Int and DocumentsContract.Document.FLAG_VIRTUAL_DOCUMENT) != 0
215+
} else {
216+
false
217+
},
218+
type = data[DocumentsContract.Document.COLUMN_MIME_TYPE] as String?,
219+
size = data[DocumentsContract.Document.COLUMN_SIZE] as Long?,
220+
lastModified = data[DocumentsContract.Document.COLUMN_LAST_MODIFIED] as Long?
223221
),
224222
dirNodes.isEmpty() && cursor.isLast
225223
)

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ enum class DocumentFileColumn {
1616

1717
enum class DocumentFileColumnType {
1818
LONG,
19-
STRING
19+
STRING,
20+
INT
2021
}
2122

2223
fun parseDocumentFileColumn(column: String): DocumentFileColumn? {
@@ -66,7 +67,8 @@ fun typeOfColumn(column: String): DocumentFileColumnType? {
6667
DocumentsContract.Document.COLUMN_MIME_TYPE to DocumentFileColumnType.STRING,
6768
DocumentsContract.Document.COLUMN_SIZE to DocumentFileColumnType.LONG,
6869
DocumentsContract.Document.COLUMN_SUMMARY to DocumentFileColumnType.STRING,
69-
DocumentsContract.Document.COLUMN_LAST_MODIFIED to DocumentFileColumnType.LONG
70+
DocumentsContract.Document.COLUMN_LAST_MODIFIED to DocumentFileColumnType.LONG,
71+
DocumentsContract.Document.COLUMN_FLAGS to DocumentFileColumnType.INT
7072
)
7173

7274
return values[column]
@@ -76,5 +78,6 @@ fun cursorHandlerOf(type: DocumentFileColumnType): (Cursor, Int) -> Any {
7678
when(type) {
7779
DocumentFileColumnType.LONG -> { return { cursor, index -> cursor.getLong(index) } }
7880
DocumentFileColumnType.STRING -> { return { cursor, index -> cursor.getString(index) } }
81+
DocumentFileColumnType.INT -> { return { cursor, index -> cursor.getInt(index) } }
7982
}
8083
}

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"
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ dependencies:
77
shared_storage: v0.3.0
88
```
99
10-
## SDK Constraint
10+
## SDK constraint
1111
1212
In `android\app\build.gradle` set `android.defaultConfig.minSdkVersion` to `19`:
1313

@@ -22,7 +22,7 @@ android {
2222
}
2323
```
2424

25-
## Plugin Import
25+
## Plugin import
2626

2727
Although this import is still supported:
2828

0 commit comments

Comments
 (0)