Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
github: [alexrintt]
22 changes: 20 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,28 @@
## 0.7.0

- New APIs and options.
- There's no major breaking changes when updating to `v0.7.0` but there are deprecation notices over Media Store and Environment API.

### New

- `openDocument` API with single and multiple files support @honjow.
- `openDocumentTree` it now also supports `persistablePermission` option which flags an one-time operation to avoid unused permission issues.

### Deprecation notices

- 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.

### Example project

- Added a new button that implements `openDocument` API.

## 0.6.0

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

### Breaking changes

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

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

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

### Deprecation Notices
### Deprecation notices

- `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.

Expand Down
21 changes: 13 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<img src="https://img.shields.io/badge/Flutter-22272E?style=for-the-badge&logo=flutter&logoColor=66B1F1">
</p>

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

## Documentation

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

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

## Supporters

- [aplicatii-romanesti](https://www.bibliotecaortodoxa.ro/) who bought me a whole month of caffeine!

## Contributors

- [clragon](https://github.com/clragon) submitted a severe bug report and opened discussions around package architecture, thanks!
- [jfaltis](https://github.com/jfaltis) fixed a memory leak and implemented an API to override existing files, thanks for you contribution!
- [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!
- Thanks [dhaval-k-simformsolutions](https://github.com/dhaval-k-simformsolutions) for taking time to submit bug reports related to duplicated file entries!
- [dangilbert](https://github.com/dangilbert) pointed and fixed bug when the user doesn't select a folder, thanks man!
- A huge thanks to [aplicatii-romanesti](https://www.bibliotecaortodoxa.ro/) for taking time to submit device specific issues and for supporting the project!
- I would thanks [ankitparmar007](https://github.com/ankitparmar007) for discussing and requesting create file related APIs!
- [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!
- [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!
- [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!
- [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!
- 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!
- [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!
- 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)!
- 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)!

<br>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import android.provider.DocumentsContract
import android.util.Log
import androidx.annotation.RequiresApi
import androidx.documentfile.provider.DocumentFile
import com.anggrayudi.storage.extension.isTreeDocumentFile
import com.anggrayudi.storage.file.child
import io.flutter.plugin.common.*
import io.flutter.plugin.common.EventChannel.StreamHandler
Expand Down Expand Up @@ -60,6 +61,10 @@ internal class DocumentFileApi(private val plugin: SharedStoragePlugin) :
result.notSupported(call.method, API_21)
}
}
OPEN_DOCUMENT ->
if (Build.VERSION.SDK_INT >= API_21) {
openDocument(call, result)
}
OPEN_DOCUMENT_TREE ->
if (Build.VERSION.SDK_INT >= API_21) {
openDocumentTree(call, result)
Expand Down Expand Up @@ -250,6 +255,32 @@ internal class DocumentFileApi(private val plugin: SharedStoragePlugin) :
}
}

@RequiresApi(API_21)
private fun openDocument(call: MethodCall, result: MethodChannel.Result) {
val initialUri = call.argument<String>("initialUri")

val intent =
Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)

if (initialUri != null) {
val tree = DocumentFile.fromTreeUri(plugin.context, Uri.parse(initialUri))
if (Build.VERSION.SDK_INT >= API_26) {
putExtra(DocumentsContract.EXTRA_INITIAL_URI, tree?.uri)
}
}

type = call.argument<String>("mimeType") ?: "*/*"
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, call.argument<Boolean>("multiple") ?: false)
}

if (pendingResults[OPEN_DOCUMENT_CODE] != null) return

pendingResults[OPEN_DOCUMENT_CODE] = Pair(call, result)

plugin.binding?.activity?.startActivityForResult(intent, OPEN_DOCUMENT_CODE)
}

@RequiresApi(API_21)
private fun openDocumentTree(call: MethodCall, result: MethodChannel.Result) {
val grantWritePermission = call.argument<Boolean>("grantWritePermission")!!
Expand Down Expand Up @@ -349,7 +380,8 @@ internal class DocumentFileApi(private val plugin: SharedStoragePlugin) :
"isReadPermission" to it.isReadPermission,
"isWritePermission" to it.isWritePermission,
"persistedTime" to it.persistedTime,
"uri" to "${it.uri}"
"uri" to "${it.uri}",
"isTreeDocumentFile" to it.uri.isTreeDocumentFile
)
}
.toList()
Expand All @@ -373,16 +405,19 @@ internal class DocumentFileApi(private val plugin: SharedStoragePlugin) :
val pendingResult = pendingResults[OPEN_DOCUMENT_TREE_CODE] ?: return false

val grantWritePermission = pendingResult.first.argument<Boolean>("grantWritePermission")!!
val persistablePermission = pendingResult.first.argument<Boolean>("persistablePermission")!!

try {
val uri = data?.data

if (uri != null) {
plugin.context.contentResolver.takePersistableUriPermission(
if (persistablePermission) {
plugin.context.contentResolver.takePersistableUriPermission(
uri,
if (grantWritePermission) Intent.FLAG_GRANT_WRITE_URI_PERMISSION
else Intent.FLAG_GRANT_READ_URI_PERMISSION
)
)
}

pendingResult.second.success("$uri")

Expand All @@ -394,6 +429,39 @@ internal class DocumentFileApi(private val plugin: SharedStoragePlugin) :
pendingResults.remove(OPEN_DOCUMENT_TREE_CODE)
}
}
OPEN_DOCUMENT_CODE -> {
val pendingResult = pendingResults[OPEN_DOCUMENT_CODE] ?: return false

val grantWritePermission = pendingResult.first.argument<Boolean>("grantWritePermission")!!
val persistablePermission = pendingResult.first.argument<Boolean>("persistablePermission")!!

try {
// if data.clipData not null, uriList from data.clipData, else uriList is data.data
val uriList = data?.clipData?.let {
(0 until it.itemCount).map { i -> it.getItemAt(i).uri }
} ?: data?.data?.let { listOf(it) }

if (uriList != null) {
if (persistablePermission) {
for (uri in uriList) {
plugin.context.contentResolver.takePersistableUriPermission(
uri,
if (grantWritePermission) Intent.FLAG_GRANT_WRITE_URI_PERMISSION
else Intent.FLAG_GRANT_READ_URI_PERMISSION
)
}
}

pendingResult.second.success(uriList.map { "$it" })

return true
}

pendingResult.second.success(null)
} finally {
pendingResults.remove(OPEN_DOCUMENT_CODE)
}
}
}

return false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const val DOCUMENTS_CONTRACT_EXTRA_INITIAL_URI =
/**
* Available DocumentFile Method Channel APIs
*/
const val OPEN_DOCUMENT = "openDocument"
const val OPEN_DOCUMENT_TREE = "openDocumentTree"
const val PERSISTED_URI_PERMISSIONS = "persistedUriPermissions"
const val RELEASE_PERSISTABLE_URI_PERMISSION = "releasePersistableUriPermission"
Expand Down Expand Up @@ -54,3 +55,4 @@ const val GET_DOCUMENT_CONTENT = "getDocumentContent"
* Intent Request Codes
*/
const val OPEN_DOCUMENT_TREE_CODE = 10
const val OPEN_DOCUMENT_CODE = 11
12 changes: 12 additions & 0 deletions docs/Migrate notes/Migrate to v0.7.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
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.

Update your `pubspec.yaml`:

```yaml
dependencies:
shared_storage: ^0.7.0
```

## Deprecation notices

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.
2 changes: 2 additions & 0 deletions docs/Usage/Environment.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
> **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.

## Import package

```dart
Expand Down
2 changes: 2 additions & 0 deletions docs/Usage/Media Store.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
> **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.

## Import package

```dart
Expand Down
40 changes: 40 additions & 0 deletions docs/Usage/Storage Access Framework.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,46 @@ if (grantedUri != null) {
}
```

### <samp>openDocument</samp>

Same as `openDocumentTree` but for file URIs, you can request user to select a file and filter by:

- Single or multiple files.
- Mime type.

You can also specify if you want a one-time operation (`persistablePermission` = false) and if you don't need write access (`grantWritePermission` = false).

```dart
const kDownloadsFolder =
'content://com.android.externalstorage.documents/tree/primary%3ADownloads/document/primary%3ADownloads';

final List<Uri>? selectedDocumentUris = await openDocument(
// if you have a previously saved URI,
// you can use the specify the tree you user will see at startup of the file picker.
initialUri: Uri.parse(kDownloadsFolder),

// whether or not allow the user select multiple files.
multiple: true,

// whether or not the selected URIs should be persisted across app and device reboots.
persistablePermission: true,

// whether or not grant write permission required to edit file metadata (name) and it's contents.
grantWritePermission: true,

// whether or not filter by mime type.
mimeType: 'image/*' // default '*/*'
);

if (selectedDocumentUris == null) {
return print('User cancelled the operation.');
}

// If [selectedDocumentUris] are [persistablePermission]s then it will be returned by this function
// along with any another URIs you've got permission over.
final List<UriPermission> persistedUris = await persistedUriPermissions();
```

### <samp>listFiles</samp>

This method list files lazily **over a granted uri:**
Expand Down
Loading