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

Support a lack of MANAGE_DOCUMENTS permission #551

Merged
merged 2 commits into from
Sep 14, 2023
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,14 @@ class StorageActivity : BackupActivity() {
*/
private val openDocumentTree = registerForActivityResult(OpenPersistableDocumentTree()) { uri ->
if (uri != null) {
Log.e(TAG, "OpenDocumentTree: $uri")
val authority = uri.authority ?: throw AssertionError("No authority in $uri")
// we are most likely not allowed to resolve storage roots,
// but being the optimists we are, we are still trying...
val storageRoot = StorageRootResolver.getStorageRoots(this, authority).getOrNull(0)
if (storageRoot == null) {
viewModel.onUriPermissionResultReceived(null)
val fakeRoot = StorageRootResolver.getFakeStorageRootForUri(this, uri)
viewModel.onSafOptionChosen(fakeRoot)
viewModel.onUriPermissionResultReceived(uri)
grote marked this conversation as resolved.
Show resolved Hide resolved
} else {
viewModel.onSafOptionChosen(storageRoot)
viewModel.onUriPermissionResultReceived(uri)
Expand All @@ -56,19 +59,19 @@ class StorageActivity : BackupActivity() {
}
viewModel.isSetupWizard = isSetupWizard()

viewModel.locationSet.observeEvent(this, {
viewModel.locationSet.observeEvent(this) {
showFragment(StorageCheckFragment.newInstance(getCheckFragmentTitle()), true)
})
}

viewModel.locationChecked.observeEvent(this, { result ->
viewModel.locationChecked.observeEvent(this) { result ->
val errorMsg = result.errorMsg
if (errorMsg == null) {
setResult(RESULT_OK)
finishAfterTransition()
} else {
onInvalidLocation(errorMsg)
}
})
}

if (savedInstanceState == null) {
if (canUseStorageRootsFragment()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,9 @@ internal class StorageOptionsFragment : Fragment(), StorageOptionClickedListener

listView.adapter = adapter

viewModel.storageOptions.observe(viewLifecycleOwner, { roots ->
viewModel.storageOptions.observe(viewLifecycleOwner) { roots ->
onRootsLoaded(roots)
})
}
}

override fun onStart() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package com.stevesoltys.seedvault.ui.storage

import android.Manifest.permission.MANAGE_DOCUMENTS
import android.content.Context
import android.database.Cursor
import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.UserHandle
import android.provider.DocumentsContract
import android.provider.DocumentsContract.Root.COLUMN_AVAILABLE_BYTES
Expand Down Expand Up @@ -57,6 +59,25 @@ internal object StorageRootResolver {
return roots
}

/**
* Used for getting a SafOption when we lack [MANAGE_DOCUMENTS],
* since we are not allowed to use [getStorageRoots] in this case.
*/
fun getFakeStorageRootForUri(context: Context, uri: Uri): SafOption {
val authority = uri.authority ?: throw AssertionError("No authority in $uri")
return SafOption(
authority = authority,
rootId = ROOT_ID_DEVICE,
documentId = DocumentsContract.getTreeDocumentId(uri),
icon = getIcon(context, authority, ROOT_ID_DEVICE, 0),
title = context.getString(R.string.storage_user_selected_location_title),
summary = "Please open a bug if you see this",
availableBytes = null,
isUsb = false, // FIXME not supported without MANAGE_DOCUMENTS permission
requiresNetwork = authority != AUTHORITY_STORAGE && authority != AUTHORITY_DOWNLOADS,
)
}

private fun getStorageRoot(context: Context, authority: String, cursor: Cursor): SafOption? {
val flags = cursor.getInt(COLUMN_FLAGS)
val supportsCreate = flags and FLAG_SUPPORTS_CREATE != 0
Expand Down Expand Up @@ -107,15 +128,19 @@ internal object StorageRootResolver {
authority == AUTHORITY_STORAGE && rootId == ROOT_ID_DEVICE -> {
context.getDrawable(R.drawable.ic_phone_android)
}

authority == AUTHORITY_STORAGE && rootId != ROOT_ID_HOME -> {
context.getDrawable(R.drawable.ic_usb)
}

authority == AUTHORITY_NEXTCLOUD -> {
context.getDrawable(R.drawable.nextcloud)
}

authority == AUTHORITY_DAVX5 -> {
context.getDrawable(R.drawable.davx5)
}

else -> null
}
}
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
<string name="storage_fragment_restore_title">Where to find your backups?</string>
<string name="storage_fragment_warning">People with access to your storage location can learn which apps you use, but do not get access to the apps\' data.</string>
<string name="storage_fragment_warning_delete">Existing backups in this location will be deleted.</string>
<string name="storage_user_selected_location_title">User-chosen location</string>
<string name="storage_fake_drive_title">USB flash drive</string>
<string name="storage_fake_drive_summary">Needs to be plugged in</string>
<string name="storage_available_bytes"><xliff:g example="1 GB" id="size">%1$s</xliff:g> free</string>
Expand Down