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 6de195db..1e0ad4c0 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 1ea5269a..01cf79fa 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 c3dc5d5e..b5e6d288 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 ccf63d03..e01fde74 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 00000000..0d8de189 --- /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 00000000..db8ac720 --- /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 d9312db4..9acb5d10 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 eedee837..ac60155f 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"