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
66 changes: 66 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,69 @@
## 1.2.0

* New Feature: Added method `all()` for `DocMan.dir` helper.
It's a simple way to get all app directories in one call as map.

```dart
/// Get all app directories as map with keys as directory names and values as paths.
final Map<String, String> dirs = await DocMan.dir.all();

///Result example:
{
"cache": "/data/user/0/com.example.app/cache",
"files": "/data/user/0/com.example.app/files",
"data": "/data/user/0/com.example.app/app_flutter",
// External directories, can be empty strings if not available.
"cacheExt": "/storage/emulated/0/Android/data/com.example.app/cache",
"filesExt": "/storage/emulated/0/Android/data/com.example.app/files",
}
```
* Feature: Added ability to instantiate `DocumentThumbnail` from `Content Uri` or `File.path`.
Same as `DocumentFile.thumbnail()` method, but now you can get `DocumentThumbnail` directly.

```dart
/// Create thumbnail from content uri or file path.
final DocumentThumbnail thumbnail = await DocumentThumbnail.fromUri(
contentUriOrFilePath,
width: 100,
height: 100,
png: true,
);
```
* Feature: Added syntax sugar for `DocumentFile` instantiation from `Content Uri` or `File.path`.

```dart
/// New syntax sugar for DocumentFile instantiation from content uri or file path.
final DocumentFile? doc = await DocumentFile.fromUri(contentUriOrFilePath);

/// Old way.
final DocumentFile? doc = await DocumentFile(contentUriOrFilePath).get();
```
* Fix: problem with parallel calls to `DocumentFile` `action` methods, when working with different
documents.
Now it's fixed. `DocManQueueManager` was primarily used for all `activity` methods, and it caused the problem.
For example, now it's possible to create list or grid of documents thumbnails without any problems.
If you were getting errors in log like `Error loading thumbnail: AlreadyRunning Method: documentfileaction`, it should
be fixed now.

* Fix: error in syntax in methods `DocumentFile.share()` & `DocumentFile.open()`.
String `title` parameter was not optional, but it should be. Now it's fixed.

Please check your code and change `title` parameter to optional if you are using these methods.

**From:**
```dart
final bool share = await doc.share('Share this document:');
final bool open = await doc.open('Open with:');
```

**To:**
```dart
final bool share = await doc.share(title: 'Share this document:');
final bool open = await doc.open(title: 'Open with:');
```

* Chore: Updated dependencies, updated example, updated README, some code cleanup & small fixes.

## 1.1.0

* New Feature: Implemented simple custom `DocumentsProvider`
Expand Down
46 changes: 37 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
[![License: MIT](https://img.shields.io/github/license/devdfcom/docman?style=flat&color=mediumseagreen)](https://opensource.org/licenses/MIT)
[![Request a feature](https://img.shields.io/badge/Request-Feature-teal?style=flat)](https://github.com/devdfcom/docman/discussions/new?category=ideas)
[![Ask a question](https://img.shields.io/badge/Ask-Question-royalblue?style=flat)](https://github.com/devdfcom/docman/discussions/new/choose)
[![Report a bug](https://img.shields.io/badge/Report-Bug-indianred?style=flat)](https://github.com/devdfcom/docman/issues/new?labels=bug&projects=&template=bug_report.yml&title=%3Ctitle%3E)
[![Report a bug](https://img.shields.io/badge/Report-Bug-indianred?style=flat)](https://github.com/devdfcom/docman/issues/new?labels=bug&projects=&template=bug_report.yml)

A Flutter plugin that simplifies file & directory operations on Android devices.
Leveraging the Storage Access Framework (SAF) API,
Expand Down Expand Up @@ -277,7 +277,7 @@ Only `cacheExt` & `filesExt` can be empty strings if external storage is not ava
///Get all directories at once via helper method
Future<void> getAllDirs() async {
final Map<String, String> dirs = await DocMan.dir.all();

print(dirs);
}

Expand Down Expand Up @@ -504,6 +504,9 @@ There are two ways to instantiate a `DocumentFile`:
```dart
Future<DocumentFile?> backupDir() =>
DocumentFile(uri: 'content://com.android.externalstorage.documents/tree/primary%3ADocMan').get();
///Or you can use static method fromUri
Future<DocumentFile?> backupDir() =>
DocumentFile.fromUri('content://com.android.externalstorage.documents/tree/primary%3ADocMan');
```

> [!CAUTION]
Expand All @@ -516,9 +519,13 @@ There are two ways to instantiate a `DocumentFile`:

```dart
Future<DocumentFile?> file() => DocumentFile(uri: 'path/to/file.jpg').get();
///Or you can use static method fromUri
Future<DocumentFile?> file() => DocumentFile.fromUri('path/to/file.jpg');

/// If directory doesn't exist, it will create all directories in the path.
Future<DocumentFile?> dir() => DocumentFile(uri: 'path/to/some/directory/notCreatedYet').get();
///Or you can use static method fromUri
Future<DocumentFile?> dir() => DocumentFile.fromUri('path/to/some/directory/notCreatedYet');
```

#### **DocumentFile Activity methods**
Expand All @@ -532,15 +539,19 @@ Like `open`, `share`, `saveTo` methods. All methods are called through Activity
the system will show a dialog to choose the app to open with.
Action can be performed only on file & file must exist.

```dart
Future<bool> openFile(DocumentFile file) => file.open('Open with:');
```
* `title` parameter is optional, and not working on all devices, depends on the system.

```dart
Future<bool> openFile(DocumentFile file) => file.open(title: 'Open with:'); //or file.open();
```

- `share` `📄` Share the file with other apps.

```dart
Future<bool> shareFile(DocumentFile file) => file.share('Share with:');
```
* `title` parameter is optional, and not working on all devices, depends on the system.

```dart
Future<bool> shareFile(DocumentFile file) => file.share(title: 'Share with:'); //or file.share();
```

- `saveTo` `📄` Save the file to the selected directory.

Expand Down Expand Up @@ -736,6 +747,8 @@ and can be performed in the background (with isolates or WorkManager).
file.moveTo('/data/user/0/devdf.plugins.docman_example/cache/TempDir', name: 'moved_file.txt');
```

<a name="documentfile-action-thumbnail"></a>

- `thumbnail` `📄` Get the thumbnail of the file.

Can be used only on file & file must exist & has flag `canThumbnail` set to `true`.
Expand Down Expand Up @@ -768,9 +781,24 @@ and can be performed in the background (with isolates or WorkManager).

#### 🧩 **DocumentThumbnail class**

`DocumentThumbnail` is a data class that holds information about the thumbnail image.
`DocumentThumbnail` is a class that holds information about the thumbnail image.
It stores the `width`, `height` of the image, and the `bytes` (Uint8List) of the image.

`📄` You can instantiate the `DocumentThumbnail` via static method `fromUri()` or from `DocumentFile` instance via
[`DocumentFile.thumbnail()`](#documentfile-action-thumbnail) method.

You must specify the width & height of the thumbnail. Optionally you can specify the quality of the image
and set `png` or `webp` to `true` to get the compressed image in that format, otherwise it will be `jpeg`.
Returns [DocumentThumbnail](#-documentthumbnail-class) instance of the thumbnail image or `null` if the thumbnail is
not available. Commonly used for images, videos, pdfs.

```dart
/// It can be instantiated from `Content Uri` or `File.path` via static method.
Future<DocumentThumbnail?> getThumbnail(String contentUriOrFilePath) async {
return await DocumentThumbnail.fromUri(contentUriOrFilePath, width: 192, height: 192, png: true, quality: 100);
}
```

#### **Unsupported methods**

Information about currently (temporarily) unsupported methods in the plugin.
Expand Down
14 changes: 7 additions & 7 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ group = "devdf.plugins.docman"
version = "1.0-SNAPSHOT"

buildscript {
ext.kotlin_version = "1.8.22"
ext.kotlin_version = "2.0.20"
repositories {
google()
mavenCentral()
}

dependencies {
classpath("com.android.tools.build:gradle:8.5.2")
classpath("com.android.tools.build:gradle:8.8.0")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version")
}
}
Expand All @@ -29,15 +29,15 @@ android {
namespace = "devdf.plugins.docman"
}

compileSdk = 34
compileSdk = 35

compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}

kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8
jvmTarget = JavaVersion.VERSION_17
}

sourceSets {
Expand Down Expand Up @@ -69,5 +69,5 @@ android {

dependencies {
implementation 'androidx.documentfile:documentfile:1.0.1'
implementation 'androidx.activity:activity-ktx:1.9.3'
implementation 'androidx.activity:activity-ktx:1.10.0'
}
6 changes: 3 additions & 3 deletions example/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ android {
ndkVersion = flutter.ndkVersion

compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}

kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8
jvmTarget = JavaVersion.VERSION_17
}

defaultConfig {
Expand Down
2 changes: 1 addition & 1 deletion example/android/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip
4 changes: 2 additions & 2 deletions example/android/settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ pluginManagement {

plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "8.1.4" apply false
id "org.jetbrains.kotlin.android" version "1.8.22" apply false
id "com.android.application" version "8.8.0" apply false
id "org.jetbrains.kotlin.android" version "2.0.20" apply false
}

include ":app"
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,10 @@ class _ActionsDocumentFileState extends State<ActionsDocumentFile> {
? exception.title
: 'Permissions for: ${_document?.name}',
subTitle: exception?.subTitle,
result: exception?.result ?? perms.toString(),
result: exception?.result ??
(perms == null
? 'DocumentFile has no persisted permissions'
: perms.toString()),
isResultOk: exception == null && perms != null,
),
]);
Expand Down Expand Up @@ -431,24 +434,44 @@ class _ActionsDocumentFileState extends State<ActionsDocumentFile> {
)
]);
} else {
//2.1. Set thumbnail method
//There are currently 2 ways to get thumbnail
// First to get thumbnail via `document.thumbnail()` method or by instantiating `DocumentThumbnail` class
// Both methods are same, depending on what you prefer
// Here commented out example of `document.thumbnail()` method, old one, just to show the difference

/// final thumbnailMethod = _document!.thumbnail(
/// width: _thumbWidth,
/// height: _thumbHeight,
/// quality: _thumbQuality,
/// png: png,
/// webp: webp,
/// );

//2.2. Get thumbnail via `DocumentThumbnail.fromUri()` method
final thumbnailMethod = DocumentThumbnail.fromUri(
_document!.uri,
width: _thumbWidth,
height: _thumbHeight,
quality: _thumbQuality,
png: png,
webp: webp,
);

//3. Set thumbnail widget to result
widget.onResultWidgets([
MethodApiWidget(
MethodApiEntry(
//name: 'DocumentFile.thumbnail(width: $_thumbWidth, height: $_thumbHeight, quality: $_thumbQuality, png: $png, webp: $webp)',
name:
'DocumentFile.thumbnail(width: $_thumbWidth, height: $_thumbHeight, quality: $_thumbQuality, png: $png, webp: $webp)',
'DocumentThumbnail.fromUri(${_document!.uri}, width: $_thumbWidth, height: $_thumbHeight, quality: $_thumbQuality, png: $png, webp: $webp)',
subTitle: 'Future intentionally delayed for 2 seconds'),
endDivider: false,
),
ThumbResultWidget(
maxWidth: _thumbWidth,
maxHeight: _thumbHeight,
getThumb: _document!.thumbnail(
width: _thumbWidth,
height: _thumbHeight,
quality: _thumbQuality,
png: png,
webp: webp),
getThumb: thumbnailMethod,
)
]);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class _ActivityDocumentFileState extends State<ActivityDocumentFile> {
MethodApiEntry? exception;

try {
isOpened = await widget.document!.open('Open Document with');
isOpened = await widget.document!.open(title: 'Open Document with');
} catch (e) {
exception = _exceptionEntry(e);
}
Expand All @@ -71,7 +71,7 @@ class _ActivityDocumentFileState extends State<ActivityDocumentFile> {
MethodApiEntry? exception;

try {
isShared = await widget.document!.share('Share Document with');
isShared = await widget.document!.share(title: 'Share Document with');
} catch (e) {
exception = _exceptionEntry(e);
}
Expand Down
10 changes: 8 additions & 2 deletions example/lib/src/ui/pages/documentfile/init_document_file.dart
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,12 @@ class _InitDocumentFileState extends State<InitDocumentFile> {
final filePath = await _getFilePath();
//2. Create DocumentFile from file path
try {
doc = await DocumentFile(uri: filePath).get();
//2.1 In DocMan v1.2.0 was added static method `fromUri` for `DocumentFile`
//So from now on it's possible to instantiate DocumentFile via `DocumentFile.fromUri(uri)`
doc = await DocumentFile.fromUri(filePath);

///Before v1.2.0 it was possible to instantiate DocumentFile only via `DocumentFile(uri: uri).get()`
///doc = await DocumentFile(uri: filePath).get();
} catch (e) {
exception = _exceptionEntry(e);
}
Expand All @@ -183,7 +188,8 @@ class _InitDocumentFileState extends State<InitDocumentFile> {
//4. Update the result
widget.onResult([
MethodApiEntry(
name: 'DocumentFile(uri: $filePath).get()',
//name: DocumentFile(uri: $filePath).get()
name: 'DocumentFile.fromUri($filePath)',
title: exception != null
? exception.title
: 'DocumentFile: ${doc?.name}',
Expand Down
11 changes: 11 additions & 0 deletions lib/src/data/document_file.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:docman/docman.dart';
import 'package:flutter/material.dart';

/// A class representing a `DocumentFile` on dart side.
Expand Down Expand Up @@ -93,6 +94,16 @@ class DocumentFile {
canThumbnail: map['canThumbnail'] as bool,
);

/// Instantiates a [DocumentFile] from a Content URI or a File path.
///
/// Same as `DocumentFile(uri: uri).get()`, just syntactic sugar.
///
/// **Note**: Or you can use old method `DocumentFile(uri: uri).get()` to get the `DocumentFile` instance.
///
/// Returns a [DocumentFile] instance or `null` if the document is not available.
static Future<DocumentFile?> fromUri(String uri) =>
DocumentFile(uri: uri).get();

/// Checks if the document is a directory.
bool get isDirectory => type == 'directory';

Expand Down
Loading
Loading