Save more information about current storage location

Show storage name in settings
This commit is contained in:
Torsten Grote 2019-09-13 14:37:32 -03:00
parent c6f83647b2
commit 54ad762eb1
No known key found for this signature in database
GPG key ID: 3E5F77D92CF891FF
14 changed files with 80 additions and 57 deletions

View file

@ -3,8 +3,8 @@ package com.stevesoltys.backup
import androidx.documentfile.provider.DocumentFile
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.runner.AndroidJUnit4
import com.stevesoltys.backup.settings.getBackupFolderUri
import com.stevesoltys.backup.settings.getBackupToken
import com.stevesoltys.backup.settings.getStorage
import com.stevesoltys.backup.transport.backup.plugins.DocumentsStorage
import com.stevesoltys.backup.transport.backup.plugins.createOrGetFile
import org.junit.After
@ -22,7 +22,7 @@ class DocumentsStorageTest {
private val context = InstrumentationRegistry.getInstrumentation().targetContext
private val token = getBackupToken(context)
private val folderUri = getBackupFolderUri(context)
private val folderUri = getStorage(context)
private val storage = DocumentsStorage(context, folderUri, token)
private lateinit var file: DocumentFile

View file

@ -31,7 +31,4 @@ class Backup : Application() {
}
// TODO fix
fun Uri.isOnExternalStorage() = authority == AUTHORITY_STORAGE
fun isDebugBuild() = Build.TYPE == "userdebug"

View file

@ -18,9 +18,8 @@ import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
import static android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
import static com.stevesoltys.backup.activity.MainActivity.OPEN_DOCUMENT_TREE_BACKUP_REQUEST_CODE;
import static com.stevesoltys.backup.activity.MainActivity.OPEN_DOCUMENT_TREE_REQUEST_CODE;
import static com.stevesoltys.backup.settings.SettingsManagerKt.getBackupFolderUri;
import static com.stevesoltys.backup.settings.SettingsManagerKt.getBackupPassword;
import static com.stevesoltys.backup.settings.SettingsManagerKt.setBackupFolderUri;
import static com.stevesoltys.backup.settings.SettingsManagerKt.getStorage;
/**
* @author Steve Soltys
@ -31,7 +30,7 @@ public class MainActivityController {
public static final String DOCUMENT_MIME_TYPE = "application/octet-stream";
void onBackupButtonClicked(Activity parent) {
Uri folderUri = getBackupFolderUri(parent);
Uri folderUri = null;
if (folderUri == null) {
showChooseFolderActivity(parent, true);
} else {
@ -42,7 +41,7 @@ public class MainActivityController {
}
boolean isChangeBackupLocationButtonVisible(Activity parent) {
return getBackupFolderUri(parent) != null;
return getStorage(parent) != null;
}
private void showChooseFolderActivity(Activity parent, boolean continueToBackup) {
@ -75,7 +74,7 @@ public class MainActivityController {
}
boolean onAutomaticBackupsButtonClicked(Activity parent) {
if (getBackupFolderUri(parent) == null || getBackupPassword(parent) == null) {
if (getStorage(parent) == null || getBackupPassword(parent) == null) {
Toast.makeText(parent, "Please make at least one manual backup first.", Toast.LENGTH_SHORT).show();
return false;
}
@ -103,9 +102,6 @@ public class MainActivityController {
(FLAG_GRANT_READ_URI_PERMISSION | FLAG_GRANT_WRITE_URI_PERMISSION);
parent.getContentResolver().takePersistableUriPermission(folderUri, takeFlags);
// store backup folder location in settings
setBackupFolderUri(parent, folderUri);
if (!continueToBackup) return;
showCreateBackupActivity(parent);

View file

@ -29,6 +29,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
private lateinit var backup: TwoStatePreference
private lateinit var autoRestore: TwoStatePreference
private lateinit var backupLocation: Preference
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.settings, rootKey)
@ -49,7 +50,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
}
}
val backupLocation = findPreference<Preference>("backup_location")!!
backupLocation = findPreference<Preference>("backup_location")!!
backupLocation.setOnPreferenceClickListener {
viewModel.chooseBackupLocation()
true
@ -85,6 +86,10 @@ class SettingsFragment : PreferenceFragmentCompat() {
val resolver = requireContext().contentResolver
autoRestore.isChecked = Settings.Secure.getInt(resolver, BACKUP_AUTO_RESTORE, 1) == 1
// TODO add time of last backup here
val storageName = getStorage(requireContext())?.name
backupLocation.summary = storageName ?: getString(R.string.settings_backup_location_none )
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {

View file

@ -5,21 +5,34 @@ import android.net.Uri
import android.preference.PreferenceManager.getDefaultSharedPreferences
import java.util.*
private const val PREF_KEY_BACKUP_URI = "backupUri"
private const val PREF_KEY_STORAGE_URI = "storageUri"
private const val PREF_KEY_STORAGE_NAME = "storageName"
private const val PREF_KEY_STORAGE_EJECTABLE = "storageEjectable"
private const val PREF_KEY_BACKUP_TOKEN = "backupToken"
private const val PREF_KEY_BACKUP_PASSWORD = "backupLegacyPassword"
fun setBackupFolderUri(context: Context, uri: Uri) {
data class Storage(
val uri: Uri,
val name: String,
val ejectable: Boolean
)
fun setStorage(context: Context, storage: Storage) {
getDefaultSharedPreferences(context)
.edit()
.putString(PREF_KEY_BACKUP_URI, uri.toString())
.putString(PREF_KEY_STORAGE_URI, storage.uri.toString())
.putString(PREF_KEY_STORAGE_NAME, storage.name)
.putBoolean(PREF_KEY_STORAGE_EJECTABLE, storage.ejectable)
.apply()
}
fun getBackupFolderUri(context: Context): Uri? {
val uriStr = getDefaultSharedPreferences(context).getString(PREF_KEY_BACKUP_URI, null)
?: return null
return Uri.parse(uriStr)
fun getStorage(context: Context): Storage? {
val prefs = getDefaultSharedPreferences(context)
val uriStr = prefs.getString(PREF_KEY_STORAGE_URI, null) ?: return null
val uri = Uri.parse(uriStr)
val name = prefs.getString(PREF_KEY_STORAGE_NAME, null) ?: throw IllegalStateException()
val ejectable = prefs.getBoolean(PREF_KEY_STORAGE_EJECTABLE, false)
return Storage(uri, name, ejectable)
}
/**

View file

@ -8,8 +8,8 @@ import com.stevesoltys.backup.header.HeaderReaderImpl
import com.stevesoltys.backup.header.HeaderWriterImpl
import com.stevesoltys.backup.metadata.MetadataReaderImpl
import com.stevesoltys.backup.metadata.MetadataWriterImpl
import com.stevesoltys.backup.settings.getBackupFolderUri
import com.stevesoltys.backup.settings.getBackupToken
import com.stevesoltys.backup.settings.getStorage
import com.stevesoltys.backup.transport.backup.BackupCoordinator
import com.stevesoltys.backup.transport.backup.FullBackup
import com.stevesoltys.backup.transport.backup.InputFactory
@ -26,7 +26,7 @@ class PluginManager(context: Context) {
// We can think about using an injection framework such as Dagger to simplify this.
private val storage = DocumentsStorage(context, getBackupFolderUri(context), getBackupToken(context))
private val storage = DocumentsStorage(context, getStorage(context), getBackupToken(context))
private val headerWriter = HeaderWriterImpl()
private val headerReader = HeaderReaderImpl()

View file

@ -2,9 +2,9 @@ package com.stevesoltys.backup.transport.backup.plugins
import android.content.Context
import android.content.pm.PackageInfo
import android.net.Uri
import android.util.Log
import androidx.documentfile.provider.DocumentFile
import com.stevesoltys.backup.settings.Storage
import com.stevesoltys.backup.settings.getAndSaveNewBackupToken
import java.io.IOException
import java.io.InputStream
@ -19,10 +19,10 @@ private const val MIME_TYPE = "application/octet-stream"
private val TAG = DocumentsStorage::class.java.simpleName
class DocumentsStorage(private val context: Context, parentFolder: Uri?, token: Long) {
class DocumentsStorage(private val context: Context, storage: Storage?, token: Long) {
internal val rootBackupDir: DocumentFile? by lazy {
val folderUri = parentFolder ?: return@lazy null
val folderUri = storage?.uri ?: return@lazy null
// [fromTreeUri] should only return null when SDK_INT < 21
val parent = DocumentFile.fromTreeUri(context, folderUri) ?: throw AssertionError()
try {

View file

@ -3,15 +3,12 @@ package com.stevesoltys.backup.ui.storage
import android.app.Application
import android.app.backup.BackupProgress
import android.app.backup.IBackupObserver
import android.content.Intent
import android.net.Uri
import android.util.Log
import androidx.annotation.WorkerThread
import com.stevesoltys.backup.Backup
import com.stevesoltys.backup.R
import com.stevesoltys.backup.settings.getAndSaveNewBackupToken
import com.stevesoltys.backup.settings.setBackupFolderUri
import com.stevesoltys.backup.transport.ConfigurableBackupTransportService
import com.stevesoltys.backup.transport.TRANSPORT_ID
private val TAG = BackupStorageViewModel::class.java.simpleName
@ -21,19 +18,11 @@ internal class BackupStorageViewModel(private val app: Application) : StorageVie
override val isRestoreOperation = false
override fun onLocationSet(uri: Uri) {
// store backup folder location in settings
setBackupFolderUri(app, uri)
// TODO also set the storage name
// stop backup service to be sure the old location will get updated
app.stopService(Intent(app, ConfigurableBackupTransportService::class.java))
saveStorage(uri)
// use a new backup token
getAndSaveNewBackupToken(app)
Log.d(TAG, "New storage location chosen: $uri")
// initialize the new location
val observer = InitializationObserver()
Backup.backupManager.initializeTransports(arrayOf(TRANSPORT_ID), observer)

View file

@ -1,13 +1,10 @@
package com.stevesoltys.backup.ui.storage
import android.app.Application
import android.content.Intent
import android.net.Uri
import android.util.Log
import androidx.documentfile.provider.DocumentFile
import com.stevesoltys.backup.R
import com.stevesoltys.backup.settings.setBackupFolderUri
import com.stevesoltys.backup.transport.ConfigurableBackupTransportService
import com.stevesoltys.backup.transport.backup.plugins.DIRECTORY_ROOT
import com.stevesoltys.backup.transport.restore.plugins.DocumentsProviderRestorePlugin
@ -19,13 +16,7 @@ internal class RestoreStorageViewModel(private val app: Application) : StorageVi
override fun onLocationSet(uri: Uri) {
if (hasBackup(uri)) {
// store backup folder location in settings
setBackupFolderUri(app, uri)
// stop backup service to be sure the old location will get updated
app.stopService(Intent(app, ConfigurableBackupTransportService::class.java))
Log.d(TAG, "New storage location chosen: $uri")
saveStorage(uri)
mLocationChecked.setEvent(LocationResult())
} else {

View file

@ -36,6 +36,10 @@ data class StorageRoot(
internal val supportsEject: Boolean,
internal val enabled: Boolean = true) {
internal val uri: Uri by lazy {
DocumentsContract.buildTreeDocumentUri(authority, documentId)
}
fun isInternal(): Boolean {
return authority == AUTHORITY_STORAGE && !supportsEject
}

View file

@ -3,7 +3,6 @@ package com.stevesoltys.backup.ui.storage
import android.content.Intent
import android.content.Intent.*
import android.os.Bundle
import android.provider.DocumentsContract
import android.view.LayoutInflater
import android.view.View
import android.view.View.INVISIBLE
@ -72,8 +71,9 @@ internal class StorageRootsFragment : Fragment(), StorageRootClickedListener {
}
override fun onClick(root: StorageRoot) {
viewModel.onStorageRootChosen(root)
val intent = Intent(requireContext(), PermissionGrantActivity::class.java)
intent.data = DocumentsContract.buildTreeDocumentUri(root.authority, root.documentId)
intent.data = root.uri
intent.addFlags(FLAG_GRANT_PERSISTABLE_URI_PERMISSION or
FLAG_GRANT_READ_URI_PERMISSION or FLAG_GRANT_WRITE_URI_PERMISSION)
startActivityForResult(intent, REQUEST_CODE_OPEN_DOCUMENT_TREE)

View file

@ -6,15 +6,21 @@ import android.content.Intent
import android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION
import android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION
import android.net.Uri
import android.util.Log
import androidx.documentfile.provider.DocumentFile
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.stevesoltys.backup.isOnExternalStorage
import com.stevesoltys.backup.settings.getBackupFolderUri
import com.stevesoltys.backup.R
import com.stevesoltys.backup.settings.Storage
import com.stevesoltys.backup.settings.getStorage
import com.stevesoltys.backup.settings.setStorage
import com.stevesoltys.backup.transport.ConfigurableBackupTransportService
import com.stevesoltys.backup.ui.LiveEvent
import com.stevesoltys.backup.ui.MutableLiveEvent
private val TAG = StorageViewModel::class.java.simpleName
internal abstract class StorageViewModel(private val app: Application) : AndroidViewModel(app), RemovableStorageListener {
private val mStorageRoots = MutableLiveData<List<StorageRoot>>()
@ -27,14 +33,15 @@ internal abstract class StorageViewModel(private val app: Application) : Android
internal val locationChecked: LiveEvent<LocationResult> get() = mLocationChecked
private val storageRootFetcher by lazy { StorageRootFetcher(app) }
private var storageRoot: StorageRoot? = null
abstract val isRestoreOperation: Boolean
companion object {
internal fun validLocationIsSet(context: Context): Boolean {
val uri = getBackupFolderUri(context) ?: return false
if (uri.isOnExternalStorage()) return true // TODO use ejectable instead
val file = DocumentFile.fromTreeUri(context, uri) ?: return false
val storage = getStorage(context) ?: return false
if (storage.ejectable) return true
val file = DocumentFile.fromTreeUri(context, storage.uri) ?: return false
return file.isDirectory
}
}
@ -50,6 +57,9 @@ internal abstract class StorageViewModel(private val app: Application) : Android
override fun onStorageChanged() = loadStorageRoots()
fun onStorageRootChosen(root: StorageRoot) {
storageRoot = root
}
internal fun onUriPermissionGranted(result: Intent?) {
val uri = result?.data ?: return
@ -66,6 +76,23 @@ internal abstract class StorageViewModel(private val app: Application) : Android
abstract fun onLocationSet(uri: Uri)
protected fun saveStorage(uri: Uri) {
// store backup storage location in settings
val root = storageRoot ?: throw IllegalStateException()
val name = if (root.isInternal()) {
"${root.title} (${app.getString(R.string.settings_backup_location_internal)})"
} else {
root.title
}
val storage = Storage(uri, name, root.supportsEject)
setStorage(app, storage)
// stop backup service to be sure the old location will get updated
app.stopService(Intent(app, ConfigurableBackupTransportService::class.java))
Log.d(TAG, "New storage location saved: $uri")
}
override fun onCleared() {
storageRootFetcher.setRemovableStorageListener(null)
super.onCleared()

View file

@ -29,7 +29,8 @@
<string name="settings_backup_location_picker">Choose backup location</string>
<string name="settings_backup_location_title">Backup Location</string>
<string name="settings_backup_location_invalid">The chosen location can not be used.</string>
<string name="settings_backup_external_storage">External Storage</string>
<string name="settings_backup_location_none">None</string>
<string name="settings_backup_location_internal">Internal Storage</string>
<string name="settings_info">All backups are encrypted on your phone. To restore from backup you will need your 12-word recovery code.</string>
<string name="settings_auto_restore_title">Automatic restore</string>
<string name="settings_auto_restore_summary">When reinstalling an app, restore backed up settings and data</string>

View file

@ -10,7 +10,7 @@
app:dependency="backup"
app:icon="@drawable/ic_storage"
app:key="backup_location"
app:summary="@string/settings_backup_external_storage"
app:summary="@string/settings_backup_location_none"
app:title="@string/settings_backup_location" />
<androidx.preference.SwitchPreferenceCompat