Prepare backup destination UI refactoring

Mostly renaming for clarity.
This commit is contained in:
Torsten Grote 2024-04-09 17:22:46 -03:00
parent ec8190755e
commit 6788d0d25a
No known key found for this signature in database
GPG key ID: 3E5F77D92CF891FF
10 changed files with 74 additions and 40 deletions

View file

@ -1,4 +1,9 @@
package com.stevesoltys.seedvault.ui.storage /*
* SPDX-FileCopyrightText: 2024 The Calyx Institute
* SPDX-License-Identifier: Apache-2.0
*/
package com.stevesoltys.seedvault.plugins.saf
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
@ -9,8 +14,13 @@ import android.provider.DocumentsContract
import android.provider.DocumentsContract.Document.COLUMN_DISPLAY_NAME import android.provider.DocumentsContract.Document.COLUMN_DISPLAY_NAME
import android.provider.DocumentsContract.Document.COLUMN_DOCUMENT_ID import android.provider.DocumentsContract.Document.COLUMN_DOCUMENT_ID
import com.stevesoltys.seedvault.R import com.stevesoltys.seedvault.R
import com.stevesoltys.seedvault.plugins.saf.StorageRootResolver.getIcon
import com.stevesoltys.seedvault.ui.storage.AUTHORITY_DAVX5
import com.stevesoltys.seedvault.ui.storage.AUTHORITY_NEXTCLOUD
import com.stevesoltys.seedvault.ui.storage.AUTHORITY_ROUND_SYNC
import com.stevesoltys.seedvault.ui.storage.AUTHORITY_STORAGE
import com.stevesoltys.seedvault.ui.storage.StorageOption
import com.stevesoltys.seedvault.ui.storage.StorageOption.SafOption import com.stevesoltys.seedvault.ui.storage.StorageOption.SafOption
import com.stevesoltys.seedvault.ui.storage.StorageRootResolver.getIcon
private const val DAVX5_PACKAGE = "at.bitfire.davdroid" private const val DAVX5_PACKAGE = "at.bitfire.davdroid"
private const val DAVX5_ACTIVITY = "at.bitfire.davdroid.ui.webdav.WebdavMountsActivity" private const val DAVX5_ACTIVITY = "at.bitfire.davdroid.ui.webdav.WebdavMountsActivity"
@ -29,15 +39,15 @@ internal class SafStorageOptions(
private val packageManager = context.packageManager private val packageManager = context.packageManager
internal fun checkOrAddExtraRoots(roots: ArrayList<SafOption>) { internal fun checkOrAddExtraRoots(roots: ArrayList<StorageOption>) {
checkOrAddUsbRoot(roots) checkOrAddUsbRoot(roots)
checkOrAddDavX5Root(roots) checkOrAddDavX5Root(roots)
checkOrAddNextCloudRoot(roots) checkOrAddNextCloudRoot(roots)
checkOrAddRoundSyncRoots(roots) checkOrAddRoundSyncRoots(roots)
} }
private fun checkOrAddUsbRoot(roots: ArrayList<SafOption>) { private fun checkOrAddUsbRoot(roots: ArrayList<StorageOption>) {
if (doNotInclude(AUTHORITY_STORAGE, roots) { it.isUsb }) return if (doNotInclude(AUTHORITY_STORAGE, roots) { it is SafOption && it.isUsb }) return
val root = SafOption( val root = SafOption(
authority = AUTHORITY_STORAGE, authority = AUTHORITY_STORAGE,
@ -57,11 +67,11 @@ internal class SafStorageOptions(
/** /**
* Add a storage root for each child directory at the RoundSync root, if it exists. * Add a storage root for each child directory at the RoundSync root, if it exists.
*/ */
private fun checkOrAddRoundSyncRoots(roots: ArrayList<SafOption>) { private fun checkOrAddRoundSyncRoots(roots: ArrayList<StorageOption>) {
val roundSyncRoot = roots.firstOrNull { val roundSyncRoot = roots.firstOrNull {
it.authority == AUTHORITY_ROUND_SYNC it is SafOption && it.authority == AUTHORITY_ROUND_SYNC
} ?: return } as? SafOption ?: return
roots.remove(roundSyncRoot) roots.remove(roundSyncRoot)
@ -105,7 +115,7 @@ internal class SafStorageOptions(
* *
* If it *is* installed and this is restore, the user can set up a new account by clicking. * If it *is* installed and this is restore, the user can set up a new account by clicking.
*/ */
private fun checkOrAddDavX5Root(roots: ArrayList<SafOption>) { private fun checkOrAddDavX5Root(roots: ArrayList<StorageOption>) {
if (doNotInclude(AUTHORITY_DAVX5, roots)) return if (doNotInclude(AUTHORITY_DAVX5, roots)) return
val intent = Intent().apply { val intent = Intent().apply {
@ -155,7 +165,7 @@ internal class SafStorageOptions(
* because we don't know if there's just no account or an activated passcode * because we don't know if there's just no account or an activated passcode
* (which hides existing accounts). * (which hides existing accounts).
*/ */
private fun checkOrAddNextCloudRoot(roots: ArrayList<SafOption>) { private fun checkOrAddNextCloudRoot(roots: ArrayList<StorageOption>) {
if (doNotInclude(AUTHORITY_NEXTCLOUD, roots)) return if (doNotInclude(AUTHORITY_NEXTCLOUD, roots)) return
val intent = Intent().apply { val intent = Intent().apply {
@ -202,11 +212,12 @@ internal class SafStorageOptions(
private fun doNotInclude( private fun doNotInclude(
authority: String, authority: String,
roots: ArrayList<SafOption>, roots: ArrayList<StorageOption>,
doNotIncludeIfTrue: ((SafOption) -> Boolean)? = null, doNotIncludeIfTrue: ((StorageOption) -> Boolean)? = null,
): Boolean { ): Boolean {
if (!isAuthoritySupported(authority)) return true if (!isAuthoritySupported(authority)) return true
for (root in roots) { for (root in roots) {
if (root !is SafOption) continue
if (root.authority == authority && doNotIncludeIfTrue?.invoke(root) != false) { if (root.authority == authority && doNotIncludeIfTrue?.invoke(root) != false) {
return true return true
} }

View file

@ -1,4 +1,9 @@
package com.stevesoltys.seedvault.ui.storage /*
* SPDX-FileCopyrightText: 2024 The Calyx Institute
* SPDX-License-Identifier: Apache-2.0
*/
package com.stevesoltys.seedvault.plugins.saf
import android.Manifest.permission.MANAGE_DOCUMENTS import android.Manifest.permission.MANAGE_DOCUMENTS
import android.content.Context import android.content.Context
@ -19,8 +24,16 @@ import android.provider.DocumentsContract.Root.FLAG_REMOVABLE_USB
import android.provider.DocumentsContract.Root.FLAG_SUPPORTS_CREATE import android.provider.DocumentsContract.Root.FLAG_SUPPORTS_CREATE
import android.provider.DocumentsContract.Root.FLAG_SUPPORTS_IS_CHILD import android.provider.DocumentsContract.Root.FLAG_SUPPORTS_IS_CHILD
import android.util.Log import android.util.Log
import androidx.appcompat.content.res.AppCompatResources.getDrawable
import com.stevesoltys.seedvault.R import com.stevesoltys.seedvault.R
import com.stevesoltys.seedvault.getStorageContext import com.stevesoltys.seedvault.getStorageContext
import com.stevesoltys.seedvault.ui.storage.AUTHORITY_DAVX5
import com.stevesoltys.seedvault.ui.storage.AUTHORITY_DOWNLOADS
import com.stevesoltys.seedvault.ui.storage.AUTHORITY_NEXTCLOUD
import com.stevesoltys.seedvault.ui.storage.AUTHORITY_ROUND_SYNC
import com.stevesoltys.seedvault.ui.storage.AUTHORITY_STORAGE
import com.stevesoltys.seedvault.ui.storage.ROOT_ID_DEVICE
import com.stevesoltys.seedvault.ui.storage.ROOT_ID_HOME
import com.stevesoltys.seedvault.ui.storage.StorageOption.SafOption import com.stevesoltys.seedvault.ui.storage.StorageOption.SafOption
internal object StorageRootResolver { internal object StorageRootResolver {
@ -126,23 +139,23 @@ internal object StorageRootResolver {
fun getIcon(context: Context, authority: String, rootId: String, icon: Int): Drawable? { fun getIcon(context: Context, authority: String, rootId: String, icon: Int): Drawable? {
return getPackageIcon(context, authority, icon) ?: when { return getPackageIcon(context, authority, icon) ?: when {
authority == AUTHORITY_STORAGE && rootId == ROOT_ID_DEVICE -> { authority == AUTHORITY_STORAGE && rootId == ROOT_ID_DEVICE -> {
context.getDrawable(R.drawable.ic_phone_android) getDrawable(context, R.drawable.ic_phone_android)
} }
authority == AUTHORITY_STORAGE && rootId != ROOT_ID_HOME -> { authority == AUTHORITY_STORAGE && rootId != ROOT_ID_HOME -> {
context.getDrawable(R.drawable.ic_usb) getDrawable(context, R.drawable.ic_usb)
} }
authority == AUTHORITY_NEXTCLOUD -> { authority == AUTHORITY_NEXTCLOUD -> {
context.getDrawable(R.drawable.nextcloud) getDrawable(context, R.drawable.nextcloud)
} }
authority == AUTHORITY_DAVX5 -> { authority == AUTHORITY_DAVX5 -> {
context.getDrawable(R.drawable.davx5) getDrawable(context, R.drawable.davx5)
} }
authority == AUTHORITY_ROUND_SYNC -> { authority == AUTHORITY_ROUND_SYNC -> {
context.getDrawable(R.drawable.round_sync) getDrawable(context, R.drawable.round_sync)
} }
else -> null else -> null

View file

@ -31,7 +31,7 @@ internal class BackupStorageViewModel(
override val isRestoreOperation = false override val isRestoreOperation = false
override fun onLocationSet(uri: Uri) { override fun onSafUriSet(uri: Uri) {
val isUsb = saveStorage(uri) val isUsb = saveStorage(uri)
if (isUsb) { if (isUsb) {
// disable storage backup if new storage is on USB // disable storage backup if new storage is on USB

View file

@ -22,7 +22,7 @@ internal class RestoreStorageViewModel(
override val isRestoreOperation = true override val isRestoreOperation = true
override fun onLocationSet(uri: Uri) { override fun onSafUriSet(uri: Uri) {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
val storage = createStorage(uri) val storage = createStorage(uri)
val hasBackup = try { val hasBackup = try {

View file

@ -9,11 +9,11 @@ import android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION
import android.content.pm.PackageManager.PERMISSION_GRANTED import android.content.pm.PackageManager.PERMISSION_GRANTED
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.util.Log
import androidx.activity.result.contract.ActivityResultContracts.OpenDocumentTree import androidx.activity.result.contract.ActivityResultContracts.OpenDocumentTree
import androidx.annotation.CallSuper import androidx.annotation.CallSuper
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import com.stevesoltys.seedvault.R import com.stevesoltys.seedvault.R
import com.stevesoltys.seedvault.plugins.saf.StorageRootResolver
import com.stevesoltys.seedvault.ui.BackupActivity import com.stevesoltys.seedvault.ui.BackupActivity
import com.stevesoltys.seedvault.ui.INTENT_EXTRA_IS_RESTORE import com.stevesoltys.seedvault.ui.INTENT_EXTRA_IS_RESTORE
import com.stevesoltys.seedvault.ui.INTENT_EXTRA_IS_SETUP_WIZARD import com.stevesoltys.seedvault.ui.INTENT_EXTRA_IS_SETUP_WIZARD
@ -82,14 +82,6 @@ class StorageActivity : BackupActivity() {
} }
} }
override fun onBackPressed() {
if (supportFragmentManager.backStackEntryCount > 0) {
Log.d(TAG, "Blocking back button.")
} else {
super.onBackPressed()
}
}
private fun onInvalidLocation(errorMsg: String) { private fun onInvalidLocation(errorMsg: String) {
if (viewModel.isRestoreOperation) { if (viewModel.isRestoreOperation) {
val dialog = AlertDialog.Builder(this) val dialog = AlertDialog.Builder(this)

View file

@ -1,6 +1,7 @@
package com.stevesoltys.seedvault.ui.storage package com.stevesoltys.seedvault.ui.storage
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.View.GONE import android.view.View.GONE
@ -10,6 +11,7 @@ import android.view.ViewGroup
import android.widget.Button import android.widget.Button
import android.widget.ProgressBar import android.widget.ProgressBar
import android.widget.TextView import android.widget.TextView
import androidx.activity.addCallback
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import com.stevesoltys.seedvault.R import com.stevesoltys.seedvault.R
@ -34,6 +36,14 @@ class StorageCheckFragment : Fragment() {
} }
} }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
requireActivity().onBackPressedDispatcher.addCallback(this) {
Log.i("StorageCheckFragment", "Not navigating back!")
}
}
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,

View file

@ -13,9 +13,11 @@ import android.provider.DocumentsContract.PROVIDER_INTERFACE
import android.provider.DocumentsContract.buildRootsUri import android.provider.DocumentsContract.buildRootsUri
import android.util.Log import android.util.Log
import com.stevesoltys.seedvault.R import com.stevesoltys.seedvault.R
import com.stevesoltys.seedvault.plugins.saf.SafStorageOptions
import com.stevesoltys.seedvault.plugins.saf.StorageRootResolver
import com.stevesoltys.seedvault.ui.storage.StorageOption.SafOption import com.stevesoltys.seedvault.ui.storage.StorageOption.SafOption
private val TAG = StorageRootFetcher::class.java.simpleName private val TAG = StorageOptionFetcher::class.java.simpleName
const val AUTHORITY_STORAGE = "com.android.externalstorage.documents" const val AUTHORITY_STORAGE = "com.android.externalstorage.documents"
const val ROOT_ID_DEVICE = "primary" const val ROOT_ID_DEVICE = "primary"
@ -30,7 +32,7 @@ internal interface RemovableStorageListener {
fun onStorageChanged() fun onStorageChanged()
} }
internal class StorageRootFetcher(private val context: Context, private val isRestore: Boolean) { internal class StorageOptionFetcher(private val context: Context, private val isRestore: Boolean) {
private val packageManager = context.packageManager private val packageManager = context.packageManager
private val contentResolver = context.contentResolver private val contentResolver = context.contentResolver
@ -60,7 +62,9 @@ internal class StorageRootFetcher(private val context: Context, private val isRe
internal fun getRemovableStorageListener() = listener internal fun getRemovableStorageListener() = listener
internal fun getStorageOptions(): List<StorageOption> { internal fun getStorageOptions(): List<StorageOption> {
val roots = ArrayList<SafOption>() val roots = ArrayList<StorageOption>().apply {
add(WebDavOption(context))
}
val intent = Intent(PROVIDER_INTERFACE) val intent = Intent(PROVIDER_INTERFACE)
val providers = packageManager.queryIntentContentProviders(intent, 0) val providers = packageManager.queryIntentContentProviders(intent, 0)
for (info in providers) { for (info in providers) {

View file

@ -55,7 +55,7 @@ internal class StorageOptionsFragment : Fragment(), StorageOptionClickedListener
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle?, savedInstanceState: Bundle?,
): View { ): View {
val v: View = inflater.inflate(R.layout.fragment_storage_root, container, false) val v: View = inflater.inflate(R.layout.fragment_storage_options, container, false)
titleView = v.requireViewById(R.id.titleView) titleView = v.requireViewById(R.id.titleView)
warningIcon = v.requireViewById(R.id.warningIcon) warningIcon = v.requireViewById(R.id.warningIcon)

View file

@ -1,5 +1,6 @@
package com.stevesoltys.seedvault.ui.storage package com.stevesoltys.seedvault.ui.storage
import android.annotation.UiThread
import android.app.Application import android.app.Application
import android.content.Context import android.content.Context
import android.content.Context.USB_SERVICE import android.content.Context.USB_SERVICE
@ -11,6 +12,7 @@ import android.util.Log
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.stevesoltys.seedvault.R import com.stevesoltys.seedvault.R
import com.stevesoltys.seedvault.isMassStorage import com.stevesoltys.seedvault.isMassStorage
import com.stevesoltys.seedvault.permitDiskReads import com.stevesoltys.seedvault.permitDiskReads
@ -21,6 +23,8 @@ import com.stevesoltys.seedvault.settings.Storage
import com.stevesoltys.seedvault.ui.LiveEvent import com.stevesoltys.seedvault.ui.LiveEvent
import com.stevesoltys.seedvault.ui.MutableLiveEvent import com.stevesoltys.seedvault.ui.MutableLiveEvent
import com.stevesoltys.seedvault.ui.storage.StorageOption.SafOption import com.stevesoltys.seedvault.ui.storage.StorageOption.SafOption
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
private val TAG = StorageViewModel::class.java.simpleName private val TAG = StorageViewModel::class.java.simpleName
@ -38,7 +42,7 @@ internal abstract class StorageViewModel(
protected val mLocationChecked = MutableLiveEvent<LocationResult>() protected val mLocationChecked = MutableLiveEvent<LocationResult>()
internal val locationChecked: LiveEvent<LocationResult> get() = mLocationChecked internal val locationChecked: LiveEvent<LocationResult> get() = mLocationChecked
private val storageRootFetcher by lazy { StorageRootFetcher(app, isRestoreOperation) } private val storageOptionFetcher by lazy { StorageOptionFetcher(app, isRestoreOperation) }
private var safOption: SafOption? = null private var safOption: SafOption? = null
internal var isSetupWizard: Boolean = false internal var isSetupWizard: Boolean = false
@ -60,11 +64,11 @@ internal abstract class StorageViewModel(
} }
internal fun loadStorageRoots() { internal fun loadStorageRoots() {
if (storageRootFetcher.getRemovableStorageListener() == null) { if (storageOptionFetcher.getRemovableStorageListener() == null) {
storageRootFetcher.setRemovableStorageListener(this) storageOptionFetcher.setRemovableStorageListener(this)
} }
Thread { Thread {
mStorageOptions.postValue(storageRootFetcher.getStorageOptions()) mStorageOptions.postValue(storageOptionFetcher.getStorageOptions())
}.start() }.start()
} }
@ -88,7 +92,7 @@ internal abstract class StorageViewModel(
val takeFlags = FLAG_GRANT_READ_URI_PERMISSION or FLAG_GRANT_WRITE_URI_PERMISSION val takeFlags = FLAG_GRANT_READ_URI_PERMISSION or FLAG_GRANT_WRITE_URI_PERMISSION
app.contentResolver.takePersistableUriPermission(uri, takeFlags) app.contentResolver.takePersistableUriPermission(uri, takeFlags)
onLocationSet(uri) onSafUriSet(uri)
} }
/** /**
@ -144,10 +148,10 @@ internal abstract class StorageViewModel(
return false return false
} }
abstract fun onLocationSet(uri: Uri) abstract fun onSafUriSet(uri: Uri)
override fun onCleared() { override fun onCleared() {
storageRootFetcher.setRemovableStorageListener(null) storageOptionFetcher.setRemovableStorageListener(null)
super.onCleared() super.onCleared()
} }