Don't force `full` (F-Droid/GitHub) releases users to grant the `All files access` permission
They may not want to give the permission, which I think is a understandable desire.
I believe that a dialog explaining why it is good to give the permission after the user has refused to grant it would be good. It should have an option to try granting the permission again and one to work the same way as play builds do.
For those stumbling across this: i gave scoped access to the Ankidroid folder and it seems to work properly.
Hello 👋, this issue has been opened for more than 3 months with no activity on it. If the issue is still here, please keep in mind that we need community support and help to fix it! Just comment something like still searching for solutions and if you found one, please open a pull request! You have 7 days until this gets closed automatically
+1, would love to see this happen 😁
I've made a merge request for f-droid to use the full build flavor, the storage access permission should be optional for f-droid users... any idea what we need to do specifically to make this happen?
100% agree here.
- I don't care about "saving my collection in a safe place that will not be deleted". My collection is already synced using AnkiWeb (and backuped in Desktop apps)
- Even if I did care about that, it should just ask for some folder, not all files permission
This permission should be optional.
any idea what we need to do specifically to make this happen?
I made a "short" dive into the code base and from what I’ve learned, some parts of the check for the "permissions required screen" ignore the fact that the data is already migrated into the app private directory.
In TL;DR and IMO, the selectAnkiDroidFolder needs to have a check here which returns AppPrivateFolder when the folder was already migrated into private storage. (Further reasoning collapsed below.)
In-Depth Explanation of Code
The check works as follows:
- at launch, AnkiDroid checks if it has all "required permissions" https://github.com/ankidroid/Anki-Android/blob/978aaf4b6010e6b00bdb2e4a19abb8e32e9319c1/AnkiDroid/src/main/java/com/ichi2/anki/introduction/CollectionPermissionScreenLauncher.kt#L42-L54
- the "required permissions" are inferred based on the "AnkiDroid folder location"
- the "folder location" is selected based on the Android OS version running, current permissions and, the location which is currently used (if data is not migrated):
https://github.com/ankidroid/Anki-Android/blob/978aaf4b6010e6b00bdb2e4a19abb8e32e9319c1/AnkiDroid/src/main/java/com/ichi2/anki/InitialActivity.kt#L175-L207
- following AnkiDroid methods is called in that process: https://github.com/ankidroid/Anki-Android/blob/978aaf4b6010e6b00bdb2e4a19abb8e32e9319c1/AnkiDroid/src/main/java/com/ichi2/utils/Permissions.kt#L164-L171
- and following OS method is called: Environment.isExternalStorageManager(), [Environment.]
So to skip the screen, some of that code needs to change. Going through this code exemplary in the case of my phone:
- Android 13 (Lineage OS 20; Tiramisu)
- AnkiDroid 2.17.1, full build installed from F-Droid
- not the most recent, but affected code, which is linked above, hasn’t changed since
- data directory already migrated to private app directory
Manually evaluting the selectAnkiDroidFolder gets me to this result (code rolled out):
// I’m running Tiramisu, T not < Q, so first statement should be false
val canAccessLegacyStorage = Build.VERSION.SDK_INT < Build.VERSION_CODES.Q
// result of Environment.isExternalStorageLegacy() is unknown
|| Environment.isExternalStorageLegacy()
// so this may be true (however unimportant for conclusion as you will see below).
val currentFolderIsAccessibleAndLegacy = canAccessLegacyStorage
// but storage is migrated, so isLegacyStorage should return false
&& isLegacyStorage(context, setCollectionPath = false) == true
// Tiramisu >= R
val canManageExternalStorage = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R &&
// this permission is defined in the manifest for F-Droid
context.arePermissionsDefinedInAnkiDroidManifest(MANAGE_EXTERNAL_STORAGE)
// so canManageExternalStorage = true.
// Tiramisu not <= Q AND currentFolderIsAccessibleAndLegacy assumed false
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q || currentFolderIsAccessibleAndLegacy) {
return AnkiDroidFolder.PublicFolder(PermissionSet.LEGACY_ACCESS)
}
// so continue here.
// but canManageExternalStorage is true,
return if (canManageExternalStorage) {
// so it jumps into here
// without looking into where the current files are actually stored
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
AnkiDroidFolder.PublicFolder(PermissionSet.TIRAMISU_EXTERNAL_MANAGER)
} else {
AnkiDroidFolder.PublicFolder(PermissionSet.EXTERNAL_MANAGER)
}
} else {
// btw. this is our goal
return AnkiDroidFolder.AppPrivateFolder
}
Actually, a dialog or some kind of screen should be shown with the two options 1. Safe storage, but with permission, 2. Storage that will be gone after uninstall, no permissions required, so the collection path is set correctly.
For those stumbling across this: i gave scoped access to the Ankidroid folder and it seems to work properly.
How did you do that?
For those stumbling across this: i gave scoped access to the Ankidroid folder and it seems to work properly.
How did you do that?
Apparently the feature is tied to the Android OS version I use: GrapheneOS. Here is the relevant documentation about it: https://grapheneos.org/usage#storage-scopes. Basically, I can select a specific folder for any given app inside the app's OS settings page. The app thinks it has full access, but actually it does not. Only to the folder I select.