From 3092fd86c3cb706f39419f55149c1bc4731bcc8a Mon Sep 17 00:00:00 2001 From: Anggrayudi Hardiannico Date: Tue, 18 Jun 2024 23:15:03 +0700 Subject: [PATCH] updated README.md --- README.md | 68 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 2accca3..18178d8 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,11 @@ allprojects { Simple Storage is built in Kotlin. Follow this [documentation](JAVA_COMPATIBILITY.md) to use it in your Java project. +Note that some long-running functions like copy, move, compress, and unzip are now only available in Kotlin. +They are powered by Kotlin Coroutines & Flow, which are easy to use. +You can still use these Java features in your project, but you will need [v1.5.6](https://github.com/anggrayudi/SimpleStorage/releases/tag/1.5.6) which is the latest version that +supports Java. + ## Terminology ![Alt text](art/terminology.png?raw=true "Simple Storage Terms") @@ -73,7 +78,7 @@ To check whether you have access to particular paths, call `DocumentFileCompat.g ![Alt text](art/getAccessibleAbsolutePaths.png?raw=true "DocumentFileCompat.getAccessibleAbsolutePaths()") All paths in those locations are accessible via functions `DocumentFileCompat.from*()`, otherwise your action will be denied by the system if you want to -access paths other than those. Functions `DocumentFileCompat.from*()` (next section) will return null as well. On API 28-, you can obtain it by requesting +access paths other than those, then functions `DocumentFileCompat.from*()` (next section) will return null as well. On API 28-, you can obtain it by requesting the runtime permission. For API 29+, it is obtained automatically by calling `SimpleStorageHelper#requestStorageAccess()` or `SimpleStorageHelper#openFolderPicker()`. The granted paths are persisted by this library via `ContentResolver#takePersistableUriPermission()`, so you don't need to remember them in preferences: @@ -246,44 +251,43 @@ For example, you can move a folder with few lines of code: val folder: DocumentFile = ... val targetFolder: DocumentFile = ... -// Since moveFolderTo() is annotated with @WorkerThread, you must execute it in the background thread -folder.moveFolderTo(applicationContext, targetFolder, skipEmptyFiles = false, callback = object : FolderCallback() { - override fun onPrepare() { - // Show notification or progress bar dialog with indeterminate state - } - - override fun onCountingFiles() { - // Inform user that the app is counting & calculating files - } - - override fun onStart(folder: DocumentFile, totalFilesToCopy: Int, workerThread: Thread): Long { - return 1000 // update progress every 1 second - } - - override fun onParentConflict(destinationFolder: DocumentFile, action: FolderCallback.ParentFolderConflictAction, canMerge: Boolean) { - handleParentFolderConflict(destinationFolder, action, canMerge) +ioScope.launch { + folder.moveFolderTo(applicationContext, targetFolder, skipEmptyFiles = false, updateInterval = 1000, onConflict = object : FolderConflictCallback(uiScope) { + override fun onParentConflict(destinationFolder: DocumentFile, action: ParentFolderConflictAction, canMerge: Boolean) { + handleParentFolderConflict(destinationFolder, action, canMerge) } override fun onContentConflict( - destinationFolder: DocumentFile, - conflictedFiles: MutableList, - action: FolderCallback.FolderContentConflictAction + destinationFolder: DocumentFile, + conflictedFiles: MutableList, + action: FolderContentConflictAction ) { - handleFolderContentConflict(action, conflictedFiles) - } - - override fun onReport(report: Report) { - Timber.d("onReport() -> ${report.progress.toInt()}% | Copied ${report.fileCount} files") + handleFolderContentConflict(action, conflictedFiles) } - - override fun onCompleted(result: Result) { - Toast.makeText(baseContext, "Copied ${result.totalCopiedFiles} of ${result.totalFilesToCopy} files", Toast.LENGTH_SHORT).show() + }).onCompletion { + if (it is CancellationException) { + Timber.d("Folder move is aborted") } - - override fun onFailed(errorCode: ErrorCode) { - Toast.makeText(baseContext, "An error has occurred: $errorCode", Toast.LENGTH_SHORT).show() + }.collect { result -> + when (result) { + is FolderResult.Validating -> Timber.d("Validating...") + is FolderResult.Preparing -> Timber.d("Preparing...") + is FolderResult.CountingFiles -> Timber.d("Counting files...") + is FolderResult.DeletingConflictedFiles -> Timber.d("Deleting conflicted files...") + is FolderResult.Starting -> Timber.d("Starting...") + is FolderResult.InProgress -> Timber.d("Progress: ${result.progress.toInt()}% | ${result.fileCount} files") + is FolderResult.Completed -> uiScope.launch { + Timber.d("Completed: ${result.totalCopiedFiles} of ${result.totalFilesToCopy} files") + Toast.makeText(baseContext, "Moved ${result.totalCopiedFiles} of ${result.totalFilesToCopy} files", Toast.LENGTH_SHORT).show() + } + + is FolderResult.Error -> uiScope.launch { + Timber.e(result.errorCode.name) + Toast.makeText(baseContext, "An error has occurred: ${result.errorCode.name}", Toast.LENGTH_SHORT).show() + } } -}) + } +} ``` The coolest thing of this library is you can ask users to choose Merge, Replace, Create New, or Skip Duplicate folders & files