Prepare backup destination UI refactoring
Mostly renaming for clarity.
This commit is contained in:
parent
ec8190755e
commit
6788d0d25a
10 changed files with 74 additions and 40 deletions
|
@ -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.Intent
|
||||
|
@ -9,8 +14,13 @@ import android.provider.DocumentsContract
|
|||
import android.provider.DocumentsContract.Document.COLUMN_DISPLAY_NAME
|
||||
import android.provider.DocumentsContract.Document.COLUMN_DOCUMENT_ID
|
||||
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.StorageRootResolver.getIcon
|
||||
|
||||
private const val DAVX5_PACKAGE = "at.bitfire.davdroid"
|
||||
private const val DAVX5_ACTIVITY = "at.bitfire.davdroid.ui.webdav.WebdavMountsActivity"
|
||||
|
@ -29,15 +39,15 @@ internal class SafStorageOptions(
|
|||
|
||||
private val packageManager = context.packageManager
|
||||
|
||||
internal fun checkOrAddExtraRoots(roots: ArrayList<SafOption>) {
|
||||
internal fun checkOrAddExtraRoots(roots: ArrayList<StorageOption>) {
|
||||
checkOrAddUsbRoot(roots)
|
||||
checkOrAddDavX5Root(roots)
|
||||
checkOrAddNextCloudRoot(roots)
|
||||
checkOrAddRoundSyncRoots(roots)
|
||||
}
|
||||
|
||||
private fun checkOrAddUsbRoot(roots: ArrayList<SafOption>) {
|
||||
if (doNotInclude(AUTHORITY_STORAGE, roots) { it.isUsb }) return
|
||||
private fun checkOrAddUsbRoot(roots: ArrayList<StorageOption>) {
|
||||
if (doNotInclude(AUTHORITY_STORAGE, roots) { it is SafOption && it.isUsb }) return
|
||||
|
||||
val root = SafOption(
|
||||
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.
|
||||
*/
|
||||
private fun checkOrAddRoundSyncRoots(roots: ArrayList<SafOption>) {
|
||||
private fun checkOrAddRoundSyncRoots(roots: ArrayList<StorageOption>) {
|
||||
|
||||
val roundSyncRoot = roots.firstOrNull {
|
||||
it.authority == AUTHORITY_ROUND_SYNC
|
||||
} ?: return
|
||||
it is SafOption && it.authority == AUTHORITY_ROUND_SYNC
|
||||
} as? SafOption ?: return
|
||||
|
||||
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.
|
||||
*/
|
||||
private fun checkOrAddDavX5Root(roots: ArrayList<SafOption>) {
|
||||
private fun checkOrAddDavX5Root(roots: ArrayList<StorageOption>) {
|
||||
if (doNotInclude(AUTHORITY_DAVX5, roots)) return
|
||||
|
||||
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
|
||||
* (which hides existing accounts).
|
||||
*/
|
||||
private fun checkOrAddNextCloudRoot(roots: ArrayList<SafOption>) {
|
||||
private fun checkOrAddNextCloudRoot(roots: ArrayList<StorageOption>) {
|
||||
if (doNotInclude(AUTHORITY_NEXTCLOUD, roots)) return
|
||||
|
||||
val intent = Intent().apply {
|
||||
|
@ -202,11 +212,12 @@ internal class SafStorageOptions(
|
|||
|
||||
private fun doNotInclude(
|
||||
authority: String,
|
||||
roots: ArrayList<SafOption>,
|
||||
doNotIncludeIfTrue: ((SafOption) -> Boolean)? = null,
|
||||
roots: ArrayList<StorageOption>,
|
||||
doNotIncludeIfTrue: ((StorageOption) -> Boolean)? = null,
|
||||
): Boolean {
|
||||
if (!isAuthoritySupported(authority)) return true
|
||||
for (root in roots) {
|
||||
if (root !is SafOption) continue
|
||||
if (root.authority == authority && doNotIncludeIfTrue?.invoke(root) != false) {
|
||||
return true
|
||||
}
|
|
@ -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.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_IS_CHILD
|
||||
import android.util.Log
|
||||
import androidx.appcompat.content.res.AppCompatResources.getDrawable
|
||||
import com.stevesoltys.seedvault.R
|
||||
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
|
||||
|
||||
internal object StorageRootResolver {
|
||||
|
@ -126,23 +139,23 @@ internal object StorageRootResolver {
|
|||
fun getIcon(context: Context, authority: String, rootId: String, icon: Int): Drawable? {
|
||||
return getPackageIcon(context, authority, icon) ?: when {
|
||||
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 -> {
|
||||
context.getDrawable(R.drawable.ic_usb)
|
||||
getDrawable(context, R.drawable.ic_usb)
|
||||
}
|
||||
|
||||
authority == AUTHORITY_NEXTCLOUD -> {
|
||||
context.getDrawable(R.drawable.nextcloud)
|
||||
getDrawable(context, R.drawable.nextcloud)
|
||||
}
|
||||
|
||||
authority == AUTHORITY_DAVX5 -> {
|
||||
context.getDrawable(R.drawable.davx5)
|
||||
getDrawable(context, R.drawable.davx5)
|
||||
}
|
||||
|
||||
authority == AUTHORITY_ROUND_SYNC -> {
|
||||
context.getDrawable(R.drawable.round_sync)
|
||||
getDrawable(context, R.drawable.round_sync)
|
||||
}
|
||||
|
||||
else -> null
|
|
@ -31,7 +31,7 @@ internal class BackupStorageViewModel(
|
|||
|
||||
override val isRestoreOperation = false
|
||||
|
||||
override fun onLocationSet(uri: Uri) {
|
||||
override fun onSafUriSet(uri: Uri) {
|
||||
val isUsb = saveStorage(uri)
|
||||
if (isUsb) {
|
||||
// disable storage backup if new storage is on USB
|
||||
|
|
|
@ -22,7 +22,7 @@ internal class RestoreStorageViewModel(
|
|||
|
||||
override val isRestoreOperation = true
|
||||
|
||||
override fun onLocationSet(uri: Uri) {
|
||||
override fun onSafUriSet(uri: Uri) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val storage = createStorage(uri)
|
||||
val hasBackup = try {
|
||||
|
|
|
@ -9,11 +9,11 @@ import android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
|||
import android.content.pm.PackageManager.PERMISSION_GRANTED
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.activity.result.contract.ActivityResultContracts.OpenDocumentTree
|
||||
import androidx.annotation.CallSuper
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.stevesoltys.seedvault.R
|
||||
import com.stevesoltys.seedvault.plugins.saf.StorageRootResolver
|
||||
import com.stevesoltys.seedvault.ui.BackupActivity
|
||||
import com.stevesoltys.seedvault.ui.INTENT_EXTRA_IS_RESTORE
|
||||
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) {
|
||||
if (viewModel.isRestoreOperation) {
|
||||
val dialog = AlertDialog.Builder(this)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.stevesoltys.seedvault.ui.storage
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.View.GONE
|
||||
|
@ -10,6 +11,7 @@ import android.view.ViewGroup
|
|||
import android.widget.Button
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import androidx.activity.addCallback
|
||||
import androidx.fragment.app.Fragment
|
||||
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(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
|
|
|
@ -13,9 +13,11 @@ import android.provider.DocumentsContract.PROVIDER_INTERFACE
|
|||
import android.provider.DocumentsContract.buildRootsUri
|
||||
import android.util.Log
|
||||
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
|
||||
|
||||
private val TAG = StorageRootFetcher::class.java.simpleName
|
||||
private val TAG = StorageOptionFetcher::class.java.simpleName
|
||||
|
||||
const val AUTHORITY_STORAGE = "com.android.externalstorage.documents"
|
||||
const val ROOT_ID_DEVICE = "primary"
|
||||
|
@ -30,7 +32,7 @@ internal interface RemovableStorageListener {
|
|||
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 contentResolver = context.contentResolver
|
||||
|
@ -60,7 +62,9 @@ internal class StorageRootFetcher(private val context: Context, private val isRe
|
|||
internal fun getRemovableStorageListener() = listener
|
||||
|
||||
internal fun getStorageOptions(): List<StorageOption> {
|
||||
val roots = ArrayList<SafOption>()
|
||||
val roots = ArrayList<StorageOption>().apply {
|
||||
add(WebDavOption(context))
|
||||
}
|
||||
val intent = Intent(PROVIDER_INTERFACE)
|
||||
val providers = packageManager.queryIntentContentProviders(intent, 0)
|
||||
for (info in providers) {
|
|
@ -55,7 +55,7 @@ internal class StorageOptionsFragment : Fragment(), StorageOptionClickedListener
|
|||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?,
|
||||
): 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)
|
||||
warningIcon = v.requireViewById(R.id.warningIcon)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.stevesoltys.seedvault.ui.storage
|
||||
|
||||
import android.annotation.UiThread
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.content.Context.USB_SERVICE
|
||||
|
@ -11,6 +12,7 @@ import android.util.Log
|
|||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.stevesoltys.seedvault.R
|
||||
import com.stevesoltys.seedvault.isMassStorage
|
||||
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.MutableLiveEvent
|
||||
import com.stevesoltys.seedvault.ui.storage.StorageOption.SafOption
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
private val TAG = StorageViewModel::class.java.simpleName
|
||||
|
||||
|
@ -38,7 +42,7 @@ internal abstract class StorageViewModel(
|
|||
protected val mLocationChecked = MutableLiveEvent<LocationResult>()
|
||||
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
|
||||
|
||||
internal var isSetupWizard: Boolean = false
|
||||
|
@ -60,11 +64,11 @@ internal abstract class StorageViewModel(
|
|||
}
|
||||
|
||||
internal fun loadStorageRoots() {
|
||||
if (storageRootFetcher.getRemovableStorageListener() == null) {
|
||||
storageRootFetcher.setRemovableStorageListener(this)
|
||||
if (storageOptionFetcher.getRemovableStorageListener() == null) {
|
||||
storageOptionFetcher.setRemovableStorageListener(this)
|
||||
}
|
||||
Thread {
|
||||
mStorageOptions.postValue(storageRootFetcher.getStorageOptions())
|
||||
mStorageOptions.postValue(storageOptionFetcher.getStorageOptions())
|
||||
}.start()
|
||||
}
|
||||
|
||||
|
@ -88,7 +92,7 @@ internal abstract class StorageViewModel(
|
|||
val takeFlags = FLAG_GRANT_READ_URI_PERMISSION or FLAG_GRANT_WRITE_URI_PERMISSION
|
||||
app.contentResolver.takePersistableUriPermission(uri, takeFlags)
|
||||
|
||||
onLocationSet(uri)
|
||||
onSafUriSet(uri)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -144,10 +148,10 @@ internal abstract class StorageViewModel(
|
|||
return false
|
||||
}
|
||||
|
||||
abstract fun onLocationSet(uri: Uri)
|
||||
abstract fun onSafUriSet(uri: Uri)
|
||||
|
||||
override fun onCleared() {
|
||||
storageRootFetcher.setRemovableStorageListener(null)
|
||||
storageOptionFetcher.setRemovableStorageListener(null)
|
||||
super.onCleared()
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue