Allow to setup NextCloud account during restore
This is especially useful when restore is only allowed during SetupWizard and the backup was stored on a NextCloud account.
This commit is contained in:
parent
2ebc7c9479
commit
791f68300d
8 changed files with 1345 additions and 9 deletions
app/src/main
java/com/stevesoltys/seedvault/ui/storage
res
|
@ -3,6 +3,7 @@ package com.stevesoltys.seedvault.ui.storage
|
|||
import android.app.Application
|
||||
import android.net.Uri
|
||||
import android.util.Log
|
||||
import androidx.annotation.WorkerThread
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import com.stevesoltys.seedvault.R
|
||||
import com.stevesoltys.seedvault.transport.backup.plugins.DIRECTORY_ROOT
|
||||
|
@ -14,19 +15,19 @@ internal class RestoreStorageViewModel(private val app: Application) : StorageVi
|
|||
|
||||
override val isRestoreOperation = true
|
||||
|
||||
override fun onLocationSet(uri: Uri) {
|
||||
override fun onLocationSet(uri: Uri) = Thread {
|
||||
if (hasBackup(uri)) {
|
||||
saveStorage(uri)
|
||||
|
||||
mLocationChecked.setEvent(LocationResult())
|
||||
mLocationChecked.postEvent(LocationResult())
|
||||
} else {
|
||||
Log.w(TAG, "Location was rejected: $uri")
|
||||
|
||||
// notify the UI that the location was invalid
|
||||
val errorMsg = app.getString(R.string.restore_invalid_location_message, DIRECTORY_ROOT)
|
||||
mLocationChecked.setEvent(LocationResult(errorMsg))
|
||||
mLocationChecked.postEvent(LocationResult(errorMsg))
|
||||
}
|
||||
}
|
||||
}.start()
|
||||
|
||||
/**
|
||||
* Searches if there's really a backup available in the given location.
|
||||
|
@ -37,7 +38,11 @@ internal class RestoreStorageViewModel(private val app: Application) : StorageVi
|
|||
*
|
||||
* TODO maybe move this to the RestoreCoordinator once we can inject it
|
||||
*/
|
||||
@WorkerThread
|
||||
private fun hasBackup(folderUri: Uri): Boolean {
|
||||
// FIXME This currently fails for NextCloud's DocumentsProvider,
|
||||
// if called right after setting up an account.
|
||||
// It requires three attempts to finally find existing backups.
|
||||
val parent = DocumentFile.fromTreeUri(app, folderUri) ?: throw AssertionError()
|
||||
val rootDir = parent.findFile(DIRECTORY_ROOT) ?: return false
|
||||
val backupSets = DocumentsProviderRestorePlugin.getBackups(rootDir)
|
||||
|
|
|
@ -70,7 +70,9 @@ internal class StorageRootAdapter(
|
|||
else -> summaryView.visibility = GONE
|
||||
}
|
||||
v.setOnClickListener {
|
||||
if (!isRestore && item.isInternal()) {
|
||||
if (item.overrideClickListener != null) {
|
||||
item.overrideClickListener.invoke()
|
||||
} else if (!isRestore && item.isInternal()) {
|
||||
showWarningDialog(v.context, item)
|
||||
} else {
|
||||
listener.onClick(item)
|
||||
|
|
|
@ -3,6 +3,8 @@ package com.stevesoltys.seedvault.ui.storage
|
|||
import android.Manifest.permission.MANAGE_DOCUMENTS
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.Intent.ACTION_VIEW
|
||||
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
import android.content.pm.PackageManager.GET_META_DATA
|
||||
import android.content.pm.ProviderInfo
|
||||
import android.database.ContentObserver
|
||||
|
@ -24,6 +26,10 @@ const val ROOT_ID_DEVICE = "primary"
|
|||
const val ROOT_ID_HOME = "home"
|
||||
|
||||
const val AUTHORITY_DOWNLOADS = "com.android.providers.downloads.documents"
|
||||
const val AUTHORITY_NEXTCLOUD = "org.nextcloud.documents"
|
||||
|
||||
private const val NEXTCLOUD_PACKAGE = "com.nextcloud.client"
|
||||
private const val NEXTCLOUD_ACTIVITY = "com.owncloud.android.authentication.AuthenticatorActivity"
|
||||
|
||||
data class StorageRoot(
|
||||
internal val authority: String,
|
||||
|
@ -34,7 +40,8 @@ data class StorageRoot(
|
|||
internal val summary: String?,
|
||||
internal val availableBytes: Long?,
|
||||
internal val isUsb: Boolean,
|
||||
internal val enabled: Boolean = true) {
|
||||
internal val enabled: Boolean = true,
|
||||
internal val overrideClickListener: (() -> Unit)? = null) {
|
||||
|
||||
internal val uri: Uri by lazy {
|
||||
DocumentsContract.buildTreeDocumentUri(authority, documentId)
|
||||
|
@ -86,7 +93,8 @@ internal class StorageRootFetcher(private val context: Context, private val isRe
|
|||
roots.addAll(getRoots(providerInfo))
|
||||
}
|
||||
}
|
||||
if (isAuthoritySupported(AUTHORITY_STORAGE)) checkOrAddUsbRoot(roots)
|
||||
checkOrAddUsbRoot(roots)
|
||||
checkOrAddNextCloudRoot(roots)
|
||||
return roots
|
||||
}
|
||||
|
||||
|
@ -136,7 +144,10 @@ internal class StorageRootFetcher(private val context: Context, private val isRe
|
|||
}
|
||||
|
||||
private fun checkOrAddUsbRoot(roots: ArrayList<StorageRoot>) {
|
||||
if (!isAuthoritySupported(AUTHORITY_STORAGE)) return
|
||||
|
||||
for (root in roots) {
|
||||
// return if we already have a USB storage root
|
||||
if (root.authority == AUTHORITY_STORAGE && root.isUsb) return
|
||||
}
|
||||
val root = StorageRoot(
|
||||
|
@ -153,6 +164,44 @@ internal class StorageRootFetcher(private val context: Context, private val isRe
|
|||
roots.add(root)
|
||||
}
|
||||
|
||||
private fun checkOrAddNextCloudRoot(roots: ArrayList<StorageRoot>) {
|
||||
if (!isRestore) return
|
||||
|
||||
for (root in roots) {
|
||||
// return if we already have a NextCloud storage root
|
||||
if (root.authority == AUTHORITY_NEXTCLOUD) return
|
||||
}
|
||||
val intent = Intent().apply {
|
||||
addFlags(FLAG_ACTIVITY_NEW_TASK)
|
||||
setClassName(NEXTCLOUD_PACKAGE, NEXTCLOUD_ACTIVITY)
|
||||
// setting a nc:// Uri prevents FirstRunActivity to show
|
||||
data = Uri.parse("nc://login/server:")
|
||||
putExtra("onlyAdd", true)
|
||||
}
|
||||
val isInstalled = packageManager.resolveActivity(intent, 0) != null
|
||||
val root = StorageRoot(
|
||||
authority = AUTHORITY_NEXTCLOUD,
|
||||
rootId = "fake",
|
||||
documentId = "fake",
|
||||
icon = getIcon(context, AUTHORITY_NEXTCLOUD, "fake", 0),
|
||||
title = context.getString(R.string.storage_fake_nextcloud_title),
|
||||
summary = context.getString(if (isInstalled) R.string.storage_fake_nextcloud_summary_installed else R.string.storage_fake_nextcloud_summary),
|
||||
availableBytes = null,
|
||||
isUsb = false,
|
||||
enabled = true,
|
||||
overrideClickListener = {
|
||||
if (isInstalled) context.startActivity(intent)
|
||||
else {
|
||||
val uri = Uri.parse("market://details?id=$NEXTCLOUD_PACKAGE")
|
||||
val i = Intent(ACTION_VIEW, uri)
|
||||
i.addFlags(FLAG_ACTIVITY_NEW_TASK)
|
||||
context.startActivity(i)
|
||||
}
|
||||
}
|
||||
)
|
||||
roots.add(root)
|
||||
}
|
||||
|
||||
private fun ProviderInfo.isSupported(): Boolean {
|
||||
return if (!exported) {
|
||||
Log.w(TAG, "Provider is not exported")
|
||||
|
@ -202,6 +251,7 @@ internal class StorageRootFetcher(private val context: Context, private val isRe
|
|||
return getPackageIcon(context, authority, icon) ?: when {
|
||||
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)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,8 +17,6 @@ import com.stevesoltys.seedvault.ui.INTENT_EXTRA_IS_RESTORE
|
|||
import com.stevesoltys.seedvault.ui.REQUEST_CODE_OPEN_DOCUMENT_TREE
|
||||
import kotlinx.android.synthetic.main.fragment_storage_root.*
|
||||
|
||||
private val TAG = StorageRootsFragment::class.java.simpleName
|
||||
|
||||
internal class StorageRootsFragment : Fragment(), StorageRootClickedListener {
|
||||
|
||||
companion object {
|
||||
|
|
24
app/src/main/res/drawable/nextcloud.xml
Normal file
24
app/src/main/res/drawable/nextcloud.xml
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Nextcloud Android client application
|
||||
|
||||
Copyright (C) 2017 Andy Scherzinger
|
||||
Copyright (C) 2017 Nextcloud.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public
|
||||
License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/nextcloud_background"/>
|
||||
<foreground android:drawable="@drawable/nextcloud_foreground"/>
|
||||
</adaptive-icon>
|
1222
app/src/main/res/drawable/nextcloud_background.xml
Normal file
1222
app/src/main/res/drawable/nextcloud_background.xml
Normal file
File diff suppressed because it is too large
Load diff
32
app/src/main/res/drawable/nextcloud_foreground.xml
Normal file
32
app/src/main/res/drawable/nextcloud_foreground.xml
Normal file
|
@ -0,0 +1,32 @@
|
|||
<!--
|
||||
Nextcloud Android client application
|
||||
|
||||
Copyright (C) 2017 Andy Scherzinger
|
||||
Copyright (C) 2017 Nextcloud.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public
|
||||
License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="1636.9231"
|
||||
android:viewportHeight="1636.9231">
|
||||
<group android:translateX="286.46155"
|
||||
android:translateY="286.46155">
|
||||
<path
|
||||
android:pathData="M532.7,320C439.3,320 360.9,383.9 337,469.9 316.1,423.9 270.1,391.3 216.6,391.3 143.8,391.3 84,451.2 84,524c-0,72.8 59.8,132.6 132.6,132.7 53.5,-0 99.4,-32.6 120.4,-78.6 23.9,86 102.4,149.9 195.7,149.9 92.8,0 170.8,-63.2 195.3,-148.5 21.2,45.1 66.5,77.2 119.4,77.2 72.8,0 132.7,-59.8 132.6,-132.7 -0,-72.8 -59.9,-132.6 -132.6,-132.6 -52.8,0 -98.2,32 -119.4,77.2 -24.4,-85.3 -102.4,-148.5 -195.3,-148.5zM532.7,397.9c70.1,0 126.1,56 126.1,126.1 0,70.1 -56,126.1 -126.1,126.1 -70.1,-0 -126.1,-56 -126.1,-126.1 0,-70.1 56,-126.1 126.1,-126.1zM216.6,469.2c30.7,0 54.8,24.1 54.8,54.8 0,30.7 -24,54.8 -54.8,54.8 -30.7,0 -54.8,-24.1 -54.8,-54.8 0,-30.7 24.1,-54.8 54.8,-54.8zM847.4,469.2c30.7,-0 54.8,24.1 54.8,54.8 0,30.7 -24.1,54.8 -54.8,54.8 -30.7,0 -54.8,-24.1 -54.8,-54.8 0,-30.7 24.1,-54.8 54.8,-54.8z"
|
||||
android:fillType="nonZero"
|
||||
android:fillColor="#ffffff"/>
|
||||
</group>
|
||||
</vector>
|
|
@ -27,6 +27,9 @@
|
|||
<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>
|
||||
<string name="storage_fake_nextcloud_title">Nextcloud</string>
|
||||
<string name="storage_fake_nextcloud_summary">Click to install</string>
|
||||
<string name="storage_fake_nextcloud_summary_installed">Click to set up account</string>
|
||||
<string name="storage_check_fragment_backup_title">Initializing backup location…</string>
|
||||
<string name="storage_check_fragment_restore_title">Looking for backups…</string>
|
||||
<string name="storage_check_fragment_backup_error">An error occurred while accessing the backup location.</string>
|
||||
|
|
Loading…
Add table
Reference in a new issue