From 60e4ca286660a45cd17f6d5bacf2955a37d13e67 Mon Sep 17 00:00:00 2001 From: Alex Baker Date: Tue, 8 Sep 2020 16:58:44 -0500 Subject: [PATCH] Update Google Drive backup settings Display timestamp of last backup --- .../main/java/org/tasks/drive/DriveInvoker.kt | 9 ++- .../main/java/org/tasks/jobs/DriveUploader.kt | 31 ++++---- .../tasks/preferences/fragments/Backups.kt | 75 ++++++++++++++----- app/src/main/res/values/strings.xml | 1 + app/src/main/res/xml/preferences_backups.xml | 18 ++++- 5 files changed, 92 insertions(+), 42 deletions(-) diff --git a/app/src/main/java/org/tasks/drive/DriveInvoker.kt b/app/src/main/java/org/tasks/drive/DriveInvoker.kt index 0e5ddc825..b26a5eab2 100644 --- a/app/src/main/java/org/tasks/drive/DriveInvoker.kt +++ b/app/src/main/java/org/tasks/drive/DriveInvoker.kt @@ -47,9 +47,10 @@ class DriveInvoker @Inject constructor( } @Throws(IOException::class) - suspend fun getFilesByPrefix(folderId: String?, prefix: String?): List { + suspend fun getFilesByPrefix(folderId: String?, vararg prefix: String?): List { + val namePredicate = prefix.joinToString(" or ") { "name contains '$it'" } val query = String.format( - "'%s' in parents and name contains '%s' and trashed = false and mimeType != '%s'", + "'%s' in parents and ($namePredicate) and trashed = false and mimeType != '%s'", folderId, prefix, MIME_FOLDER) return execute( service @@ -59,6 +60,7 @@ class DriveInvoker @Inject constructor( .setSpaces("drive") .setFields("files(id, modifiedTime)")) ?.files + ?.sortedWith(DRIVE_FILE_COMPARATOR) ?: emptyList() } @@ -139,5 +141,8 @@ class DriveInvoker @Inject constructor( companion object { private const val MIME_FOLDER = "application/vnd.google-apps.folder" + private val DRIVE_FILE_COMPARATOR = Comparator { f1, f2 -> + f2.modifiedTime.value.compareTo(f1.modifiedTime.value) + } } } \ No newline at end of file diff --git a/app/src/main/java/org/tasks/jobs/DriveUploader.kt b/app/src/main/java/org/tasks/jobs/DriveUploader.kt index 11095df00..115e7a9f4 100644 --- a/app/src/main/java/org/tasks/jobs/DriveUploader.kt +++ b/app/src/main/java/org/tasks/jobs/DriveUploader.kt @@ -19,7 +19,6 @@ import java.io.IOException import java.net.ConnectException import java.net.SocketTimeoutException import java.net.UnknownHostException -import java.util.* import javax.net.ssl.SSLException class DriveUploader @WorkerInject constructor( @@ -37,18 +36,20 @@ class DriveUploader @WorkerInject constructor( preferences.setString(R.string.p_google_drive_backup_folder, folder.id) drive.createFile(folder.id, uri) if (inputData.getBoolean(EXTRA_PURGE, false)) { - val files = drive.getFilesByPrefix(folder.id, "auto.") - for (file in getDeleteList(files)) { - try { - drive.delete(file) - } catch (e: GoogleJsonResponseException) { - if (e.statusCode == 404) { - Timber.e(e) - } else { - throw e + drive + .getFilesByPrefix(folder.id, "auto.") + .drop(BackupWork.DAYS_TO_KEEP_BACKUP) + .forEach { + try { + drive.delete(it) + } catch (e: GoogleJsonResponseException) { + if (e.statusCode == 404) { + Timber.e(e) + } else { + throw e + } + } } - } - } } Result.success() } catch (e: SocketTimeoutException) { @@ -89,17 +90,11 @@ class DriveUploader @WorkerInject constructor( private const val FOLDER_NAME = "Tasks Backups" private const val EXTRA_URI = "extra_uri" private const val EXTRA_PURGE = "extra_purge" - private val DRIVE_FILE_COMPARATOR = Comparator { f1, f2 -> - f2.modifiedTime.value.compareTo(f1.modifiedTime.value) - } fun getInputData(uri: Uri, purge: Boolean) = Data.Builder() .putString(EXTRA_URI, uri.toString()) .putBoolean(EXTRA_PURGE, purge) .build() - - private fun getDeleteList(files: List) = - files.sortedWith(DRIVE_FILE_COMPARATOR).drop(BackupWork.DAYS_TO_KEEP_BACKUP) } } \ No newline at end of file diff --git a/app/src/main/java/org/tasks/preferences/fragments/Backups.kt b/app/src/main/java/org/tasks/preferences/fragments/Backups.kt index faea9c5f8..c56c6306c 100644 --- a/app/src/main/java/org/tasks/preferences/fragments/Backups.kt +++ b/app/src/main/java/org/tasks/preferences/fragments/Backups.kt @@ -3,15 +3,18 @@ package org.tasks.preferences.fragments import android.app.Activity.RESULT_OK import android.content.Intent import android.os.Bundle +import androidx.lifecycle.lifecycleScope import androidx.preference.Preference import androidx.preference.SwitchPreferenceCompat import com.google.api.services.drive.DriveScopes import com.todoroo.andlib.utility.DateUtilities import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.launch import org.tasks.PermissionUtil import org.tasks.R import org.tasks.dialogs.ExportTasksDialog import org.tasks.dialogs.ImportTasksDialog +import org.tasks.drive.DriveInvoker import org.tasks.drive.DriveLoginActivity import org.tasks.files.FileHelper import org.tasks.gtasks.GoogleAccountManager @@ -37,6 +40,7 @@ class Backups : InjectingPreferenceFragment() { @Inject lateinit var toaster: Toaster @Inject lateinit var googleAccountManager: GoogleAccountManager @Inject lateinit var locale: Locale + @Inject lateinit var driveInvoker: DriveInvoker override fun getPreferenceXml() = R.xml.preferences_backups @@ -58,12 +62,36 @@ class Backups : InjectingPreferenceFragment() { .show(parentFragmentManager, FRAG_TAG_EXPORT_TASKS) false } + + findPreference(R.string.google_drive_backup) + .setOnPreferenceChangeListener(this@Backups::onGoogleDriveCheckChanged) + findPreference(R.string.p_google_drive_backup_account) + .setOnPreferenceClickListener { + requestGoogleDriveLogin() + false + } } override fun onResume() { super.onResume() - updateGoogleDriveCheckbox() + updateDriveAccount() + + val driveBackup = findPreference(R.string.google_drive_backup) as SwitchPreferenceCompat + driveBackup.isChecked = driveAccount != null + if (driveAccount != null) { + lifecycleScope.launch { + val folder = preferences.getStringValue(R.string.p_google_drive_backup_folder) + val files = driveInvoker.getFilesByPrefix(folder, "auto.", "user.") + driveBackup.summary = getString( + R.string.last_backup, + if (files.isEmpty()) { + getString(R.string.last_backup_never) + } else { + DateUtilities.getLongDateStringWithTime(files[0].modifiedTime.value, locale) + }) + } + } val lastBackup = preferences.getLong(R.string.p_backups_android_backup_last, 0L) findPreference(R.string.p_backups_android_backup_enabled).summary = @@ -123,35 +151,46 @@ class Backups : InjectingPreferenceFragment() { } } - private fun updateGoogleDriveCheckbox() { - val account = preferences.getStringValue(R.string.p_google_drive_backup_account) - val pref = findPreference(R.string.google_drive_backup) as SwitchPreferenceCompat - pref.isChecked = preferences.getBoolean(R.string.p_google_drive_backup, false) - && googleAccountManager.canAccessAccount(account) - && !preferences.alreadyNotified(account, DriveScopes.DRIVE_FILE) - pref.summary = if (pref.isChecked) account else null - findPreference(R.string.google_drive_backup) - .setOnPreferenceChangeListener(this@Backups::onGoogleDriveCheckChanged) - } + private val driveAccount: String? + get() { + val account = preferences.getStringValue(R.string.p_google_drive_backup_account) + val enabled = !account.isNullOrBlank() + && preferences.getBoolean(R.string.p_google_drive_backup, false) + && googleAccountManager.canAccessAccount(account) + && !preferences.alreadyNotified(account, DriveScopes.DRIVE_FILE) + return if (enabled) account else null + } private fun onGoogleDriveCheckChanged(preference: Preference, newValue: Any?) = when { newValue as Boolean -> { - if (permissionRequestor.requestAccountPermissions()) { - requestGoogleDriveLogin() - } + requestGoogleDriveLogin() false } else -> { preference.summary = null + preferences.setString(R.string.p_google_drive_backup_account, null) + updateDriveAccount() true } } + private fun updateDriveAccount() { + val account = driveAccount + val pref = findPreference(R.string.p_google_drive_backup_account) + pref.isEnabled = account != null + pref.summary = + account + ?.takeIf { it.isNotBlank() } + ?: getString(R.string.none) + } + private fun requestGoogleDriveLogin() { - startActivityForResult( - Intent(context, DriveLoginActivity::class.java), - REQUEST_DRIVE_BACKUP - ) + if (permissionRequestor.requestAccountPermissions()) { + startActivityForResult( + Intent(context, DriveLoginActivity::class.java), + REQUEST_DRIVE_BACKUP + ) + } } private fun initializeBackupDirectory() { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 18b6ad1f6..72ce47392 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -642,4 +642,5 @@ File %1$s contained %2$s.\n\n Last backup: %s never Device settings + Account diff --git a/app/src/main/res/xml/preferences_backups.xml b/app/src/main/res/xml/preferences_backups.xml index 371f61456..5f44d309b 100644 --- a/app/src/main/res/xml/preferences_backups.xml +++ b/app/src/main/res/xml/preferences_backups.xml @@ -6,10 +6,6 @@ android:key="@string/p_backup_dir" android:title="@string/backup_directory" /> - - @@ -18,6 +14,20 @@ android:key="@string/backup_BAc_export" android:title="@string/backup_BAc_export" /> + + + + + + + +