1
0
Fork 0

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:
Torsten Grote 2019-12-10 11:50:13 -03:00
parent 2ebc7c9479
commit 791f68300d
No known key found for this signature in database
GPG key ID: 3E5F77D92CF891FF
8 changed files with 1345 additions and 9 deletions

View file

@ -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)

View file

@ -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)

View file

@ -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
}
}

View file

@ -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 {

View 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>

File diff suppressed because it is too large Load diff

View 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>

View file

@ -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>