Comments (9)
To request the access, you need to call requestStorageAccess()
. On Android 11+, you might need to save the granted path to the preferences, because API 30 can't grant the root path, so you'll need remember which path that you have.
storageHelper.onStorageAccessGranted = { _, root ->
val preferences = PreferenceManager.getDefaultSharedPreferences(this)
preferences.edit().putString("path", root.getAbsolutePath(this)).apply()
}
To reuse this granted path in the future, call DocumentFileCompat.fromFullPath()
:
val preferences = PreferenceManager.getDefaultSharedPreferences(this)
val path = preferences.getString("path", null)
val folder = DocumentFileCompat.fromFullPath(this, path!!, requiresWriteAccess = true)
// now you can make file on this folder
val file = folder?.makeFile(this, "notes", "text/plain")
I'm planning to release SimpleStorage v1.5.4 to help you querying all granted paths in your app, so you don't need to remember them in preferences anymore. Here's the sample code will look like:
val grantedPaths: Map<String, Set<String>> = DocumentFileCompat.getAccessibleAbsolutePaths(context)
Here's the output of that new function:
If you can't wait for the release, just use snapshot version 1.5.4-SNAPSHOT
in your dependencies.
from simplestorage.
If you don't use SimpleStorageHelper
, then you need to call requestStorageAccess()
in FolderPickerCallback#onStorageAccessDenied()
callback. But if you're using it, then you can ignore requestStorageAccess()
, because SimpleStorageHelper#openFolderPicker()
already handles that capability.
From the code you've shared, you're using SimpleStorageHelper
, which is recommended for most cases. Then it's ok to ignore requestStorageAccess()
function and SimpleStorageHelper#onStorageAccessGranted()
callback. You may call SimpleStorageHelper#openFolderPicker()
directly. No need to request access anymore:
buttonSelectFolder.setOnClickListener {
storageHelper.openFolderPicker()
}
storageHelper.onFolderSelected = { requestCode, folder ->
// tell user the selected path
}
In the future, you can write a file into the selected folder, for example:
val grantedPaths = DocumentFileCompat.getAccessibleAbsolutePaths(this)
val path = grantedPaths.values.firstOrNull()?.firstOrNull() ?: return
val folder = DocumentFileCompat.fromFullPath(this, path, requiresWriteAccess = true)
val file = folder?.makeFile(this, "notes", "text/plain")
from simplestorage.
v1.5.4 is released.
from simplestorage.
Oh so
SimpleStorageHelper.openFolderPicker()
is similar to requestStorageAccess(), but requestStorageAccess is more general (can request access to both folders and files) if I understand correctly?
Basically, if you're not using custom dialog styles, then no need to think much about requestStorageAccess()
. You'll understand this function's purpose once you dig into SimpleStorage
and SimpleStorageHelper
classes.
I would strongly suggest to add both examples you gave above in the README
Sure, I'll do that once v1.5.4 is released.
from simplestorage.
And I know it's not the fault of this library. Android's file permission system is insane since Android 11, it doesn't make any sense how hard it is to access storage via scoped storage permissions, most of the time it just plainly fails and there is no documentation even years later to just explain how to do that. It looks like Google wants most apps to be segregated in their own respective storage spaces.
So thank you very much for making this library. I am looking forward to your valuable inputs that will hopefully help me work out a working app.
from simplestorage.
Thank you so much, it appears to work! I'm going to revamp my app using the approach you suggested, thank you very much!
The new function would be very helpful, that would be great! But my issue was rather that I did not know how to store and retrieve adequately the permission, your approach fixed it!
Just one question out of curiosity: I notice you suggest to use requestStorageAccess()
, why not openFolderPicker()
? I thought that FolderPicker was made for this purpose (of selecting a folder tree), no? Or is the FolderPicker only to request read-only access, not write access?
from simplestorage.
Oh so SimpleStorageHelper.openFolderPicker()
is similar to requestStorageAccess(), but requestStorageAccess is more general (can request access to both folders and files) if I understand correctly?
The new function to query grantedPaths looks awesome, so that we won't need to keep the URIs, it will be great also to debug whether we have permission (instead of trying to infer from getting a returned null object)!
I would strongly suggest to add both examples you gave above in the README, as I think this usecase of requesting access to an external shared folder to allow for file syncing is a pretty common one, and your answers are making this much more easy than I thought. I tried to make it work for days and could not!
Also maybe it would be nice to clarify that DocumentFileCompat can be used to get write access too, as the readme makes it sound like it's only to read files, not to write them.
Thank you once again, your library is providing an invaluable service to all of us poor Android developers!
(You can close this issue whenever you want)
from simplestorage.
(I have misunderstood something in my previous message, please check the edited/corrected comment on github)
from simplestorage.
This is awesome, thank you so much!
Just a heads up for others who may run into a similar issue, DocumentFile.openOutputStream()
does not work anymore if the specified file does not exist.
So this won't work unless the file is created first by another mean:
val preferences2 = PreferenceManager.getDefaultSharedPreferences(appContext)
val path2 = preferences2.getString("path", null)
val path3 = "$path2/testfile.txt"
val file = DocumentFileCompat.fromFullPath(appContext, path3!!, requiresWriteAccess = true) // Don't do it like this, if the file does not exist, trying to openOutputStream() on it will fail. Rather, open the folder, then makeFile with a CreateMode to append.
val out1 = file!!.openOutputStream(appContext, append=true)
try {
out1!!.write("Some output!".toByteArray(Charsets.UTF_8))
} catch (e: Exception) {
Log.d("MyApp", "Unable to write test file: " + e.stackTraceToString())
} finally {
out1?.close()
}
Whereas if we replace the val file
line by the following, it will always work (it will create the file if necessary, and if it exists, it will append to it):
val folder = DocumentFileCompat.fromFullPath(appContext, path2!!, requiresWriteAccess = true)
val file = folder?.makeFile(appContext, "testfile.txt", "text/plain", mode=CreateMode.REUSE)
from simplestorage.
Related Issues (20)
- Is there some way to listen for changes under a tree URI or at least over a single document URI? HOT 1
- Just a question to cloud storages like GoogleDrive / Dropbox
- Request codes must be unique. File picker=3, Folder picker=0, Storage access=0, Create file=4 HOT 1
- IllegalArgumentsException: Empty Storage ID HOT 4
- Execution failed for task ':app:mergeDebugResources'
- On Android 13 device hasStorageAccess returns false HOT 1
- How can i set specific path or uri in storageHelper.openFilePicker(REQUEST_CODE_PICK_FILE_FOR_RENAME)? HOT 1
- Awesome HOT 1
- Case when permission is canceled was not handled HOT 5
- Not working on complieSDK/targetSDK 33 HOT 3
- filterMimeTypes not working with single or mulitple mime types HOT 1
- Judgment of permissions is not accurate
- Error " java.lang.IllegalArgumentException: Request codes must be unique" similar to Issue #93 in 1.5.5 HOT 2
- Full Access above 33 using MANAGE_EXTERNAL_STORAGE HOT 1
- multiple mime type HOT 1
- Fully support coroutines HOT 2
- [QA/Bug] getting permission denied HOT 2
- How can I specify to obtain files of certain formats, and then customize the view to display these files, such as PDF files? HOT 3
- Some useful suggestions
- File extension lost HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from simplestorage.