Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update dependencies, polish stuff, make FileCallback.onCompleted receive newly created FileCallback.Result and more #144

Closed
wants to merge 16 commits into from
Closed
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,5 @@ fastlane/test_output
fastlane/readme.md

# Java dump memory
*.hprof
*.hprof
/.kotlin/
31 changes: 16 additions & 15 deletions .run/publishToCentral.run.xml
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="publishToCentral" type="ShConfigurationType">
<option name="SCRIPT_TEXT" value="./gradlew :storage:publishAllPublicationsToMavenCentral" />
<option name="INDEPENDENT_SCRIPT_PATH" value="true" />
<option name="SCRIPT_PATH" value="" />
<option name="SCRIPT_OPTIONS" value="" />
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
<option name="INTERPRETER_PATH" value="/bin/zsh" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="EXECUTE_IN_TERMINAL" value="true" />
<option name="EXECUTE_SCRIPT_FILE" value="false" />
<envs />
<method v="2" />
</configuration>
<configuration name="publishToCentral" default="false" type="ShConfigurationType">
<option name="SCRIPT_TEXT"
value="./gradlew :storage:publishAllPublicationsToMavenCentral" />
<option name="INDEPENDENT_SCRIPT_PATH" value="true" />
<option name="SCRIPT_PATH" value="" />
<option name="SCRIPT_OPTIONS" value="" />
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
<option name="INTERPRETER_PATH" value="/bin/zsh" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="EXECUTE_IN_TERMINAL" value="true" />
<option name="EXECUTE_SCRIPT_FILE" value="false" />
<envs />
<method v="2" />
</configuration>
</component>
30 changes: 15 additions & 15 deletions .run/publishToLocal.run.xml
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="publishToLocal" type="ShConfigurationType">
<option name="SCRIPT_TEXT" value="./gradlew :storage:publishToMavenLocal" />
<option name="INDEPENDENT_SCRIPT_PATH" value="true" />
<option name="SCRIPT_PATH" value="" />
<option name="SCRIPT_OPTIONS" value="" />
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
<option name="INTERPRETER_PATH" value="/bin/zsh" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="EXECUTE_IN_TERMINAL" value="true" />
<option name="EXECUTE_SCRIPT_FILE" value="false" />
<envs />
<method v="2" />
</configuration>
<configuration name="publishToLocal" default="false" type="ShConfigurationType">
<option name="SCRIPT_TEXT" value="./gradlew :storage:publishToMavenLocal" />
<option name="INDEPENDENT_SCRIPT_PATH" value="true" />
<option name="SCRIPT_PATH" value="" />
<option name="SCRIPT_OPTIONS" value="" />
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
<option name="INTERPRETER_PATH" value="/bin/zsh" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="EXECUTE_IN_TERMINAL" value="true" />
<option name="EXECUTE_SCRIPT_FILE" value="false" />
<envs />
<method v="2" />
</configuration>
</component>
64 changes: 49 additions & 15 deletions FAQ.md
Original file line number Diff line number Diff line change
@@ -1,65 +1,99 @@
# Frequently Asked Questions

### The app is not responding when copy, move, and other IO tasks

Read the quick documentation, Javadoc or go to the source code.
All functions annotated by `@WorkerThread` must be called in the background thread,
otherwise `@UiThread` must be called in the main thread.
If you ignore the annotation, your apps will lead to [ANR](https://developer.android.com/topic/performance/vitals/anr).
If you ignore the annotation, your apps will lead
to [ANR](https://developer.android.com/topic/performance/vitals/anr).

### How to open quick documentation?

Use keyboard shortcut Control + Q on Windows or Control + J on MacOS.
More shortcuts can be found on [Android Studio keyboard shortcuts](https://developer.android.com/studio/intro/keyboard-shortcuts).
More shortcuts can be found
on [Android Studio keyboard shortcuts](https://developer.android.com/studio/intro/keyboard-shortcuts).

### Why permission dialog is not shown on API 29+?

No runtime permission is required to be prompted on scoped storage.

### How to upload the `DocumentFile` and `MediaFile` to server?

Read the input stream with extension function `openInputStream()` and upload it as Base64 text.

### File path returns empty string
Getting file path (`getAbsolutePath()`, `getBasePath()`, etc.) may returns empty string if the `DocumentFile` is an instance of `androidx.documentfile.provider.SingleDocumentFile`. The following URIs are the example of `SingleDocumentFile`:

Getting file path (`getAbsolutePath()`, `getBasePath()`, etc.) may returns empty string if
the `DocumentFile` is an instance of `androidx.documentfile.provider.SingleDocumentFile`. The
following URIs are the example of `SingleDocumentFile`:

```
content://com.android.providers.downloads.documents/document/9
content://com.android.providers.media.documents/document/document%3A34
```

Here're some notes:

* Empty file path is not this library's limitation, but Android OS itself.
* To check if the file has guaranteed direct file path, extension function `DocumentFile.isTreeDocumentFile` will return `true`.
* You can convert `SingleDocumentFile` to `MediaFile` and use `MediaFile.absolutePath`. If this still does not work, then there's no other way.
* We don't recommend you to use direct file path for file management, such as reading, uploading it to the server, or importing it into your app.
Because Android OS wants us to use URI, thus direct file path is useless. So you need to use extension function `Uri.openInputStream()` for `DocumentFile` and `MediaFile`.
* To check if the file has guaranteed direct file path, extension
function `DocumentFile.isTreeDocumentFile` will return `true`.
* You can convert `SingleDocumentFile` to `MediaFile` and use `MediaFile.absolutePath`. If this
still does not work, then there's no other way.
* We don't recommend you to use direct file path for file management, such as reading, uploading it
to the server, or importing it into your app.
Because Android OS wants us to use URI, thus direct file path is useless. So you need to use
extension function `Uri.openInputStream()` for `DocumentFile` and `MediaFile`.

### How to check if a folder/file is writable?

Use `isWritable()` extension function, because `DocumentFile.canWrite()` sometimes buggy on API 30.

### Which paths are writable with `java.io.File` on scoped storage?
Accessing files in scoped storage requires URI, but the following paths are exception and no storage permission needed:

Accessing files in scoped storage requires URI, but the following paths are exception and no storage
permission needed:

* `/storage/emulated/0/Android/data/<your.app.package>`
* `/storage/<SD card ID>/Android/data/<your.app.package>`
* `/data/user/0/<your.app.package>` (API 24+)
* `/data/data/<your.app.package>` (API 23-)

### What is the target branch for pull requests?

Use branch `release/*` if exists, or use `master` instead.

### I have Java projects, but this library is built in Kotlin. How can I use it?

Kotlin is compatible with Java. You can read Kotlin functions as Java methods.
Read: [Java Compatibility](https://github.com/anggrayudi/SimpleStorage/blob/master/JAVA_COMPATIBILITY.md)

### Why does SimpleStorage use Kotlin?

The main reasons why this library really needs Kotlin:
* SimpleStorage requires thread suspension feature, but this feature is only provided by [Kotlin Coroutines](https://github.com/Kotlin/kotlinx.coroutines).
* SimpleStorage contains many `String` & `Collection` manipulations, and Kotlin can overcome them in simple and easy ways.

* SimpleStorage requires thread suspension feature, but this feature is only provided
by [Kotlin Coroutines](https://github.com/Kotlin/kotlinx.coroutines).
* SimpleStorage contains many `String` & `Collection` manipulations, and Kotlin can overcome them in
simple and easy ways.

Other reasons are:

* Kotlin can shorten and simplify your code.
* Writing code in Kotlin is faster, thus it saves your time and improves your productivity.
* [Google is Kotlin first](https://techcrunch.com/2019/05/07/kotlin-is-now-googles-preferred-language-for-android-app-development/) now.
* [Google is Kotlin first](https://techcrunch.com/2019/05/07/kotlin-is-now-googles-preferred-language-for-android-app-development/)
now.

### What are SimpleStorage alternatives?
You can't run from the fact that Google is Kotlin First now. Even Google has created [ModernStorage](https://github.com/google/modernstorage) (alternative for SimpleStorage) in Kotlin.

You can't run from the fact that Google is Kotlin First now. Even Google has
created [ModernStorage](https://github.com/google/modernstorage) (alternative for SimpleStorage) in
Kotlin.
Learn Kotlin, or Google will leave you far behind.

**We have no intention to create Java version of SimpleStorage.** It will double our works and requires a lot of effort.
Keep in mind that we don't want to archive this library, even though Google has released the stable version of ModernStorage.
This library has rich features that Google may not covers, e.g. moving, copying, compressing and scanning folders.
**We have no intention to create Java version of SimpleStorage.** It will double our works and
requires a lot of effort.
Keep in mind that we don't want to archive this library, even though Google has released the stable
version of ModernStorage.
This library has rich features that Google may not covers, e.g. moving, copying, compressing and
scanning folders.
24 changes: 16 additions & 8 deletions JAVA_COMPATIBILITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,25 @@ Kotlin is compatible with Java, meaning that Kotlin code is readable in Java.

## How to use?

Simple Storage contains utility functions stored in `object` class, e.g. `DocumentFileCompat` and `MediaStoreCompat`.
Simple Storage contains utility functions stored in `object` class, e.g. `DocumentFileCompat`
and `MediaStoreCompat`.
These classes contain only static functions.

Additionally, this library also has extension functions, e.g. `DocumentFileExtKt` and `FileExtKt`.
You can learn it [here](https://www.raywenderlich.com/10986797-extension-functions-and-properties-in-kotlin).
You can learn
it [here](https://www.raywenderlich.com/10986797-extension-functions-and-properties-in-kotlin).

### Extension Functions

Common extension functions are stored in package `com.anggrayudi.storage.extension`. The others are in `com.anggrayudi.storage.file`.
You'll find that the most useful extension functions come from `DocumentFileExtKt` and `FileExtKt`. They are:
* `DocumentFile.getStorageId()` and `File.getStorageId()` → Get storage ID. Returns `primary` for external storage and something like `AAAA-BBBB` for SD card.
* `DocumentFile.getAbsolutePath()` → Get file's absolute path. Returns something like `/storage/AAAA-BBBB/Music/My Love.mp3`.
Common extension functions are stored in package `com.anggrayudi.storage.extension`. The others are
in `com.anggrayudi.storage.file`.
You'll find that the most useful extension functions come from `DocumentFileExtKt` and `FileExtKt`.
They are:

* `DocumentFile.getStorageId()` and `File.getStorageId()` → Get storage ID. Returns `primary` for
external storage and something like `AAAA-BBBB` for SD card.
* `DocumentFile.getAbsolutePath()` → Get file's absolute path. Returns something
like `/storage/AAAA-BBBB/Music/My Love.mp3`.
* `DocumentFile.copyFileTo()` and `File.copyFileTo()`
* `DocumentFile.search()` and `File.search()`, etc.

Expand Down Expand Up @@ -43,7 +50,8 @@ their class names are renamed from using suffix `ExtKt` to `Utils`.
I will refer to utility functions stored in Kotlin `object` class so you can understand it easily.
You can find the most useful utility functions in `DocumentFileCompat` and `MediaStoreCompat`.

Suppose that I want to get file from SD card with the following simple path: `AAAA-BBBB:Music/My Love.mp3`.
Suppose that I want to get file from SD card with the following simple
path: `AAAA-BBBB:Music/My Love.mp3`.
BTW, `AAAA-BBBB` is the SD card's storage ID for this example.

#### In Kotlin
Expand All @@ -65,5 +73,5 @@ Just go to the source code to check whether it has the annotation.
## Sample Code

* More sample code in Java can be found in
[`JavaActivity`](https://github.com/anggrayudi/SimpleStorage/blob/master/sample/src/main/java/com/anggrayudi/storage/sample/activity/JavaActivity.java)
[`JavaActivity`](https://github.com/anggrayudi/SimpleStorage/blob/master/sample/src/main/java/com/anggrayudi/storage/sample/activity/JavaActivity.java)
* Learn Kotlin on [Udacity](https://classroom.udacity.com/courses/ud9011). It's easy and free!
Loading
Loading