Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
0648b7e
(#61) add writeToFile, writeToFileAsBytes, writeToFileAsString
jfaltis Jul 14, 2022
83fa900
(#85) Apply PR fixes
Jul 15, 2022
90ae52d
(#85, #79, #61) Add more actions related to the `writeToFile` API
Jul 15, 2022
46a2b7e
(#88) Replace unsecure `HTTP` with `HTTPS` link
Jul 15, 2022
83f5ad7
(#89) Update description to match `pub.dev` requirements
Jul 15, 2022
74e8700
(#85) Update plugin version to `v0.5.0`
Jul 15, 2022
0815bd2
(#89) Remove redundant `with`
Jul 15, 2022
ace3244
Merge pull request #85 from jfaltis/feature/write-to-file
Jul 16, 2022
d2518c5
Merge pull request #95 from lakscastro/hotfix/replace-unsecure-http-link
Jul 16, 2022
13dbaf9
Merge pull request #96 from lakscastro/hotfix/fix-plugin-description
Jul 16, 2022
75d3592
Merge branch 'master' into chore/set-plugin-version-v0.5.0
Jul 16, 2022
a94a03b
Merge pull request #98 from lakscastro/chore/set-plugin-version-v0.5.0
Jul 16, 2022
07dd394
Merge branch 'master' of https://github.com/LaksCastro/shared-storage…
Jul 17, 2022
8c75cea
(#94) Add actions and refactor `listFiles` API
Jul 17, 2022
52ba947
(#90) Fixes wrong API call and doc
Jul 17, 2022
f0cc802
(#94) Add missing `toMap` fields
Jul 17, 2022
e1b3d4e
(#94) Fix `NullPointerException`
Jul 17, 2022
8e6d645
(#94) Fix `KeyValueText` doc
Jul 17, 2022
b2e9352
Merge pull request #94 from lakscastro/feat/add-write-to-file-actions
Jul 17, 2022
f619efd
Merge pull request #99 from lakscastro/bugfix/canread-api-calls-wrong…
Jul 17, 2022
ca468c5
(#100) Add `v0.5.0` change log
Jul 18, 2022
4f5c98e
(#100) Update docs to match latest breaking changes
Jul 18, 2022
b61990e
(#100) Remove `permission_handler` dependency from `/example` project
Jul 18, 2022
823bf8f
(#100) Improve `/example` confirmation dialog description
Jul 18, 2022
6cad20c
Merge pull request #101 from lakscastro/chore/changelog-v0.5.0
Jul 18, 2022
04f4e80
Merge pull request #102 from lakscastro/chore/improve-confirmation-di…
Jul 18, 2022
6bc03fd
Merge pull request #104 from lakscastro/chore/remove-permission-handl…
Jul 18, 2022
e8a77fa
Merge pull request #103 from lakscastro/chore/update-docs-v0.5.0
Jul 18, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 38 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,39 @@
## 0.5.0

This release contains:

- Major breaking changes.
- New API to edit existing files.
- Example project improvements.
- Bug fixes.

To see details, refer to rollup PR [#100](https://github.com/lakscastro/shared-storage/pull/100).

### New

- Added `writeToFile`, `writeToFileAsString` and `writeToFileAsBytes` APIs to allow overwrite existing files by appending (`FileMode.append`) or truncating `FileMode.write` (@jfaltis).

### Breaking changes

- `listFiles` it's now returns a `Stream<DocumentFile>` instead of `Stream<PartialDocumentFile>`.
- `DocumentFile.lastModified` it's now returns a `DateTime?` instead of `Future<DateTime?>` (removed the asynchronous plugin call).
- All `DocumentFile` class fields are now nullable except by `DocumentFile.uri`.
- `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.

### Bug fixes

- `DocumentFile.canRead` it's now calling the right API (`canRead`) instead of the similar one (`canWrite`).
- [Fix](https://github.com/lakscastro/shared-storage/pull/100/files#diff-6f516633fcc1095b16ad5e0cc2a2c9711ee903cb115835d703f3c0ccfd6e0d31R38-R62) infinite loading of `getDocumentThumbnail` API when thumbnail is not available.

### Example project

- 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.
- File cards have now a expanded and collapsed state instead of showing all data at once.
- 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.
- 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**.
- 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).
- File card now shows the decoded uris to fix the visual pollution.

## 0.4.2

Minimal hotfix:
Expand Down Expand Up @@ -40,7 +76,7 @@ Minor improvements and bug fixes:

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

### Breaking Changes
### Breaking changes

- `minSdkVersion` set to `19`.
- `getMediaStoreContentDirectory` return type changed to `Uri`.
Expand All @@ -50,7 +86,7 @@ Major release focused on support for `Storage Access Framework`.
- `import 'package:shared_storage/media_store.dart' as mediastore;` to enable **Media Store** API.
- `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).

### New Features
### New

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

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ These are the brilliant minds behind the development of this plugin!
<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>
<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>
<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>
<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>
<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>
</tr>
</table>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,13 @@ internal class DocumentFileApi(private val plugin: SharedStoragePlugin) :
call.argument<ByteArray>("content")!!
)
}
WRITE_TO_FILE ->
writeToFile(
result,
call.argument<String>("uri")!!,
call.argument<ByteArray>("content")!!,
call.argument<String>("mode")!!
)
PERSISTED_URI_PERMISSIONS ->
persistedUriPermissions(result)
RELEASE_PERSISTABLE_URI_PERMISSION ->
Expand Down Expand Up @@ -312,6 +319,25 @@ internal class DocumentFileApi(private val plugin: SharedStoragePlugin) :
}
}

private fun writeToFile(
result: MethodChannel.Result,
uri: String,
content: ByteArray,
mode: String
) {
try {
plugin.context.contentResolver.openOutputStream(Uri.parse(uri), mode)?.apply {
write(content)
flush()
close()

result.success(true)
}
} catch (e: Exception) {
result.success(false)
}
}

@RequiresApi(API_19)
private fun persistedUriPermissions(result: MethodChannel.Result) {
val persistedUriPermissions = plugin.context.contentResolver.persistedUriPermissions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,30 +35,31 @@ internal class DocumentsContractApi(private val plugin: SharedStoragePlugin) :
val width = call.argument<Int>("width")!!
val height = call.argument<Int>("height")!!

val bitmap =
DocumentsContract.getDocumentThumbnail(
plugin.context.contentResolver,
uri,
Point(width, height),
null
)
val bitmap = DocumentsContract.getDocumentThumbnail(
plugin.context.contentResolver,
uri,
Point(width, height),
null
)

CoroutineScope(Dispatchers.Default).launch {
if (bitmap != null) {
if (bitmap != null) {
CoroutineScope(Dispatchers.Default).launch {
val base64 = bitmapToBase64(bitmap)

val data =
mapOf(
"base64" to base64,
"uri" to "$uri",
"width" to bitmap.width,
"height" to bitmap.height,
"byteCount" to bitmap.byteCount,
"density" to bitmap.density
)
mapOf(
"base64" to base64,
"uri" to "$uri",
"width" to bitmap.width,
"height" to bitmap.height,
"byteCount" to bitmap.byteCount,
"density" to bitmap.density
)

launch(Dispatchers.Main) { result.success(data) }
}
} else {
result.success(null)
}
} else {
result.notSupported(call.method, API_21)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,74 +39,58 @@ fun documentFromUri(


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

return mapOf(
"isDirectory" to documentFile.isDirectory,
"isFile" to documentFile.isFile,
"isVirtual" to documentFile.isVirtual,
"name" to (documentFile.name ?: ""),
"type" to (documentFile.type ?: ""),
"uri" to "${documentFile.uri}",
"exists" to "${documentFile.exists()}"
return createDocumentFileMap(
DocumentsContract.getDocumentId(documentFile.uri),
parentUri = documentFile.parentFile?.uri,
isDirectory = documentFile.isDirectory,
isFile = documentFile.isFile,
isVirtual = documentFile.isVirtual,
name = documentFile.name,
type = documentFile.type,
uri = documentFile.uri,
exists = documentFile.exists(),
size = documentFile.length(),
lastModified = documentFile.lastModified()
)
}


/**
* Standard map encoding of a row result of a `DocumentFile`
* ```kt
* Standard map encoding of a `DocumentFile` and must be used before returning any `DocumentFile`
* from plugin results, like:
* ```dart
* result.success(createDocumentFileMap(documentFile))
* ```
*
* Example:
* ```py
* input = {
* "last_modified": 2939496, # Key from DocumentsContract.Document.COLUMN_LAST_MODIFIED
* "_display_name": "MyFile" # Key from DocumentsContract.Document.COLUMN_DISPLAY_NAME
* }
*
* output = createCursorRowMap(input)
*
* print(output)
* {
* "lastModified": 2939496,
* "displayName": "MyFile"
* }
* ```
*/
fun createCursorRowMap(
parentUri: Uri,
fun createDocumentFileMap(
id: String?,
parentUri: Uri?,
isDirectory: Boolean?,
isFile: Boolean?,
isVirtual: Boolean?,
name: String?,
type: String?,
uri: Uri,
data: Map<String, Any>,
isDirectory: Boolean?
): Map<String, Any> {
val values = DocumentFileColumn.values()

val formattedData = mutableMapOf<String, Any>()

for (value in values) {
val key = parseDocumentFileColumn(value)

if (data[key] != null) {
formattedData[documentFileColumnToRawString(value)!!] = data[key]!!
}
}

exists: Boolean?,
size: Long?,
lastModified: Long?
): Map<String, Any?> {
return mapOf(
"data" to formattedData,
"metadata" to mapOf(
"parentUri" to "$parentUri",
"isDirectory" to isDirectory,
"uri" to "$uri"
)
"id" to id,
"parentUri" to "$parentUri",
"isDirectory" to isDirectory,
"isFile" to isFile,
"isVirtual" to isVirtual,
"name" to name,
"type" to type,
"uri" to "$uri",
"exists" to exists,
"size" to size,
"lastModified" to lastModified
)
}

Expand All @@ -130,7 +114,7 @@ fun traverseDirectoryEntries(
targetUri: Uri,
columns: Array<String>,
rootOnly: Boolean,
block: (data: Map<String, Any>, isLast: Boolean) -> Unit
block: (data: Map<String, Any?>, isLast: Boolean) -> Unit
): Boolean {
val documentId = try {
DocumentsContract.getDocumentId(targetUri)
Expand Down Expand Up @@ -158,7 +142,10 @@ fun traverseDirectoryEntries(
if (rootOnly) emptyArray() else arrayOf(DocumentsContract.Document.COLUMN_MIME_TYPE)

val intrinsicColumns =
arrayOf(DocumentsContract.Document.COLUMN_DOCUMENT_ID)
arrayOf(
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
DocumentsContract.Document.COLUMN_FLAGS
)

val projection = arrayOf(
*columns,
Expand Down Expand Up @@ -215,11 +202,22 @@ fun traverseDirectoryEntries(
}

block(
createCursorRowMap(
parent,
uri,
data,
isDirectory = isDirectory
createDocumentFileMap(
parentUri = parent,
uri = uri,
name = data[DocumentsContract.Document.COLUMN_DISPLAY_NAME] as String?,
exists = true,
id = data[DocumentsContract.Document.COLUMN_DOCUMENT_ID] as String,
isDirectory = isDirectory == true,
isFile = isDirectory == false,
isVirtual = if (Build.VERSION.SDK_INT >= API_24) {
(data[DocumentsContract.Document.COLUMN_FLAGS] as Int and DocumentsContract.Document.FLAG_VIRTUAL_DOCUMENT) != 0
} else {
false
},
type = data[DocumentsContract.Document.COLUMN_MIME_TYPE] as String?,
size = data[DocumentsContract.Document.COLUMN_SIZE] as Long?,
lastModified = data[DocumentsContract.Document.COLUMN_LAST_MODIFIED] as Long?
),
dirNodes.isEmpty() && cursor.isLast
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ enum class DocumentFileColumn {

enum class DocumentFileColumnType {
LONG,
STRING
STRING,
INT
}

fun parseDocumentFileColumn(column: String): DocumentFileColumn? {
Expand Down Expand Up @@ -66,7 +67,8 @@ fun typeOfColumn(column: String): DocumentFileColumnType? {
DocumentsContract.Document.COLUMN_MIME_TYPE to DocumentFileColumnType.STRING,
DocumentsContract.Document.COLUMN_SIZE to DocumentFileColumnType.LONG,
DocumentsContract.Document.COLUMN_SUMMARY to DocumentFileColumnType.STRING,
DocumentsContract.Document.COLUMN_LAST_MODIFIED to DocumentFileColumnType.LONG
DocumentsContract.Document.COLUMN_LAST_MODIFIED to DocumentFileColumnType.LONG,
DocumentsContract.Document.COLUMN_FLAGS to DocumentFileColumnType.INT
)

return values[column]
Expand All @@ -76,5 +78,6 @@ fun cursorHandlerOf(type: DocumentFileColumnType): (Cursor, Int) -> Any {
when(type) {
DocumentFileColumnType.LONG -> { return { cursor, index -> cursor.getLong(index) } }
DocumentFileColumnType.STRING -> { return { cursor, index -> cursor.getString(index) } }
DocumentFileColumnType.INT -> { return { cursor, index -> cursor.getInt(index) } }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const val OPEN_DOCUMENT_TREE = "openDocumentTree"
const val PERSISTED_URI_PERMISSIONS = "persistedUriPermissions"
const val RELEASE_PERSISTABLE_URI_PERMISSION = "releasePersistableUriPermission"
const val CREATE_FILE = "createFile"
const val WRITE_TO_FILE = "writeToFile"
const val FROM_TREE_URI = "fromTreeUri"
const val CAN_WRITE = "canWrite"
const val CAN_READ = "canRead"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ dependencies:
shared_storage: v0.3.0
```

## SDK Constraint
## SDK constraint

In `android\app\build.gradle` set `android.defaultConfig.minSdkVersion` to `19`:

Expand All @@ -22,7 +22,7 @@ android {
}
```

## Plugin Import
## Plugin import

Although this import is still supported:

Expand Down
Loading