Skip to content

Commit 310bf9d

Browse files
author
Alex Rintt
authored
Merge pull request #111 from alexrintt/master
`v0.7.0`
2 parents 25f58f9 + b806fbd commit 310bf9d

File tree

20 files changed

+517
-118
lines changed

20 files changed

+517
-118
lines changed

.github/FUNDING.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
github: [alexrintt]

CHANGELOG.md

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,28 @@
1+
## 0.7.0
2+
3+
- New APIs and options.
4+
- There's no major breaking changes when updating to `v0.7.0` but there are deprecation notices over Media Store and Environment API.
5+
6+
### New
7+
8+
- `openDocument` API with single and multiple files support @honjow.
9+
- `openDocumentTree` it now also supports `persistablePermission` option which flags an one-time operation to avoid unused permission issues.
10+
11+
### Deprecation notices
12+
13+
- All non SAF APIs are deprecated (Media Store and Environment APIs), if you are using them, let us know by [opening an issue](https://github.com/alexrintt/shared-storage/issues/new) with your use-case so we can implement a new compatible API using a cross-platform approach.
14+
15+
### Example project
16+
17+
- Added a new button that implements `openDocument` API.
18+
119
## 0.6.0
220

321
This release contains a severe API fixes and some minor doc changes:
422

523
### Breaking changes
624

7-
- Unused arguments in `DocumentFile.getContent` and `DocumentFile.getContentAsString`. [#107](https://github.com/alexrintt/shared-storage/issues/107).
25+
- Unused arguments in `DocumentFile.getContent` and `DocumentFile.getContentAsString`. [#107](https://github.com/alexrintt/shared-storage/issues/107) @clragon.
826
- Package import it's now done through a single import.
927

1028
## 0.5.0
@@ -170,7 +188,7 @@ See the label [reference here](/docs/Usage/API%20Labeling.md).
170188

171189
- <samp>Mirror</samp> `getStorageDirectory` from [`Environment.getStorageDirectory`](https://developer.android.com/reference/android/os/Environment#getStorageDirectory%28%29).
172190

173-
### Deprecation Notices
191+
### Deprecation notices
174192

175193
- `getExternalStoragePublicDirectory` was marked as deprecated and should be replaced with an equivalent API depending on your use-case, see [how to migrate `getExternalStoragePublicDirectory`](https://stackoverflow.com/questions/56468539/getexternalstoragepublicdirectory-deprecated-in-android-q). This deprecation is originated from official Android documentation and not by the plugin itself.
176194

README.md

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
<img src="https://img.shields.io/badge/Flutter-22272E?style=for-the-badge&logo=flutter&logoColor=66B1F1">
1919
</p>
2020

21-
<a href="https://pub.dev/packages/shared_storage"><h4 align="center"><samp>Install It</samp></h4></a>
21+
<h4 align="center"><samp><a href="https://pub.dev/packages/shared_storage">Install It</a></samp></h4>
2222

2323
## Documentation
2424

@@ -34,15 +34,20 @@ Latest changes are available on `master` branch and the actual latest published
3434

3535
All other branches are derivated from issues, new features or bug fixes.
3636

37+
## Supporters
38+
39+
- [aplicatii-romanesti](https://www.bibliotecaortodoxa.ro/) who bought me a whole month of caffeine!
40+
3741
## Contributors
3842

39-
- [clragon](https://github.com/clragon) submitted a severe bug report and opened discussions around package architecture, thanks!
40-
- [jfaltis](https://github.com/jfaltis) fixed a memory leak and implemented an API to override existing files, thanks for you contribution!
41-
- [EternityForest](https://github.com/EternityForest) did fix a severe crash when the ID column was not provided and implemented a new feature to list all subfolders, thanks man!
42-
- Thanks [dhaval-k-simformsolutions](https://github.com/dhaval-k-simformsolutions) for taking time to submit bug reports related to duplicated file entries!
43-
- [dangilbert](https://github.com/dangilbert) pointed and fixed bug when the user doesn't select a folder, thanks man!
44-
- A huge thanks to [aplicatii-romanesti](https://www.bibliotecaortodoxa.ro/) for taking time to submit device specific issues and for supporting the project!
45-
- I would thanks [ankitparmar007](https://github.com/ankitparmar007) for discussing and requesting create file related APIs!
43+
- [honjow](https://github.com/honjow) contributed by [implementing `openDocument` Android API #110](https://github.com/alexrintt/shared-storage/pull/110) to pick single or multiple file URIs. Really helpful, thanks!
44+
- [clragon](https://github.com/clragon) submitted a severe [bug report #107](https://github.com/alexrintt/shared-storage/issues/107) and opened [discussions around package architecture #108](https://github.com/alexrintt/shared-storage/discussions/108), thanks!
45+
- [jfaltis](https://github.com/jfaltis) fixed [a memory leak #86](https://github.com/alexrintt/shared-storage/pull/86) and implemented an API to [override existing files #85](https://github.com/alexrintt/shared-storage/pull/85), thanks for your contribution!
46+
- [EternityForest](https://github.com/EternityForest) did [report a severe crash #50](https://github.com/alexrintt/shared-storage/issues/50) when the column ID was not provided and [implemented a new feature to list all subfolders #59](https://github.com/alexrintt/shared-storage/pull/59), thanks man!
47+
- Thanks [dhaval-k-simformsolutions](https://github.com/dhaval-k-simformsolutions) for taking time to submit [bug reports](https://github.com/alexrintt/shared-storage/issues?q=is%3Aissue+author%3Adhaval-k-simformsolutions) related to duplicated file entries!
48+
- [dangilbert](https://github.com/dangilbert) pointed and [fixed a bug #14](https://github.com/alexrintt/shared-storage/pull/14) when the user doesn't select a folder, thanks man!
49+
- A huge thanks to [aplicatii-romanesti](https://www.bibliotecaortodoxa.ro/) for taking time to submit [device specific issues](https://github.com/alexrintt/shared-storage/issues?q=author%3Aaplicatii-romanesti)!
50+
- I would thanks [ankitparmar007](https://github.com/ankitparmar007) for [discussing and requesting create file related APIs #20](https://github.com/alexrintt/shared-storage/issues/10)!
4651

4752
<br>
4853

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

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import android.provider.DocumentsContract
77
import android.util.Log
88
import androidx.annotation.RequiresApi
99
import androidx.documentfile.provider.DocumentFile
10+
import com.anggrayudi.storage.extension.isTreeDocumentFile
1011
import com.anggrayudi.storage.file.child
1112
import io.flutter.plugin.common.*
1213
import io.flutter.plugin.common.EventChannel.StreamHandler
@@ -60,6 +61,10 @@ internal class DocumentFileApi(private val plugin: SharedStoragePlugin) :
6061
result.notSupported(call.method, API_21)
6162
}
6263
}
64+
OPEN_DOCUMENT ->
65+
if (Build.VERSION.SDK_INT >= API_21) {
66+
openDocument(call, result)
67+
}
6368
OPEN_DOCUMENT_TREE ->
6469
if (Build.VERSION.SDK_INT >= API_21) {
6570
openDocumentTree(call, result)
@@ -250,6 +255,32 @@ internal class DocumentFileApi(private val plugin: SharedStoragePlugin) :
250255
}
251256
}
252257

258+
@RequiresApi(API_21)
259+
private fun openDocument(call: MethodCall, result: MethodChannel.Result) {
260+
val initialUri = call.argument<String>("initialUri")
261+
262+
val intent =
263+
Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
264+
addCategory(Intent.CATEGORY_OPENABLE)
265+
266+
if (initialUri != null) {
267+
val tree = DocumentFile.fromTreeUri(plugin.context, Uri.parse(initialUri))
268+
if (Build.VERSION.SDK_INT >= API_26) {
269+
putExtra(DocumentsContract.EXTRA_INITIAL_URI, tree?.uri)
270+
}
271+
}
272+
273+
type = call.argument<String>("mimeType") ?: "*/*"
274+
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, call.argument<Boolean>("multiple") ?: false)
275+
}
276+
277+
if (pendingResults[OPEN_DOCUMENT_CODE] != null) return
278+
279+
pendingResults[OPEN_DOCUMENT_CODE] = Pair(call, result)
280+
281+
plugin.binding?.activity?.startActivityForResult(intent, OPEN_DOCUMENT_CODE)
282+
}
283+
253284
@RequiresApi(API_21)
254285
private fun openDocumentTree(call: MethodCall, result: MethodChannel.Result) {
255286
val grantWritePermission = call.argument<Boolean>("grantWritePermission")!!
@@ -349,7 +380,8 @@ internal class DocumentFileApi(private val plugin: SharedStoragePlugin) :
349380
"isReadPermission" to it.isReadPermission,
350381
"isWritePermission" to it.isWritePermission,
351382
"persistedTime" to it.persistedTime,
352-
"uri" to "${it.uri}"
383+
"uri" to "${it.uri}",
384+
"isTreeDocumentFile" to it.uri.isTreeDocumentFile
353385
)
354386
}
355387
.toList()
@@ -373,16 +405,19 @@ internal class DocumentFileApi(private val plugin: SharedStoragePlugin) :
373405
val pendingResult = pendingResults[OPEN_DOCUMENT_TREE_CODE] ?: return false
374406

375407
val grantWritePermission = pendingResult.first.argument<Boolean>("grantWritePermission")!!
408+
val persistablePermission = pendingResult.first.argument<Boolean>("persistablePermission")!!
376409

377410
try {
378411
val uri = data?.data
379412

380413
if (uri != null) {
381-
plugin.context.contentResolver.takePersistableUriPermission(
414+
if (persistablePermission) {
415+
plugin.context.contentResolver.takePersistableUriPermission(
382416
uri,
383417
if (grantWritePermission) Intent.FLAG_GRANT_WRITE_URI_PERMISSION
384418
else Intent.FLAG_GRANT_READ_URI_PERMISSION
385-
)
419+
)
420+
}
386421

387422
pendingResult.second.success("$uri")
388423

@@ -394,6 +429,39 @@ internal class DocumentFileApi(private val plugin: SharedStoragePlugin) :
394429
pendingResults.remove(OPEN_DOCUMENT_TREE_CODE)
395430
}
396431
}
432+
OPEN_DOCUMENT_CODE -> {
433+
val pendingResult = pendingResults[OPEN_DOCUMENT_CODE] ?: return false
434+
435+
val grantWritePermission = pendingResult.first.argument<Boolean>("grantWritePermission")!!
436+
val persistablePermission = pendingResult.first.argument<Boolean>("persistablePermission")!!
437+
438+
try {
439+
// if data.clipData not null, uriList from data.clipData, else uriList is data.data
440+
val uriList = data?.clipData?.let {
441+
(0 until it.itemCount).map { i -> it.getItemAt(i).uri }
442+
} ?: data?.data?.let { listOf(it) }
443+
444+
if (uriList != null) {
445+
if (persistablePermission) {
446+
for (uri in uriList) {
447+
plugin.context.contentResolver.takePersistableUriPermission(
448+
uri,
449+
if (grantWritePermission) Intent.FLAG_GRANT_WRITE_URI_PERMISSION
450+
else Intent.FLAG_GRANT_READ_URI_PERMISSION
451+
)
452+
}
453+
}
454+
455+
pendingResult.second.success(uriList.map { "$it" })
456+
457+
return true
458+
}
459+
460+
pendingResult.second.success(null)
461+
} finally {
462+
pendingResults.remove(OPEN_DOCUMENT_CODE)
463+
}
464+
}
397465
}
398466

399467
return false

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const val DOCUMENTS_CONTRACT_EXTRA_INITIAL_URI =
1919
/**
2020
* Available DocumentFile Method Channel APIs
2121
*/
22+
const val OPEN_DOCUMENT = "openDocument"
2223
const val OPEN_DOCUMENT_TREE = "openDocumentTree"
2324
const val PERSISTED_URI_PERMISSIONS = "persistedUriPermissions"
2425
const val RELEASE_PERSISTABLE_URI_PERMISSION = "releasePersistableUriPermission"
@@ -54,3 +55,4 @@ const val GET_DOCUMENT_CONTENT = "getDocumentContent"
5455
* Intent Request Codes
5556
*/
5657
const val OPEN_DOCUMENT_TREE_CODE = 10
58+
const val OPEN_DOCUMENT_CODE = 11
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
There's no major breaking changes when updating to `v0.7.0` but there are deprecation notices if you are using Media Store and Environment API.
2+
3+
Update your `pubspec.yaml`:
4+
5+
```yaml
6+
dependencies:
7+
shared_storage: ^0.7.0
8+
```
9+
10+
## Deprecation notices
11+
12+
All non SAF APIs are deprecated, if you are using them, let us know by [opening an issue](https://github.com/alexrintt/shared-storage/issues/new) with your use-case so we can implement a new compatible API using a cross-platform approach.

docs/Usage/Environment.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
> **WARNING** This API is deprecated and will be removed soon. If you need it, please open an issue with your use-case to include in the next release as part of the new original cross-platform API.
2+
13
## Import package
24

35
```dart

docs/Usage/Media Store.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
> **WARNING** This API is deprecated and will be removed soon. If you need it, please open an issue with your use-case to include in the next release as part of the new original cross-platform API.
2+
13
## Import package
24

35
```dart

docs/Usage/Storage Access Framework.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,46 @@ if (grantedUri != null) {
6464
}
6565
```
6666

67+
### <samp>openDocument</samp>
68+
69+
Same as `openDocumentTree` but for file URIs, you can request user to select a file and filter by:
70+
71+
- Single or multiple files.
72+
- Mime type.
73+
74+
You can also specify if you want a one-time operation (`persistablePermission` = false) and if you don't need write access (`grantWritePermission` = false).
75+
76+
```dart
77+
const kDownloadsFolder =
78+
'content://com.android.externalstorage.documents/tree/primary%3ADownloads/document/primary%3ADownloads';
79+
80+
final List<Uri>? selectedDocumentUris = await openDocument(
81+
// if you have a previously saved URI,
82+
// you can use the specify the tree you user will see at startup of the file picker.
83+
initialUri: Uri.parse(kDownloadsFolder),
84+
85+
// whether or not allow the user select multiple files.
86+
multiple: true,
87+
88+
// whether or not the selected URIs should be persisted across app and device reboots.
89+
persistablePermission: true,
90+
91+
// whether or not grant write permission required to edit file metadata (name) and it's contents.
92+
grantWritePermission: true,
93+
94+
// whether or not filter by mime type.
95+
mimeType: 'image/*' // default '*/*'
96+
);
97+
98+
if (selectedDocumentUris == null) {
99+
return print('User cancelled the operation.');
100+
}
101+
102+
// If [selectedDocumentUris] are [persistablePermission]s then it will be returned by this function
103+
// along with any another URIs you've got permission over.
104+
final List<UriPermission> persistedUris = await persistedUriPermissions();
105+
```
106+
67107
### <samp>listFiles</samp>
68108

69109
This method list files lazily **over a granted uri:**

0 commit comments

Comments
 (0)