Skip to content

Commit

Permalink
Merge pull request #100 from lakscastro/master
Browse files Browse the repository at this point in the history
Rollup `v0.5.0`
  • Loading branch information
alexrintt authored Jul 18, 2022
2 parents 2d9a0c9 + e8a77fa commit 394cf26
Show file tree
Hide file tree
Showing 31 changed files with 1,280 additions and 496 deletions.
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

0 comments on commit 394cf26

Please sign in to comment.