diff --git a/data/src/androidTest/java/org/cryptomator/data/db/UpgradeDatabaseTest.kt b/data/src/androidTest/java/org/cryptomator/data/db/UpgradeDatabaseTest.kt index 6de195db3..1e0ad4c0a 100644 --- a/data/src/androidTest/java/org/cryptomator/data/db/UpgradeDatabaseTest.kt +++ b/data/src/androidTest/java/org/cryptomator/data/db/UpgradeDatabaseTest.kt @@ -50,7 +50,7 @@ class UpgradeDatabaseTest { Upgrade6To7().applyTo(db, 6) Upgrade7To8().applyTo(db, 7) Upgrade8To9(sharedPreferencesHandler).applyTo(db, 8) - Upgrade9To10().applyTo(db, 9) + Upgrade9To10(sharedPreferencesHandler).applyTo(db, 9) CloudEntityDao(DaoConfig(db, CloudEntityDao::class.java)).loadAll() VaultEntityDao(DaoConfig(db, VaultEntityDao::class.java)).loadAll() @@ -446,7 +446,7 @@ class UpgradeDatabaseTest { Sql.insertInto("VAULT_ENTITY") // .integer("_id", 26) // .integer("FOLDER_CLOUD_ID", 4) // - .text("FOLDER_PATH", "path") // + .text("FOLDER_PATH", "pathOfVault26") // .text("FOLDER_NAME", "name") // .text("CLOUD_TYPE", CloudType.LOCAL.name) // .text("PASSWORD", "password") // @@ -457,18 +457,17 @@ class UpgradeDatabaseTest { Assert.assertThat(it.count, CoreMatchers.`is`(5)) } - Upgrade9To10().applyTo(db, 9) + Upgrade9To10(sharedPreferencesHandler).applyTo(db, 9) Sql.query("VAULT_ENTITY").executeOn(db).use { - it.moveToFirst() - Assert.assertThat(it.getString(it.getColumnIndex("FOLDER_CLOUD_ID")), CoreMatchers.`is`("15")) - it.moveToNext() - Assert.assertThat(it.getString(it.getColumnIndex("FOLDER_CLOUD_ID")), CoreMatchers.nullValue()) + Assert.assertThat(it.count, CoreMatchers.`is`(1)) } Sql.query("CLOUD_ENTITY").executeOn(db).use { Assert.assertThat(it.count, CoreMatchers.`is`(4)) } + + Assert.assertThat(sharedPreferencesHandler.vaultsRemovedDuringMigration(), CoreMatchers.`is`(Pair("LOCAL", arrayListOf("pathOfVault26")))) } } diff --git a/data/src/main/java/org/cryptomator/data/db/Upgrade9To10.kt b/data/src/main/java/org/cryptomator/data/db/Upgrade9To10.kt index 1ea5269a5..01cf79fa5 100644 --- a/data/src/main/java/org/cryptomator/data/db/Upgrade9To10.kt +++ b/data/src/main/java/org/cryptomator/data/db/Upgrade9To10.kt @@ -1,23 +1,42 @@ package org.cryptomator.data.db +import org.cryptomator.domain.CloudType +import org.cryptomator.util.SharedPreferencesHandler import org.greenrobot.greendao.database.Database import javax.inject.Inject import javax.inject.Singleton +import timber.log.Timber @Singleton -internal class Upgrade9To10 @Inject constructor() : DatabaseUpgrade(9, 10) { +internal class Upgrade9To10 @Inject constructor(private val sharedPreferencesHandler: SharedPreferencesHandler) : DatabaseUpgrade(9, 10) { + + private val defaultLocalStorageCloudId = 4L override fun internalApplyTo(db: Database, origin: Int) { db.beginTransaction() try { - Sql.update("VAULT_ENTITY") - .set("FOLDER_CLOUD_ID", Sql.toString(null)) - .where("FOLDER_CLOUD_ID", Sql.eq(4)) + Sql.query("VAULT_ENTITY") + .columns(listOf("FOLDER_PATH")) + .where("FOLDER_CLOUD_ID", Sql.eq(defaultLocalStorageCloudId)) + .executeOn(db).use { + val vaultsToBeRemoved = ArrayList() + while (it.moveToNext()) { + val folderPath = it.getString(it.getColumnIndex("FOLDER_PATH")) + vaultsToBeRemoved.add(folderPath) + } + if (vaultsToBeRemoved.isNotEmpty()) { + sharedPreferencesHandler.vaultsRemovedDuringMigration(Pair(CloudType.LOCAL.name, vaultsToBeRemoved)) + Timber.tag("Upgrade9To10").i("Added %s to the removeDuringMigrations", vaultsToBeRemoved) + } + } + + Sql.deleteFrom("VAULT_ENTITY") + .where("FOLDER_CLOUD_ID", Sql.eq(defaultLocalStorageCloudId)) .executeOn(db) Sql.deleteFrom("CLOUD_ENTITY") - .where("_id", Sql.eq(4)) + .where("_id", Sql.eq(defaultLocalStorageCloudId)) .where("TYPE", Sql.eq("LOCAL")) .executeOn(db) diff --git a/presentation/src/main/java/org/cryptomator/presentation/presenter/VaultListPresenter.kt b/presentation/src/main/java/org/cryptomator/presentation/presenter/VaultListPresenter.kt index c3dc5d5e4..b5e6d288d 100644 --- a/presentation/src/main/java/org/cryptomator/presentation/presenter/VaultListPresenter.kt +++ b/presentation/src/main/java/org/cryptomator/presentation/presenter/VaultListPresenter.kt @@ -12,6 +12,7 @@ import org.cryptomator.data.cloud.crypto.CryptoCloud import org.cryptomator.data.util.NetworkConnectionCheck import org.cryptomator.domain.Cloud import org.cryptomator.domain.CloudFolder +import org.cryptomator.domain.CloudType import org.cryptomator.domain.Vault import org.cryptomator.domain.di.PerView import org.cryptomator.domain.exception.license.LicenseNotValidException @@ -49,6 +50,7 @@ import org.cryptomator.presentation.ui.dialog.AskForLockScreenDialog import org.cryptomator.presentation.ui.dialog.EnterPasswordDialog import org.cryptomator.presentation.ui.dialog.UpdateAppAvailableDialog import org.cryptomator.presentation.ui.dialog.UpdateAppDialog +import org.cryptomator.presentation.ui.dialog.VaultsRemovedDuringMigrationDialog import org.cryptomator.presentation.util.FileUtil import org.cryptomator.presentation.workflow.ActivityResult import org.cryptomator.presentation.workflow.AddExistingVaultWorkflow @@ -103,6 +105,12 @@ class VaultListPresenter @Inject constructor( // sharedPreferencesHandler.setScreenLockDialogAlreadyShown() } + sharedPreferencesHandler.vaultsRemovedDuringMigration()?.let { + val cloudNameString = getString(CloudTypeModel.valueOf(CloudType.valueOf(it.first)).displayNameResource) + view?.showDialog(VaultsRemovedDuringMigrationDialog.newInstance(Pair(cloudNameString, it.second))) + sharedPreferencesHandler.vaultsRemovedDuringMigration(null) + } + checkLicense() } @@ -118,9 +126,10 @@ class VaultListPresenter @Inject constructor( // } override fun onError(e: Throwable) { - var license: String? = "" - if (e is LicenseNotValidException) { - license = e.license + val license = if (e is LicenseNotValidException) { + e.license + } else { + "" } val intent = Intent(context(), LicenseCheckActivity::class.java) intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK diff --git a/presentation/src/main/java/org/cryptomator/presentation/ui/dialog/AppIsObscuredInfoDialog.kt b/presentation/src/main/java/org/cryptomator/presentation/ui/dialog/AppIsObscuredInfoDialog.kt index ccf63d031..e01fde74c 100644 --- a/presentation/src/main/java/org/cryptomator/presentation/ui/dialog/AppIsObscuredInfoDialog.kt +++ b/presentation/src/main/java/org/cryptomator/presentation/ui/dialog/AppIsObscuredInfoDialog.kt @@ -13,10 +13,10 @@ import kotlinx.android.synthetic.main.dialog_app_is_obscured_info.tv_app_is_obsc class AppIsObscuredInfoDialog : BaseDialog() { public override fun setupDialog(builder: AlertDialog.Builder): android.app.Dialog { - builder // + return builder // .setTitle(R.string.dialog_app_is_obscured_info_title) // - .setNeutralButton(R.string.dialog_app_is_obscured_info_neutral_button) { dialog: DialogInterface, _: Int -> dialog.dismiss() } - return builder.create() + .setNeutralButton(R.string.dialog_app_is_obscured_info_neutral_button) { dialog: DialogInterface, _: Int -> dialog.dismiss() } // + .create() } override fun disableDialogWhenObscured(): Boolean { diff --git a/presentation/src/main/java/org/cryptomator/presentation/ui/dialog/VaultsRemovedDuringMigrationDialog.kt b/presentation/src/main/java/org/cryptomator/presentation/ui/dialog/VaultsRemovedDuringMigrationDialog.kt new file mode 100644 index 000000000..0d8de1892 --- /dev/null +++ b/presentation/src/main/java/org/cryptomator/presentation/ui/dialog/VaultsRemovedDuringMigrationDialog.kt @@ -0,0 +1,47 @@ +package org.cryptomator.presentation.ui.dialog + +import android.app.Activity +import android.content.DialogInterface +import android.os.Bundle +import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.DialogFragment +import org.cryptomator.generator.Dialog +import org.cryptomator.presentation.R +import kotlinx.android.synthetic.main.dialog_vaults_removed_during_migration.tv_message + +@Dialog(R.layout.dialog_vaults_removed_during_migration) +class VaultsRemovedDuringMigrationDialog : BaseDialog() { + + public override fun setupDialog(builder: AlertDialog.Builder): android.app.Dialog { + val vaultsRemovedDuringMigration = requireArguments().getSerializable(VAULTS_REMOVED_ARG) as Pair> + + return builder // + .setTitle(String.format(getString(R.string.dialog_vaults_removed_during_migration_title), vaultsRemovedDuringMigration.first)) // + .setNeutralButton(R.string.dialog_vaults_removed_during_migration_neutral_button) { dialog: DialogInterface, _: Int -> dialog.dismiss() } + .create() + } + + public override fun setupView() { + val vaultsRemovedDuringMigration = requireArguments().getSerializable(VAULTS_REMOVED_ARG) as Pair> + + val vaultsRemovedDuringMigrationString = vaultsRemovedDuringMigration + .second + .map { path -> "* $path" } + .reduce { acc, s -> "$acc\n$s" } + + tv_message.text = String.format(getString(R.string.dialog_vaults_removed_during_migration_hint), vaultsRemovedDuringMigrationString) + } + + companion object { + + private const val VAULTS_REMOVED_ARG = "vaultsRemovedArg" + + fun newInstance(vaultsRemovedDuringMigration: Pair>): DialogFragment { + val args = Bundle() + args.putSerializable(VAULTS_REMOVED_ARG, vaultsRemovedDuringMigration) + val fragment = VaultsRemovedDuringMigrationDialog() + fragment.arguments = args + return fragment + } + } +} diff --git a/presentation/src/main/res/layout/dialog_vaults_removed_during_migration.xml b/presentation/src/main/res/layout/dialog_vaults_removed_during_migration.xml new file mode 100644 index 000000000..db8ac7208 --- /dev/null +++ b/presentation/src/main/res/layout/dialog_vaults_removed_during_migration.xml @@ -0,0 +1,19 @@ + + + + + + + + + diff --git a/presentation/src/main/res/values/strings.xml b/presentation/src/main/res/values/strings.xml index d9312db43..9acb5d10b 100644 --- a/presentation/src/main/res/values/strings.xml +++ b/presentation/src/main/res/values/strings.xml @@ -166,7 +166,6 @@ @string/screen_vault_list_vault_action_delete Click here to add locations Server doesn\'t seem to be WebDAV compatible - Custom locations No additional locations available. @@ -416,6 +415,12 @@ Another app is displaying something on top of Cryptomator (e.g., a blue light filter or night mode app). For security reasons, Cryptomator is disabled.\n\nHow to enable Cryptomator Close + + Please re-add vaults for %1s cloud + While migrating to this app version we need to remove the following vaults from the app:\n%2s \n\nThose vaults aren\'t removed from the cloud but only from this app. Sorry for the inconvenience and please re-add these vaults to continue working with them. + @string/dialog_unable_to_share_positive_button + + This setting is a security feature and prevents other apps from tricking users into doing things they do not wan\'t to do.\n\nBy disabling, you confirm that you are aware of the risks. Are you sure you want to remove this cloud connection? diff --git a/util/src/main/java/org/cryptomator/util/SharedPreferencesHandler.kt b/util/src/main/java/org/cryptomator/util/SharedPreferencesHandler.kt index eedee837d..ac60155f9 100644 --- a/util/src/main/java/org/cryptomator/util/SharedPreferencesHandler.kt +++ b/util/src/main/java/org/cryptomator/util/SharedPreferencesHandler.kt @@ -236,6 +236,31 @@ constructor(context: Context) : SharedPreferences.OnSharedPreferenceChangeListen return defaultSharedPreferences.getBoolean(BACKGROUND_UNLOCK_PREPARATION, true) } + fun vaultsRemovedDuringMigration(vaultsToBeRemoved: Pair>?) { + vaultsToBeRemoved?.let { + val vaultsToBeRemovedString = if (it.second.isNotEmpty()) { + it.second.reduce { acc, s -> "$acc,$s" } + } else { + "" + } + defaultSharedPreferences.setValue(VAULTS_REMOVED_DURING_MIGRATION_TYPE, it.first) + defaultSharedPreferences.setValue(VAULTS_REMOVED_DURING_MIGRATION, vaultsToBeRemovedString) + } ?: run { + defaultSharedPreferences.setValue(VAULTS_REMOVED_DURING_MIGRATION_TYPE, null) + defaultSharedPreferences.setValue(VAULTS_REMOVED_DURING_MIGRATION, null) + } + } + + fun vaultsRemovedDuringMigration(): Pair>? { + val vaultsRemovedDuringMigrationType = defaultSharedPreferences.getString(VAULTS_REMOVED_DURING_MIGRATION_TYPE, null) + val vaultsRemovedDuringMigration = defaultSharedPreferences.getString(VAULTS_REMOVED_DURING_MIGRATION, null) + return if(vaultsRemovedDuringMigrationType != null && vaultsRemovedDuringMigration != null) { + Pair(vaultsRemovedDuringMigrationType, ArrayList(vaultsRemovedDuringMigration.split(','))) + } else { + null + } + } + companion object { private const val SCREEN_LOCK_DIALOG_SHOWN = "askForScreenLockDialogShown" @@ -248,6 +273,8 @@ constructor(context: Context) : SharedPreferences.OnSharedPreferenceChangeListen private const val GLOB_SEARCH = "globSearch" private const val KEEP_UNLOCKED_WHILE_EDITING = "keepUnlockedWhileEditing" private const val BACKGROUND_UNLOCK_PREPARATION = "backgroundUnlockPreparation" + private const val VAULTS_REMOVED_DURING_MIGRATION = "vaultsRemovedDuringMigration" + private const val VAULTS_REMOVED_DURING_MIGRATION_TYPE = "vaultsRemovedDuringMigrationType" const val DEBUG_MODE = "debugMode" const val DISABLE_APP_WHEN_OBSCURED = "disableAppWhenObscured" const val SECURE_SCREEN = "secureScreen"