-
Notifications
You must be signed in to change notification settings - Fork 19
re-implement file sync feature with scoped permissions via SimpleStorage library #32
Conversation
…age library, drop MANAGE_EXTERNAL_STORAGE permission Signed-off-by: Stephen L. <[email protected]>
I just tested syncing between an Android device under Android 10 and a desktop client on Windows 10 via SyncThing (SyncThing-Fork on Android, Synctrayzor on Windows). It worked perfectly well :-) I did not know Super Productivity had in-built conflict detection and resolution, that's awesome! Thank you a lot @johannesjo for making and maintaining this software, and to all other contributors who added features and bugfixes :-) |
ping @anggrayudi, author of SimpleStorage, he has been super helpful to implement scoped storage permissions handling correctly! |
Thank you so much @lrq3000 ! This is a very important feature! |
Sorry! I was a bit over eager with the merge :D Maybe we can proceed with a new PR for the remaining issues? I found two issues: Let me know if I can help you debugging this somehow. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for late review, but at least my comments will be useful for your future implementation.
// Restore scoped storage permission on Android 10+ | ||
super.onRequestPermissionsResult(requestCode, permissions, grantResults) | ||
// Mandatory for Activity, but not for Fragment & ComponentActivity | ||
storageHelper.onRequestPermissionsResult(requestCode, permissions, grantResults) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AppCompatActivity
is subclass of ComponentActivity
, thus no need to call storageHelper.onRequestPermissionsResult()
@@ -322,39 +326,100 @@ abstract class CommonJavaScriptInterface( | |||
@Suppress("unused") | |||
@JavascriptInterface | |||
fun getFileRev(filePath: String): String { | |||
val file = File(filePath) | |||
return file.lastModified().toString() | |||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to add API check here, because the library is smart enough to detect whether it uses scoped storage or not. Just write code like this:
val file = DocumentFileCompat.fromFullPath(activity, filePath, requiresWriteAccess = false)
file?.lastModified().toString()
} | ||
|
||
@Suppress("unused") | ||
@JavascriptInterface | ||
fun readFile(filePath: String): String { | ||
val reader = BufferedReader(FileReader(filePath)) | ||
val reader = | ||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DocumentFileCompat.fromFullPath()
works for non scoped-storage, so no need to add check for API level.
writer.write(data) | ||
writer.close() | ||
val writer: Writer = | ||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DocumentFileCompat.fromFullPath()
works for non scoped-storage, so no need to check the API level.
@anggrayudi thank you so much for your inputs once again! :D I know simplestorage can handle older versions but i wished to maintain the old behavior for older users, but maybe it's better if everything is done with simplestorage now? I'd suggest we drop older file management code after a while once enough users have tested the new version, just to be on the safe side :-)
|
On API 29, yes the user should select the root path first. But for API 30 and higher, no need to select the root path first. The user can select any folder directly and SimpleStorage will persist the permission in background. You can test these behaviors on emulators. Additionally, you can force the user to select/grant specific folders with |
@anggrayudi Ah ok thank you very much for the precision! @johannesjo In fact about issue 1 you meant that the UI says "Needs permission" after first selection, right? I can reproduce the issue, I'll fix it. And likely issue 2 is the same I have about reading the file, so it should be fixed too in my next PR. |
@johannesjo I'm sorry but I'll need your help to fix issue 1: it's a concurrency issue, what is happening is that the web app calls (IS_ANDROID_WEB_VIEW &&
(androidInterface as any).grantFilePermission &&
androidInterface.isGrantedFilePermission) Hence, The solution is to rewrite the condition in the web app to wait for I unfortunately don't know how to code in typescript/nodejs, I hope my description above is sufficiently clear :-/ I have already implemented the necessary changes in the android app in my master branch (I will make another PR soon when I'll have tackled issue 2), so when the typescript web app will be updated, it should work. Also, the proposed change above should be retrocompatible with older versions of Android anyway, and even current apps, because the |
Description
Re-implements PR #25 without requiring
MANAGE_EXTERNAL_STORAGE
permission, which is asks for permission to write ALL files on the Android device and which is rejected by the Google Play Store rules for all apps except those that really require it (such as file managers).With this PR here, there is no need for special permissions anymore, the app asks for permission to write in a single folder. In fact we could rewrite to ask to write only in a single file, but having permission over a whole folder allows to write more files if there is a need in the future.
To test, there is a debug APK here: https://github.com/lrq3000/super-productivity-android/releases/tag/super-productivity-android_scoped-storage
Issues Resolved
Fixes #31, fixes #13.
Can also be used as a workaround for #9.
The library that is managing permissions, SimpleStorage, can also be used to implement #8.
Check List