Refactor unlock and cloud authentication from UseCase to Activity
This commit is contained in:
parent
c8e9616894
commit
36fd5b2a8a
@ -90,11 +90,14 @@ public class CryptoCloudFactory {
|
|||||||
throw new CancellationException();
|
throw new CancellationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
cryptoCloudContentRepositoryFactory.registerCryptor(impl.getVault(), cryptor);
|
Vault vault = aCopyOf(token.getVault()) //
|
||||||
|
|
||||||
return aCopyOf(token.getVault()) //
|
|
||||||
.withVersion(impl.getKeyFile().getVersion()) //
|
.withVersion(impl.getKeyFile().getVersion()) //
|
||||||
|
.withUnlocked(true) //
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
cryptoCloudContentRepositoryFactory.registerCryptor(vault, cryptor);
|
||||||
|
|
||||||
|
return vault;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UnlockTokenImpl createUnlockToken(Vault vault) throws BackendException {
|
public UnlockTokenImpl createUnlockToken(Vault vault) throws BackendException {
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
package org.cryptomator.domain.repository;
|
|
||||||
|
|
||||||
import org.cryptomator.domain.Cloud;
|
|
||||||
import org.cryptomator.domain.exception.BackendException;
|
|
||||||
|
|
||||||
public interface CloudAuthenticationService {
|
|
||||||
|
|
||||||
boolean isAuthenticated(Cloud cloud) throws BackendException;
|
|
||||||
|
|
||||||
boolean canAuthenticate(Cloud cloud);
|
|
||||||
|
|
||||||
Cloud updateAuthenticatedCloud(Cloud cloud);
|
|
||||||
}
|
|
@ -97,10 +97,11 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<activity android:name=".ui.activity.UnlockVaultActivity"
|
||||||
|
android:theme="@style/TransparentAlertDialogCustom"
|
||||||
|
android:label=""/>
|
||||||
<activity android:name=".ui.activity.EmptyDirIdFileInfoActivity" />
|
<activity android:name=".ui.activity.EmptyDirIdFileInfoActivity" />
|
||||||
|
|
||||||
|
|
||||||
<!-- Settings -->
|
<!-- Settings -->
|
||||||
<activity android:name=".ui.activity.BiometricAuthSettingsActivity" />
|
<activity android:name=".ui.activity.BiometricAuthSettingsActivity" />
|
||||||
<activity android:name=".ui.activity.CloudConnectionListActivity" />
|
<activity android:name=".ui.activity.CloudConnectionListActivity" />
|
||||||
|
@ -21,6 +21,7 @@ import org.cryptomator.presentation.ui.activity.SettingsActivity;
|
|||||||
import org.cryptomator.presentation.ui.activity.SharedFilesActivity;
|
import org.cryptomator.presentation.ui.activity.SharedFilesActivity;
|
||||||
import org.cryptomator.presentation.ui.activity.SplashActivity;
|
import org.cryptomator.presentation.ui.activity.SplashActivity;
|
||||||
import org.cryptomator.presentation.ui.activity.TextEditorActivity;
|
import org.cryptomator.presentation.ui.activity.TextEditorActivity;
|
||||||
|
import org.cryptomator.presentation.ui.activity.UnlockVaultActivity;
|
||||||
import org.cryptomator.presentation.ui.activity.VaultListActivity;
|
import org.cryptomator.presentation.ui.activity.VaultListActivity;
|
||||||
import org.cryptomator.presentation.ui.activity.WebDavAddOrChangeActivity;
|
import org.cryptomator.presentation.ui.activity.WebDavAddOrChangeActivity;
|
||||||
import org.cryptomator.presentation.ui.fragment.AutoUploadChooseVaultFragment;
|
import org.cryptomator.presentation.ui.fragment.AutoUploadChooseVaultFragment;
|
||||||
@ -34,6 +35,7 @@ import org.cryptomator.presentation.ui.fragment.ImagePreviewFragment;
|
|||||||
import org.cryptomator.presentation.ui.fragment.SetPasswordFragment;
|
import org.cryptomator.presentation.ui.fragment.SetPasswordFragment;
|
||||||
import org.cryptomator.presentation.ui.fragment.SharedFilesFragment;
|
import org.cryptomator.presentation.ui.fragment.SharedFilesFragment;
|
||||||
import org.cryptomator.presentation.ui.fragment.TextEditorFragment;
|
import org.cryptomator.presentation.ui.fragment.TextEditorFragment;
|
||||||
|
import org.cryptomator.presentation.ui.fragment.UnlockVaultFragment;
|
||||||
import org.cryptomator.presentation.ui.fragment.VaultListFragment;
|
import org.cryptomator.presentation.ui.fragment.VaultListFragment;
|
||||||
import org.cryptomator.presentation.ui.fragment.WebDavAddOrChangeFragment;
|
import org.cryptomator.presentation.ui.fragment.WebDavAddOrChangeFragment;
|
||||||
import org.cryptomator.presentation.workflow.AddExistingVaultWorkflow;
|
import org.cryptomator.presentation.workflow.AddExistingVaultWorkflow;
|
||||||
@ -114,4 +116,8 @@ public interface ActivityComponent {
|
|||||||
void inject(AutoUploadChooseVaultFragment autoUploadChooseVaultFragment);
|
void inject(AutoUploadChooseVaultFragment autoUploadChooseVaultFragment);
|
||||||
|
|
||||||
void inject(LicenseCheckActivity licenseCheckActivity);
|
void inject(LicenseCheckActivity licenseCheckActivity);
|
||||||
|
|
||||||
|
void inject(UnlockVaultActivity unlockVaultActivity);
|
||||||
|
|
||||||
|
void inject(UnlockVaultFragment unlockVaultFragment);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
package org.cryptomator.presentation.intent;
|
||||||
|
|
||||||
|
import org.cryptomator.generator.Intent;
|
||||||
|
import org.cryptomator.presentation.model.VaultModel;
|
||||||
|
import org.cryptomator.presentation.ui.activity.UnlockVaultActivity;
|
||||||
|
|
||||||
|
@Intent(UnlockVaultActivity.class)
|
||||||
|
public interface UnlockVaultIntent {
|
||||||
|
|
||||||
|
VaultModel vaultModel();
|
||||||
|
|
||||||
|
VaultAction vaultAction();
|
||||||
|
|
||||||
|
enum VaultAction {
|
||||||
|
UNLOCK,
|
||||||
|
UNLOCK_FOR_BIOMETRIC_AUTH,
|
||||||
|
ENCRYPT_PASSWORD,
|
||||||
|
CHANGE_PASSWORD
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -15,6 +15,8 @@ class VaultModel(private val vault: Vault) : Serializable {
|
|||||||
get() = !vault.isUnlocked
|
get() = !vault.isUnlocked
|
||||||
val position: Int
|
val position: Int
|
||||||
get() = vault.position
|
get() = vault.position
|
||||||
|
val version: Int
|
||||||
|
get() = vault.version
|
||||||
|
|
||||||
fun toVault(): Vault {
|
fun toVault(): Vault {
|
||||||
return vault
|
return vault
|
||||||
|
@ -7,17 +7,14 @@ import org.cryptomator.domain.di.PerView
|
|||||||
import org.cryptomator.domain.usecases.GetDecryptedCloudForVaultUseCase
|
import org.cryptomator.domain.usecases.GetDecryptedCloudForVaultUseCase
|
||||||
import org.cryptomator.domain.usecases.cloud.GetRootFolderUseCase
|
import org.cryptomator.domain.usecases.cloud.GetRootFolderUseCase
|
||||||
import org.cryptomator.domain.usecases.vault.GetVaultListUseCase
|
import org.cryptomator.domain.usecases.vault.GetVaultListUseCase
|
||||||
import org.cryptomator.domain.usecases.vault.RemoveStoredVaultPasswordsUseCase
|
|
||||||
import org.cryptomator.domain.usecases.vault.UnlockVaultUseCase
|
|
||||||
import org.cryptomator.domain.usecases.vault.VaultOrUnlockToken
|
|
||||||
import org.cryptomator.generator.Callback
|
import org.cryptomator.generator.Callback
|
||||||
import org.cryptomator.presentation.R
|
import org.cryptomator.presentation.R
|
||||||
import org.cryptomator.presentation.exception.ExceptionHandlers
|
import org.cryptomator.presentation.exception.ExceptionHandlers
|
||||||
import org.cryptomator.presentation.intent.ChooseCloudNodeSettings
|
import org.cryptomator.presentation.intent.ChooseCloudNodeSettings
|
||||||
import org.cryptomator.presentation.intent.Intents
|
import org.cryptomator.presentation.intent.Intents
|
||||||
|
import org.cryptomator.presentation.intent.UnlockVaultIntent
|
||||||
import org.cryptomator.presentation.model.CloudFolderModel
|
import org.cryptomator.presentation.model.CloudFolderModel
|
||||||
import org.cryptomator.presentation.model.CloudModel
|
import org.cryptomator.presentation.model.CloudModel
|
||||||
import org.cryptomator.presentation.model.ProgressModel
|
|
||||||
import org.cryptomator.presentation.model.VaultModel
|
import org.cryptomator.presentation.model.VaultModel
|
||||||
import org.cryptomator.presentation.model.mappers.CloudFolderModelMapper
|
import org.cryptomator.presentation.model.mappers.CloudFolderModelMapper
|
||||||
import org.cryptomator.presentation.ui.activity.view.AutoUploadChooseVaultView
|
import org.cryptomator.presentation.ui.activity.view.AutoUploadChooseVaultView
|
||||||
@ -32,8 +29,6 @@ class AutoUploadChooseVaultPresenter @Inject constructor( //
|
|||||||
private val getVaultListUseCase: GetVaultListUseCase, //
|
private val getVaultListUseCase: GetVaultListUseCase, //
|
||||||
private val getRootFolderUseCase: GetRootFolderUseCase, //
|
private val getRootFolderUseCase: GetRootFolderUseCase, //
|
||||||
private val getDecryptedCloudForVaultUseCase: GetDecryptedCloudForVaultUseCase, //
|
private val getDecryptedCloudForVaultUseCase: GetDecryptedCloudForVaultUseCase, //
|
||||||
private val unlockVaultUseCase: UnlockVaultUseCase, //
|
|
||||||
private val removeStoredVaultPasswordsUseCase: RemoveStoredVaultPasswordsUseCase, //
|
|
||||||
private val cloudFolderModelMapper: CloudFolderModelMapper, //
|
private val cloudFolderModelMapper: CloudFolderModelMapper, //
|
||||||
private val sharedPreferencesHandler: SharedPreferencesHandler, //
|
private val sharedPreferencesHandler: SharedPreferencesHandler, //
|
||||||
private val authenticationExceptionHandler: AuthenticationExceptionHandler, //
|
private val authenticationExceptionHandler: AuthenticationExceptionHandler, //
|
||||||
@ -83,11 +78,24 @@ class AutoUploadChooseVaultPresenter @Inject constructor( //
|
|||||||
decryptedCloudFor(authenticatedVault)
|
decryptedCloudFor(authenticatedVault)
|
||||||
} else {
|
} else {
|
||||||
if (!isPaused) {
|
if (!isPaused) {
|
||||||
view?.showEnterPasswordDialog(VaultModel(authenticatedVault))
|
requestActivityResult( //
|
||||||
|
ActivityResultCallbacks.vaultUnlockedAutoUpload(), //
|
||||||
|
Intents.unlockVaultIntent().withVaultModel(VaultModel(authenticatedVault)).withVaultAction(UnlockVaultIntent.VaultAction.UNLOCK))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Callback
|
||||||
|
fun vaultUnlockedAutoUpload(result: ActivityResult) {
|
||||||
|
val cloud = result.intent().getSerializableExtra(SINGLE_RESULT) as Cloud
|
||||||
|
when {
|
||||||
|
result.isResultOk -> rootFolderFor(cloud)
|
||||||
|
else -> TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private fun decryptedCloudFor(vault: Vault) {
|
private fun decryptedCloudFor(vault: Vault) {
|
||||||
getDecryptedCloudForVaultUseCase //
|
getDecryptedCloudForVaultUseCase //
|
||||||
.withVault(vault) //
|
.withVault(vault) //
|
||||||
@ -151,49 +159,11 @@ class AutoUploadChooseVaultPresenter @Inject constructor( //
|
|||||||
location?.let { view?.showChosenLocation(it) }
|
location?.let { view?.showChosenLocation(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onUnlockCanceled() {
|
|
||||||
unlockVaultUseCase.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onUnlockPressed(vaultModel: VaultModel, password: String?) {
|
|
||||||
view?.showProgress(ProgressModel.GENERIC)
|
|
||||||
unlockVaultUseCase //
|
|
||||||
.withVaultOrUnlockToken(VaultOrUnlockToken.from(vaultModel.toVault())) //
|
|
||||||
.andPassword(password) //
|
|
||||||
.run(object : DefaultResultHandler<Cloud>() {
|
|
||||||
override fun onSuccess(cloud: Cloud) {
|
|
||||||
view?.showProgress(ProgressModel.COMPLETED)
|
|
||||||
rootFolderFor(cloud)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onError(e: Throwable) {
|
|
||||||
if (!authenticationExceptionHandler.handleAuthenticationException( //
|
|
||||||
this@AutoUploadChooseVaultPresenter, //
|
|
||||||
e, //
|
|
||||||
ActivityResultCallbacks.unlockVaultAfterAuth(vaultModel.toVault(), password))) {
|
|
||||||
showError(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onBiometricKeyInvalidated(vaultModel: VaultModel?) {
|
|
||||||
removeStoredVaultPasswordsUseCase.run(object : DefaultResultHandler<Void?>() {
|
|
||||||
override fun onSuccess(void: Void?) {
|
|
||||||
view?.showBiometricAuthKeyInvalidatedDialog()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fun useConfirmationInFaceUnlockBiometricAuthentication(): Boolean {
|
|
||||||
return sharedPreferencesHandler.useConfirmationInFaceUnlockBiometricAuthentication()
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class AuthenticationState {
|
enum class AuthenticationState {
|
||||||
CHOOSE_LOCATION, INIT_ROOT
|
CHOOSE_LOCATION, INIT_ROOT
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
unsubscribeOnDestroy(getVaultListUseCase)
|
unsubscribeOnDestroy(getVaultListUseCase, getRootFolderUseCase, getDecryptedCloudForVaultUseCase)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,21 +2,21 @@ package org.cryptomator.presentation.presenter
|
|||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import org.cryptomator.cryptolib.api.InvalidPassphraseException
|
|
||||||
import org.cryptomator.domain.Cloud
|
import org.cryptomator.domain.Cloud
|
||||||
import org.cryptomator.domain.Vault
|
import org.cryptomator.domain.Vault
|
||||||
import org.cryptomator.domain.di.PerView
|
import org.cryptomator.domain.di.PerView
|
||||||
import org.cryptomator.domain.usecases.vault.*
|
import org.cryptomator.domain.usecases.vault.GetVaultListUseCase
|
||||||
|
import org.cryptomator.domain.usecases.vault.LockVaultUseCase
|
||||||
|
import org.cryptomator.domain.usecases.vault.SaveVaultUseCase
|
||||||
import org.cryptomator.generator.Callback
|
import org.cryptomator.generator.Callback
|
||||||
import org.cryptomator.presentation.exception.ExceptionHandlers
|
import org.cryptomator.presentation.exception.ExceptionHandlers
|
||||||
import org.cryptomator.presentation.model.CloudModel
|
import org.cryptomator.presentation.intent.Intents
|
||||||
import org.cryptomator.presentation.model.ProgressModel
|
import org.cryptomator.presentation.intent.UnlockVaultIntent
|
||||||
import org.cryptomator.presentation.model.VaultModel
|
import org.cryptomator.presentation.model.VaultModel
|
||||||
import org.cryptomator.presentation.ui.activity.view.BiometricAuthSettingsView
|
import org.cryptomator.presentation.ui.activity.view.BiometricAuthSettingsView
|
||||||
import org.cryptomator.presentation.workflow.ActivityResult
|
import org.cryptomator.presentation.workflow.ActivityResult
|
||||||
import org.cryptomator.presentation.workflow.AuthenticationExceptionHandler
|
|
||||||
import org.cryptomator.util.SharedPreferencesHandler
|
import org.cryptomator.util.SharedPreferencesHandler
|
||||||
import java.util.*
|
import java.util.ArrayList
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
@ -24,13 +24,9 @@ import timber.log.Timber
|
|||||||
class BiometricAuthSettingsPresenter @Inject constructor( //
|
class BiometricAuthSettingsPresenter @Inject constructor( //
|
||||||
private val getVaultListUseCase: GetVaultListUseCase, //
|
private val getVaultListUseCase: GetVaultListUseCase, //
|
||||||
private val saveVaultUseCase: SaveVaultUseCase, //
|
private val saveVaultUseCase: SaveVaultUseCase, //
|
||||||
private val removeStoredVaultPasswordsUseCase: RemoveStoredVaultPasswordsUseCase, //
|
|
||||||
private val checkVaultPasswordUseCase: CheckVaultPasswordUseCase, //
|
|
||||||
private val unlockVaultUseCase: UnlockVaultUseCase, //
|
|
||||||
private val lockVaultUseCase: LockVaultUseCase, //
|
private val lockVaultUseCase: LockVaultUseCase, //
|
||||||
exceptionMappings: ExceptionHandlers, //
|
exceptionMappings: ExceptionHandlers, //
|
||||||
private val sharedPreferencesHandler: SharedPreferencesHandler, //
|
private val sharedPreferencesHandler: SharedPreferencesHandler) : Presenter<BiometricAuthSettingsView>(exceptionMappings) {
|
||||||
private val authenticationExceptionHandler: AuthenticationExceptionHandler) : Presenter<BiometricAuthSettingsView>(exceptionMappings) {
|
|
||||||
|
|
||||||
fun loadVaultList() {
|
fun loadVaultList() {
|
||||||
updateVaultListView()
|
updateVaultListView()
|
||||||
@ -49,92 +45,56 @@ class BiometricAuthSettingsPresenter @Inject constructor( //
|
|||||||
|
|
||||||
fun updateVaultEntityWithChangedBiometricAuthSettings(vaultModel: VaultModel, useBiometricAuth: Boolean) {
|
fun updateVaultEntityWithChangedBiometricAuthSettings(vaultModel: VaultModel, useBiometricAuth: Boolean) {
|
||||||
if (useBiometricAuth) {
|
if (useBiometricAuth) {
|
||||||
view?.showEnterPasswordDialog(VaultModel(vaultModel.toVault()))
|
verifyPassword(vaultModel)
|
||||||
} else {
|
} else {
|
||||||
removePasswordAndSave(vaultModel.toVault())
|
removePasswordAndSave(vaultModel.toVault())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun verifyPassword(vaultModel: VaultModel) {
|
private fun verifyPassword(vaultModel: VaultModel) {
|
||||||
Timber.tag("BiomtricAuthSettngsPres").i("Checking entered vault password")
|
Timber.tag("BiomtricAuthSettngsPres").i("Checking entered vault password")
|
||||||
if (vaultModel.isLocked) {
|
if (vaultModel.isLocked) {
|
||||||
unlockVault(vaultModel)
|
requestActivityResult( //
|
||||||
|
ActivityResultCallbacks.vaultUnlockedBiometricAuthPres(vaultModel), //
|
||||||
|
Intents.unlockVaultIntent().withVaultModel(vaultModel).withVaultAction(UnlockVaultIntent.VaultAction.UNLOCK_FOR_BIOMETRIC_AUTH))
|
||||||
} else {
|
} else {
|
||||||
checkPassword(vaultModel)
|
lockVaultUseCase
|
||||||
|
.withVault(vaultModel.toVault())
|
||||||
|
.run(object : DefaultResultHandler<Vault>() {
|
||||||
|
override fun onSuccess(vault: Vault) {
|
||||||
|
super.onSuccess(vault)
|
||||||
|
requestActivityResult( //
|
||||||
|
ActivityResultCallbacks.vaultUnlockedBiometricAuthPres(vaultModel), //
|
||||||
|
Intents.unlockVaultIntent().withVaultModel(vaultModel).withVaultAction(UnlockVaultIntent.VaultAction.UNLOCK_FOR_BIOMETRIC_AUTH))
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkPassword(vaultModel: VaultModel) {
|
@Callback
|
||||||
view?.showProgress(ProgressModel.GENERIC)
|
fun vaultUnlockedBiometricAuthPres(result: ActivityResult, vaultModel: VaultModel) {
|
||||||
checkVaultPasswordUseCase //
|
val cloud = result.intent().getSerializableExtra(SINGLE_RESULT) as Cloud
|
||||||
.withVault(vaultModel.toVault()) //
|
val password = result.intent().getStringExtra(UnlockVaultPresenter.PASSWORD)
|
||||||
.andPassword(vaultModel.password) //
|
val vault = Vault.aCopyOf(vaultModel.toVault()).withCloud(cloud).withSavedPassword(password).build()
|
||||||
.run(object : DefaultResultHandler<Boolean>() {
|
when {
|
||||||
override fun onSuccess(passwordCorrect: Boolean) {
|
result.isResultOk -> requestActivityResult( //
|
||||||
if (passwordCorrect) {
|
ActivityResultCallbacks.encryptVaultPassword(vaultModel), //
|
||||||
Timber.tag("BiomtricAuthSettngsPres").i("Password is correct")
|
Intents.unlockVaultIntent().withVaultModel(VaultModel(vault)).withVaultAction(UnlockVaultIntent.VaultAction.ENCRYPT_PASSWORD))
|
||||||
onPasswordCheckSucceeded(vaultModel)
|
else -> TODO("Not yet implemented")
|
||||||
} else {
|
}
|
||||||
Timber.tag("BiomtricAuthSettngsPres").i("Password is wrong")
|
|
||||||
showError(InvalidPassphraseException())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onError(e: Throwable) {
|
|
||||||
super.onError(e)
|
|
||||||
Timber.tag("BiomtricAuthSettngsPres").e(e, "Password check failed")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun unlockVault(vaultModel: VaultModel) {
|
|
||||||
view?.showProgress(ProgressModel.GENERIC)
|
|
||||||
unlockVaultUseCase //
|
|
||||||
.withVaultOrUnlockToken(VaultOrUnlockToken.from(vaultModel.toVault())) //
|
|
||||||
.andPassword(vaultModel.password) //
|
|
||||||
.run(object : DefaultResultHandler<Cloud>() {
|
|
||||||
override fun onSuccess(cloud: Cloud) {
|
|
||||||
Timber.tag("BiomtricAuthSettngsPres").i("Password is correct")
|
|
||||||
onUnlockSucceeded(vaultModel)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onError(e: Throwable) {
|
|
||||||
if (!authenticationExceptionHandler.handleAuthenticationException(this@BiometricAuthSettingsPresenter, e, ActivityResultCallbacks.unlockVaultAfterAuth(vaultModel.toVault()))) {
|
|
||||||
showError(e)
|
|
||||||
Timber.tag("BiomtricAuthSettngsPres").e(e, "Password check failed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onUnlockSucceeded(vaultModel: VaultModel) {
|
|
||||||
lockVaultUseCase
|
|
||||||
.withVault(vaultModel.toVault())
|
|
||||||
.run(object : DefaultResultHandler<Vault>() {
|
|
||||||
override fun onSuccess(vault: Vault) {
|
|
||||||
super.onSuccess(vault)
|
|
||||||
onPasswordCheckSucceeded(vaultModel)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onError(e: Throwable) {
|
|
||||||
Timber.tag("BiomtricAuthSettngsPres").e(e, "Locking vault after unlocking failed but continue to save changes")
|
|
||||||
onPasswordCheckSucceeded(vaultModel)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Callback
|
@Callback
|
||||||
fun unlockVaultAfterAuth(result: ActivityResult, vault: Vault?) {
|
fun encryptVaultPassword(result: ActivityResult, vaultModel: VaultModel) {
|
||||||
val cloud = result.getSingleResult(CloudModel::class.java).toCloud()
|
val tmpVault = result.intent().getSerializableExtra(SINGLE_RESULT) as VaultModel
|
||||||
val vaultWithUpdatedCloud = Vault.aCopyOf(vault).withCloud(cloud).build()
|
val vault = Vault.aCopyOf(vaultModel.toVault()).withSavedPassword(tmpVault.password).build()
|
||||||
unlockVault(VaultModel(vaultWithUpdatedCloud))
|
when {
|
||||||
|
result.isResultOk -> saveVault(vault)
|
||||||
|
else -> TODO("Not yet implemented")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onPasswordCheckSucceeded(vaultModel: VaultModel) {
|
private fun saveVault(vault: Vault?) {
|
||||||
view?.showBiometricAuthenticationDialog(vaultModel)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun saveVault(vault: Vault?) {
|
|
||||||
saveVaultUseCase //
|
saveVaultUseCase //
|
||||||
.withVault(vault) //
|
.withVault(vault) //
|
||||||
.run(object : ProgressCompletingResultHandler<Vault>() {
|
.run(object : ProgressCompletingResultHandler<Vault>() {
|
||||||
@ -145,8 +105,7 @@ class BiometricAuthSettingsPresenter @Inject constructor( //
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun switchedGeneralBiometricAuthSettings(isChecked: Boolean) {
|
fun switchedGeneralBiometricAuthSettings(isChecked: Boolean) {
|
||||||
sharedPreferencesHandler //
|
sharedPreferencesHandler.changeUseBiometricAuthentication(isChecked)
|
||||||
.changeUseBiometricAuthentication(isChecked)
|
|
||||||
if (isChecked) {
|
if (isChecked) {
|
||||||
loadVaultList()
|
loadVaultList()
|
||||||
} else {
|
} else {
|
||||||
@ -173,32 +132,15 @@ class BiometricAuthSettingsPresenter @Inject constructor( //
|
|||||||
|
|
||||||
fun onSetupBiometricAuthInSystemClicked() {
|
fun onSetupBiometricAuthInSystemClicked() {
|
||||||
val openSecuritySettings = Intent(Settings.ACTION_SECURITY_SETTINGS)
|
val openSecuritySettings = Intent(Settings.ACTION_SECURITY_SETTINGS)
|
||||||
requestActivityResult(ActivityResultCallbacks.onSetupFingerCompleted(), openSecuritySettings)
|
requestActivityResult(ActivityResultCallbacks.onSetupBiometricAuthInSystemCompleted(), openSecuritySettings)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Callback
|
@Callback
|
||||||
fun onSetupFingerCompleted(result: ActivityResult?) {
|
fun onSetupBiometricAuthInSystemCompleted(result: ActivityResult?) {
|
||||||
view?.showSetupBiometricAuthDialog()
|
view?.showSetupBiometricAuthDialog()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onBiometricAuthKeyInvalidated(vaultModel: VaultModel?) {
|
|
||||||
removeStoredVaultPasswordsUseCase.run(object : DefaultResultHandler<Void?>() {
|
|
||||||
override fun onSuccess(void: Void?) {
|
|
||||||
view?.showBiometricAuthKeyInvalidatedDialog()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onUnlockCanceled() {
|
|
||||||
unlockVaultUseCase.cancel()
|
|
||||||
loadVaultList()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun useConfirmationInFaceUnlockBiometricAuthentication(): Boolean {
|
|
||||||
return sharedPreferencesHandler.useConfirmationInFaceUnlockBiometricAuthentication()
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
unsubscribeOnDestroy(getVaultListUseCase, saveVaultUseCase, checkVaultPasswordUseCase, removeStoredVaultPasswordsUseCase, unlockVaultUseCase)
|
unsubscribeOnDestroy(getVaultListUseCase, saveVaultUseCase, lockVaultUseCase)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,15 +8,13 @@ import org.cryptomator.domain.di.PerView
|
|||||||
import org.cryptomator.domain.usecases.GetDecryptedCloudForVaultUseCase
|
import org.cryptomator.domain.usecases.GetDecryptedCloudForVaultUseCase
|
||||||
import org.cryptomator.domain.usecases.cloud.*
|
import org.cryptomator.domain.usecases.cloud.*
|
||||||
import org.cryptomator.domain.usecases.vault.GetVaultListUseCase
|
import org.cryptomator.domain.usecases.vault.GetVaultListUseCase
|
||||||
import org.cryptomator.domain.usecases.vault.RemoveStoredVaultPasswordsUseCase
|
|
||||||
import org.cryptomator.domain.usecases.vault.UnlockVaultUseCase
|
|
||||||
import org.cryptomator.domain.usecases.vault.VaultOrUnlockToken
|
|
||||||
import org.cryptomator.generator.Callback
|
import org.cryptomator.generator.Callback
|
||||||
import org.cryptomator.generator.InstanceState
|
import org.cryptomator.generator.InstanceState
|
||||||
import org.cryptomator.presentation.R
|
import org.cryptomator.presentation.R
|
||||||
import org.cryptomator.presentation.exception.ExceptionHandlers
|
import org.cryptomator.presentation.exception.ExceptionHandlers
|
||||||
import org.cryptomator.presentation.intent.ChooseCloudNodeSettings
|
import org.cryptomator.presentation.intent.ChooseCloudNodeSettings
|
||||||
import org.cryptomator.presentation.intent.Intents
|
import org.cryptomator.presentation.intent.Intents
|
||||||
|
import org.cryptomator.presentation.intent.UnlockVaultIntent
|
||||||
import org.cryptomator.presentation.model.*
|
import org.cryptomator.presentation.model.*
|
||||||
import org.cryptomator.presentation.model.mappers.CloudFolderModelMapper
|
import org.cryptomator.presentation.model.mappers.CloudFolderModelMapper
|
||||||
import org.cryptomator.presentation.model.mappers.ProgressModelMapper
|
import org.cryptomator.presentation.model.mappers.ProgressModelMapper
|
||||||
@ -27,7 +25,6 @@ import org.cryptomator.presentation.workflow.ActivityResult
|
|||||||
import org.cryptomator.presentation.workflow.AuthenticationExceptionHandler
|
import org.cryptomator.presentation.workflow.AuthenticationExceptionHandler
|
||||||
import org.cryptomator.presentation.workflow.PermissionsResult
|
import org.cryptomator.presentation.workflow.PermissionsResult
|
||||||
import org.cryptomator.util.Optional
|
import org.cryptomator.util.Optional
|
||||||
import org.cryptomator.util.SharedPreferencesHandler
|
|
||||||
import org.cryptomator.util.file.FileCacheUtils
|
import org.cryptomator.util.file.FileCacheUtils
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -36,14 +33,11 @@ import timber.log.Timber
|
|||||||
@PerView
|
@PerView
|
||||||
class SharedFilesPresenter @Inject constructor( //
|
class SharedFilesPresenter @Inject constructor( //
|
||||||
private val getVaultListUseCase: GetVaultListUseCase, //
|
private val getVaultListUseCase: GetVaultListUseCase, //
|
||||||
private val unlockVaultUseCase: UnlockVaultUseCase, //
|
|
||||||
private val getRootFolderUseCase: GetRootFolderUseCase, //
|
private val getRootFolderUseCase: GetRootFolderUseCase, //
|
||||||
private val getDecryptedCloudForVaultUseCase: GetDecryptedCloudForVaultUseCase, //
|
private val getDecryptedCloudForVaultUseCase: GetDecryptedCloudForVaultUseCase, //
|
||||||
private val uploadFilesUseCase: UploadFilesUseCase, //
|
private val uploadFilesUseCase: UploadFilesUseCase, //
|
||||||
private val getCloudListUseCase: GetCloudListUseCase, //
|
private val getCloudListUseCase: GetCloudListUseCase, //
|
||||||
private val removeStoredVaultPasswordsUseCase: RemoveStoredVaultPasswordsUseCase, //
|
|
||||||
private val contentResolverUtil: ContentResolverUtil, //
|
private val contentResolverUtil: ContentResolverUtil, //
|
||||||
private val sharedPreferencesHandler: SharedPreferencesHandler, //
|
|
||||||
private val fileCacheUtils: FileCacheUtils, //
|
private val fileCacheUtils: FileCacheUtils, //
|
||||||
private val authenticationExceptionHandler: AuthenticationExceptionHandler, //
|
private val authenticationExceptionHandler: AuthenticationExceptionHandler, //
|
||||||
private val cloudFolderModelMapper: CloudFolderModelMapper, //
|
private val cloudFolderModelMapper: CloudFolderModelMapper, //
|
||||||
@ -128,6 +122,28 @@ class SharedFilesPresenter @Inject constructor( //
|
|||||||
vaultModel?.let { onCloudOfVaultAuthenticated(it.toVault()) }
|
vaultModel?.let { onCloudOfVaultAuthenticated(it.toVault()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun onCloudOfVaultAuthenticated(authenticatedVault: Vault) {
|
||||||
|
if (authenticatedVault.isUnlocked) {
|
||||||
|
decryptedCloudFor(authenticatedVault)
|
||||||
|
} else {
|
||||||
|
if (!isPaused) {
|
||||||
|
requestActivityResult( //
|
||||||
|
ActivityResultCallbacks.vaultUnlockedSharedFiles(), //
|
||||||
|
Intents.unlockVaultIntent().withVaultModel(VaultModel(authenticatedVault)).withVaultAction(UnlockVaultIntent.VaultAction.UNLOCK))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Callback
|
||||||
|
fun vaultUnlockedSharedFiles(result: ActivityResult) {
|
||||||
|
val cloud = result.intent().getSerializableExtra(SINGLE_RESULT) as Cloud
|
||||||
|
when {
|
||||||
|
result.isResultOk -> rootFolderFor(cloud)
|
||||||
|
else -> TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun decryptedCloudFor(vault: Vault) {
|
private fun decryptedCloudFor(vault: Vault) {
|
||||||
getDecryptedCloudForVaultUseCase //
|
getDecryptedCloudForVaultUseCase //
|
||||||
.withVault(vault) //
|
.withVault(vault) //
|
||||||
@ -172,32 +188,6 @@ class SharedFilesPresenter @Inject constructor( //
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onUnlockPressed(vaultModel: VaultModel, password: String?) {
|
|
||||||
view?.showProgress(ProgressModel.GENERIC)
|
|
||||||
unlockVaultUseCase //
|
|
||||||
.withVaultOrUnlockToken(VaultOrUnlockToken.from(vaultModel.toVault())) //
|
|
||||||
.andPassword(password) //
|
|
||||||
.run(object : DefaultResultHandler<Cloud>() {
|
|
||||||
override fun onSuccess(cloud: Cloud) {
|
|
||||||
view?.showProgress(ProgressModel.COMPLETED)
|
|
||||||
rootFolderFor(cloud)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onError(e: Throwable) {
|
|
||||||
if (!authenticationExceptionHandler.handleAuthenticationException(this@SharedFilesPresenter, e, ActivityResultCallbacks.unlockVaultAfterAuth(vaultModel.toVault(), password))) {
|
|
||||||
showError(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
@Callback
|
|
||||||
fun unlockVaultAfterAuth(result: ActivityResult, vault: Vault?, password: String?) {
|
|
||||||
val cloud = result.getSingleResult(CloudModel::class.java).toCloud()
|
|
||||||
val vaultWithUpdatedCloud = Vault.aCopyOf(vault).withCloud(cloud).build()
|
|
||||||
onUnlockPressed(VaultModel(vaultWithUpdatedCloud), password)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setLocation(location: CloudFolderModel) {
|
private fun setLocation(location: CloudFolderModel) {
|
||||||
this.location = location
|
this.location = location
|
||||||
}
|
}
|
||||||
@ -372,16 +362,6 @@ class SharedFilesPresenter @Inject constructor( //
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onCloudOfVaultAuthenticated(authenticatedVault: Vault) {
|
|
||||||
if (authenticatedVault.isUnlocked) {
|
|
||||||
decryptedCloudFor(authenticatedVault)
|
|
||||||
} else {
|
|
||||||
if (!isPaused) {
|
|
||||||
view?.showEnterPasswordDialog(VaultModel(authenticatedVault))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onChooseLocationPressed() {
|
fun onChooseLocationPressed() {
|
||||||
authenticate(selectedVault)
|
authenticate(selectedVault)
|
||||||
}
|
}
|
||||||
@ -410,25 +390,6 @@ class SharedFilesPresenter @Inject constructor( //
|
|||||||
view?.closeDialog()
|
view?.closeDialog()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onBiometricAuthKeyInvalidated() {
|
|
||||||
removeStoredVaultPasswordsUseCase.run(object : DefaultResultHandler<Void?>() {
|
|
||||||
override fun onFinished() {
|
|
||||||
view?.showBiometricAuthKeyInvalidatedDialog()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
selectedVault?.let {
|
|
||||||
selectedVault = VaultModel(Vault.aCopyOf(it.toVault()).withSavedPassword(null).build())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onUnlockCanceled() {
|
|
||||||
unlockVaultUseCase.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun useConfirmationInFaceUnlockBiometricAuthentication(): Boolean {
|
|
||||||
return sharedPreferencesHandler.useConfirmationInFaceUnlockBiometricAuthentication()
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum class AuthenticationState {
|
private enum class AuthenticationState {
|
||||||
CHOOSE_LOCATION, INIT_ROOT
|
CHOOSE_LOCATION, INIT_ROOT
|
||||||
}
|
}
|
||||||
@ -444,11 +405,9 @@ class SharedFilesPresenter @Inject constructor( //
|
|||||||
init {
|
init {
|
||||||
unsubscribeOnDestroy( //
|
unsubscribeOnDestroy( //
|
||||||
getRootFolderUseCase, //
|
getRootFolderUseCase, //
|
||||||
unlockVaultUseCase, //
|
|
||||||
getVaultListUseCase, //
|
getVaultListUseCase, //
|
||||||
getDecryptedCloudForVaultUseCase, //
|
getDecryptedCloudForVaultUseCase, //
|
||||||
uploadFilesUseCase, //
|
uploadFilesUseCase, //
|
||||||
getCloudListUseCase, //
|
getCloudListUseCase)
|
||||||
removeStoredVaultPasswordsUseCase)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,357 @@
|
|||||||
|
package org.cryptomator.presentation.presenter
|
||||||
|
|
||||||
|
import android.os.Handler
|
||||||
|
import androidx.biometric.BiometricManager
|
||||||
|
import org.cryptomator.domain.Cloud
|
||||||
|
import org.cryptomator.domain.Vault
|
||||||
|
import org.cryptomator.domain.di.PerView
|
||||||
|
import org.cryptomator.domain.exception.NetworkConnectionException
|
||||||
|
import org.cryptomator.domain.exception.authentication.AuthenticationException
|
||||||
|
import org.cryptomator.domain.usecases.vault.ChangePasswordUseCase
|
||||||
|
import org.cryptomator.domain.usecases.vault.LockVaultUseCase
|
||||||
|
import org.cryptomator.domain.usecases.vault.PrepareUnlockUseCase
|
||||||
|
import org.cryptomator.domain.usecases.vault.RemoveStoredVaultPasswordsUseCase
|
||||||
|
import org.cryptomator.domain.usecases.vault.SaveVaultUseCase
|
||||||
|
import org.cryptomator.domain.usecases.vault.UnlockToken
|
||||||
|
import org.cryptomator.domain.usecases.vault.UnlockVaultUseCase
|
||||||
|
import org.cryptomator.domain.usecases.vault.VaultOrUnlockToken
|
||||||
|
import org.cryptomator.generator.Callback
|
||||||
|
import org.cryptomator.generator.InjectIntent
|
||||||
|
import org.cryptomator.presentation.R
|
||||||
|
import org.cryptomator.presentation.exception.ExceptionHandlers
|
||||||
|
import org.cryptomator.presentation.intent.UnlockVaultIntent
|
||||||
|
import org.cryptomator.presentation.model.CloudModel
|
||||||
|
import org.cryptomator.presentation.model.ProgressModel
|
||||||
|
import org.cryptomator.presentation.model.ProgressStateModel
|
||||||
|
import org.cryptomator.presentation.model.VaultModel
|
||||||
|
import org.cryptomator.presentation.ui.activity.view.UnlockVaultView
|
||||||
|
import org.cryptomator.presentation.workflow.ActivityResult
|
||||||
|
import org.cryptomator.presentation.workflow.AuthenticationExceptionHandler
|
||||||
|
import org.cryptomator.util.SharedPreferencesHandler
|
||||||
|
import java.io.Serializable
|
||||||
|
import javax.inject.Inject
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
|
@PerView
|
||||||
|
class UnlockVaultPresenter @Inject constructor(
|
||||||
|
private val changePasswordUseCase: ChangePasswordUseCase,
|
||||||
|
private val lockVaultUseCase: LockVaultUseCase,
|
||||||
|
private val unlockVaultUseCase: UnlockVaultUseCase,
|
||||||
|
private val prepareUnlockUseCase: PrepareUnlockUseCase,
|
||||||
|
private val removeStoredVaultPasswordsUseCase: RemoveStoredVaultPasswordsUseCase,
|
||||||
|
private val saveVaultUseCase: SaveVaultUseCase,
|
||||||
|
private val authenticationExceptionHandler: AuthenticationExceptionHandler,
|
||||||
|
private val sharedPreferencesHandler: SharedPreferencesHandler,
|
||||||
|
exceptionMappings: ExceptionHandlers) : Presenter<UnlockVaultView>(exceptionMappings) {
|
||||||
|
|
||||||
|
private var startedUsingPrepareUnlock = false
|
||||||
|
private var retryUnlockHandler: Handler? = null
|
||||||
|
private var pendingUnlock: PendingUnlock? = null
|
||||||
|
|
||||||
|
@InjectIntent
|
||||||
|
lateinit var intent: UnlockVaultIntent
|
||||||
|
|
||||||
|
@Volatile
|
||||||
|
private var running: Boolean = false
|
||||||
|
|
||||||
|
override fun destroyed() {
|
||||||
|
super.destroyed()
|
||||||
|
if (retryUnlockHandler != null) {
|
||||||
|
running = false
|
||||||
|
retryUnlockHandler?.removeCallbacks(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setup() {
|
||||||
|
when (intent.vaultAction()) {
|
||||||
|
UnlockVaultIntent.VaultAction.ENCRYPT_PASSWORD -> view?.getEncryptedPasswordWithBiometricAuthentication(intent.vaultModel())
|
||||||
|
UnlockVaultIntent.VaultAction.UNLOCK, UnlockVaultIntent.VaultAction.UNLOCK_FOR_BIOMETRIC_AUTH -> unlockVault(intent.vaultModel())
|
||||||
|
UnlockVaultIntent.VaultAction.CHANGE_PASSWORD -> view?.showChangePasswordDialog(intent.vaultModel())
|
||||||
|
else -> TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun unlockVault(vaultModel: VaultModel) {
|
||||||
|
if (canUseBiometricOn(vaultModel)) {
|
||||||
|
if (startedUsingPrepareUnlock) {
|
||||||
|
startPrepareUnlockUseCase(vaultModel.toVault())
|
||||||
|
}
|
||||||
|
view?.showBiometricDialog(vaultModel)
|
||||||
|
} else {
|
||||||
|
startPrepareUnlockUseCase(vaultModel.toVault())
|
||||||
|
view?.showEnterPasswordDialog(vaultModel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onWindowFocusChanged(hasFocus: Boolean) {
|
||||||
|
if (hasFocus) {
|
||||||
|
if (retryUnlockHandler != null) {
|
||||||
|
running = false
|
||||||
|
retryUnlockHandler?.removeCallbacks(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun pendingUnlockFor(vault: Vault): PendingUnlock? {
|
||||||
|
if (pendingUnlock == null) {
|
||||||
|
pendingUnlock = PendingUnlock(vault)
|
||||||
|
}
|
||||||
|
return if (pendingUnlock?.belongsTo(vault) == true) {
|
||||||
|
pendingUnlock
|
||||||
|
} else {
|
||||||
|
PendingUnlock.NO_OP_PENDING_UNLOCK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun canUseBiometricOn(vault: VaultModel): Boolean {
|
||||||
|
return vault.password != null && BiometricManager.from(context()).canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onUnlockCanceled() {
|
||||||
|
prepareUnlockUseCase.unsubscribe()
|
||||||
|
unlockVaultUseCase.cancel()
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun startPrepareUnlockUseCase(vault: Vault) {
|
||||||
|
pendingUnlock = null
|
||||||
|
prepareUnlockUseCase //
|
||||||
|
.withVault(vault) //
|
||||||
|
.run(object : DefaultResultHandler<UnlockToken>() {
|
||||||
|
override fun onSuccess(unlockToken: UnlockToken) {
|
||||||
|
if (!startedUsingPrepareUnlock && vault.password != null) {
|
||||||
|
doUnlock(unlockToken, vault.password)
|
||||||
|
} else {
|
||||||
|
unlockTokenObtained(unlockToken)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onError(e: Throwable) {
|
||||||
|
if (e is AuthenticationException) {
|
||||||
|
view?.cancelBasicAuthIfRunning()
|
||||||
|
}
|
||||||
|
if (!authenticationExceptionHandler.handleAuthenticationException(this@UnlockVaultPresenter, e, ActivityResultCallbacks.authenticatedAfterUnlock(vault))) {
|
||||||
|
super.onError(e)
|
||||||
|
if (e is NetworkConnectionException) {
|
||||||
|
running = true
|
||||||
|
retryUnlockHandler = Handler()
|
||||||
|
restartUnlockUseCase(vault)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@Callback(dispatchResultOkOnly = false)
|
||||||
|
fun authenticatedAfterUnlock(result: ActivityResult, vault: Vault) {
|
||||||
|
if (result.isResultOk) {
|
||||||
|
val cloud = result.getSingleResult(CloudModel::class.java).toCloud()
|
||||||
|
if (startedUsingPrepareUnlock) {
|
||||||
|
startPrepareUnlockUseCase(Vault.aCopyOf(vault).withCloud(cloud).build())
|
||||||
|
if (view?.stoppedBiometricAuthDuringCloudAuthentication() == true) {
|
||||||
|
view?.showBiometricDialog(VaultModel(vault))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
view?.showProgress(ProgressModel.GENERIC)
|
||||||
|
startPrepareUnlockUseCase(vault)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
view?.closeDialog()
|
||||||
|
val error = result.getSingleResult(Throwable::class.java)
|
||||||
|
error?.let { showError(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun restartUnlockUseCase(vault: Vault) {
|
||||||
|
retryUnlockHandler?.postDelayed({
|
||||||
|
if (running) {
|
||||||
|
prepareUnlockUseCase //
|
||||||
|
.withVault(vault) //
|
||||||
|
.run(object : DefaultResultHandler<UnlockToken>() {
|
||||||
|
override fun onSuccess(unlockToken: UnlockToken) {
|
||||||
|
if (!startedUsingPrepareUnlock && vault.password != null) {
|
||||||
|
doUnlock(unlockToken, vault.password)
|
||||||
|
} else {
|
||||||
|
unlockTokenObtained(unlockToken)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onError(e: Throwable) {
|
||||||
|
if (e is NetworkConnectionException) {
|
||||||
|
restartUnlockUseCase(vault)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun unlockTokenObtained(unlockToken: UnlockToken) {
|
||||||
|
pendingUnlockFor(unlockToken.vault)?.setUnlockToken(unlockToken, this)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onUnlockClick(vault: VaultModel, password: String?) {
|
||||||
|
view?.showProgress(ProgressModel.GENERIC)
|
||||||
|
pendingUnlockFor(vault.toVault())?.setPassword(password, this)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun doUnlock(token: UnlockToken, password: String) {
|
||||||
|
unlockVaultUseCase //
|
||||||
|
.withVaultOrUnlockToken(VaultOrUnlockToken.from(token)) //
|
||||||
|
.andPassword(password) //
|
||||||
|
.run(object : DefaultResultHandler<Cloud>() {
|
||||||
|
override fun onSuccess(cloud: Cloud) {
|
||||||
|
when (intent.vaultAction()) {
|
||||||
|
UnlockVaultIntent.VaultAction.ENCRYPT_PASSWORD, UnlockVaultIntent.VaultAction.UNLOCK_FOR_BIOMETRIC_AUTH -> {
|
||||||
|
handleUnlockVaultSuccess(token.vault, cloud, password)
|
||||||
|
}
|
||||||
|
UnlockVaultIntent.VaultAction.UNLOCK -> finishWithResult(cloud)
|
||||||
|
else -> TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleUnlockVaultSuccess(vault: Vault, cloud: Cloud, password: String) {
|
||||||
|
lockVaultUseCase.withVault(vault).run(object : DefaultResultHandler<Vault>() {
|
||||||
|
override fun onFinished() {
|
||||||
|
finishWithResultAndExtra(cloud, PASSWORD, password)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun startedUsingPrepareUnlock(): Boolean {
|
||||||
|
return startedUsingPrepareUnlock
|
||||||
|
}
|
||||||
|
|
||||||
|
fun useConfirmationInFaceUnlockBiometricAuthentication(): Boolean {
|
||||||
|
return sharedPreferencesHandler.useConfirmationInFaceUnlockBiometricAuthentication()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onBiometricKeyInvalidated() {
|
||||||
|
removeStoredVaultPasswordsUseCase.run(object : DefaultResultHandler<Void?>() {
|
||||||
|
override fun onSuccess(void: Void?) {
|
||||||
|
view?.showBiometricAuthKeyInvalidatedDialog()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onError(e: Throwable) {
|
||||||
|
Timber.tag("VaultListPresenter").e(e, "Error while removing vault passwords")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onBiometricAuthenticationSucceeded(vaultModel: VaultModel) {
|
||||||
|
when (intent.vaultAction()) {
|
||||||
|
UnlockVaultIntent.VaultAction.ENCRYPT_PASSWORD -> finishWithResult(vaultModel)
|
||||||
|
UnlockVaultIntent.VaultAction.UNLOCK, UnlockVaultIntent.VaultAction.UNLOCK_FOR_BIOMETRIC_AUTH -> {
|
||||||
|
if (startedUsingPrepareUnlock) {
|
||||||
|
onUnlockClick(vaultModel, vaultModel.password)
|
||||||
|
} else {
|
||||||
|
view?.showProgress(ProgressModel.GENERIC)
|
||||||
|
startPrepareUnlockUseCase(vaultModel.toVault())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UnlockVaultIntent.VaultAction.CHANGE_PASSWORD -> {
|
||||||
|
saveVaultUseCase //
|
||||||
|
.withVault(vaultModel.toVault()) //
|
||||||
|
.run(object : DefaultResultHandler<Vault>() {
|
||||||
|
override fun onSuccess(vault: Vault) {
|
||||||
|
finishWithResult(vaultModel)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else -> TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onChangePasswordClick(vaultModel: VaultModel, oldPassword: String, newPassword: String) {
|
||||||
|
view?.showProgress(ProgressModel(ProgressStateModel.CHANGING_PASSWORD))
|
||||||
|
changePasswordUseCase.withVault(vaultModel.toVault()) //
|
||||||
|
.andOldPassword(oldPassword) //
|
||||||
|
.andNewPassword(newPassword) //
|
||||||
|
.run(object : DefaultResultHandler<Void?>() {
|
||||||
|
override fun onSuccess(void: Void?) {
|
||||||
|
view?.showProgress(ProgressModel.COMPLETED)
|
||||||
|
view?.showMessage(R.string.screen_vault_list_change_password_successful)
|
||||||
|
if (canUseBiometricOn(vaultModel)) {
|
||||||
|
view?.getEncryptedPasswordWithBiometricAuthentication(VaultModel( //
|
||||||
|
Vault.aCopyOf(vaultModel.toVault()) //
|
||||||
|
.withSavedPassword(newPassword) //
|
||||||
|
.build()))
|
||||||
|
} else {
|
||||||
|
finishWithResult(vaultModel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onError(e: Throwable) {
|
||||||
|
if (!authenticationExceptionHandler.handleAuthenticationException( //
|
||||||
|
this@UnlockVaultPresenter, e, //
|
||||||
|
ActivityResultCallbacks.changePasswordAfterAuthentication(vaultModel.toVault(), oldPassword, newPassword))) {
|
||||||
|
showError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@Callback
|
||||||
|
fun changePasswordAfterAuthentication(result: ActivityResult, vault: Vault, oldPassword: String, newPassword: String) {
|
||||||
|
val cloud = result.getSingleResult(CloudModel::class.java).toCloud()
|
||||||
|
val vaultWithUpdatedCloud = Vault.aCopyOf(vault).withCloud(cloud).build()
|
||||||
|
onChangePasswordClick(VaultModel(vaultWithUpdatedCloud), oldPassword, newPassword)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveVaultAfterChangePasswordButFailedBiometricAuth(vault: Vault) {
|
||||||
|
Timber.tag("UnlockVaultPresenter").e("Save vault without password because biometric auth failed after changing vault password")
|
||||||
|
saveVaultUseCase //
|
||||||
|
.withVault(vault) //
|
||||||
|
.run(object : DefaultResultHandler<Vault>() {
|
||||||
|
override fun onSuccess(vault: Vault) {
|
||||||
|
finishWithResult(vault)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private open class PendingUnlock(private val vault: Vault?) : Serializable {
|
||||||
|
|
||||||
|
private var unlockToken: UnlockToken? = null
|
||||||
|
private var password: String? = null
|
||||||
|
|
||||||
|
fun setUnlockToken(unlockToken: UnlockToken?, presenter: UnlockVaultPresenter) {
|
||||||
|
this.unlockToken = unlockToken
|
||||||
|
continueIfComplete(presenter)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setPassword(password: String?, presenter: UnlockVaultPresenter) {
|
||||||
|
this.password = password
|
||||||
|
continueIfComplete(presenter)
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun continueIfComplete(presenter: UnlockVaultPresenter) {
|
||||||
|
unlockToken?.let { token -> password?.let { password -> presenter.doUnlock(token, password) } }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun belongsTo(vault: Vault): Boolean {
|
||||||
|
return vault == this.vault
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
val NO_OP_PENDING_UNLOCK: PendingUnlock = object : PendingUnlock(null) {
|
||||||
|
override fun continueIfComplete(presenter: UnlockVaultPresenter) {
|
||||||
|
// empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
const val PASSWORD = "password"
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
unsubscribeOnDestroy(changePasswordUseCase, lockVaultUseCase, unlockVaultUseCase, prepareUnlockUseCase, removeStoredVaultPasswordsUseCase, saveVaultUseCase)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -6,16 +6,12 @@ import android.content.ActivityNotFoundException
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Handler
|
|
||||||
import androidx.biometric.BiometricManager
|
|
||||||
import org.cryptomator.data.cloud.crypto.CryptoCloud
|
import org.cryptomator.data.cloud.crypto.CryptoCloud
|
||||||
import org.cryptomator.data.util.NetworkConnectionCheck
|
import org.cryptomator.data.util.NetworkConnectionCheck
|
||||||
import org.cryptomator.domain.Cloud
|
import org.cryptomator.domain.Cloud
|
||||||
import org.cryptomator.domain.CloudFolder
|
import org.cryptomator.domain.CloudFolder
|
||||||
import org.cryptomator.domain.Vault
|
import org.cryptomator.domain.Vault
|
||||||
import org.cryptomator.domain.di.PerView
|
import org.cryptomator.domain.di.PerView
|
||||||
import org.cryptomator.domain.exception.NetworkConnectionException
|
|
||||||
import org.cryptomator.domain.exception.authentication.AuthenticationException
|
|
||||||
import org.cryptomator.domain.exception.license.LicenseNotValidException
|
import org.cryptomator.domain.exception.license.LicenseNotValidException
|
||||||
import org.cryptomator.domain.exception.update.SSLHandshakePreAndroid5UpdateCheckException
|
import org.cryptomator.domain.exception.update.SSLHandshakePreAndroid5UpdateCheckException
|
||||||
import org.cryptomator.domain.usecases.DoLicenseCheckUseCase
|
import org.cryptomator.domain.usecases.DoLicenseCheckUseCase
|
||||||
@ -26,27 +22,21 @@ import org.cryptomator.domain.usecases.LicenseCheck
|
|||||||
import org.cryptomator.domain.usecases.NoOpResultHandler
|
import org.cryptomator.domain.usecases.NoOpResultHandler
|
||||||
import org.cryptomator.domain.usecases.UpdateCheck
|
import org.cryptomator.domain.usecases.UpdateCheck
|
||||||
import org.cryptomator.domain.usecases.cloud.GetRootFolderUseCase
|
import org.cryptomator.domain.usecases.cloud.GetRootFolderUseCase
|
||||||
import org.cryptomator.domain.usecases.vault.ChangePasswordUseCase
|
|
||||||
import org.cryptomator.domain.usecases.vault.DeleteVaultUseCase
|
import org.cryptomator.domain.usecases.vault.DeleteVaultUseCase
|
||||||
import org.cryptomator.domain.usecases.vault.GetVaultListUseCase
|
import org.cryptomator.domain.usecases.vault.GetVaultListUseCase
|
||||||
import org.cryptomator.domain.usecases.vault.LockVaultUseCase
|
import org.cryptomator.domain.usecases.vault.LockVaultUseCase
|
||||||
import org.cryptomator.domain.usecases.vault.MoveVaultPositionUseCase
|
import org.cryptomator.domain.usecases.vault.MoveVaultPositionUseCase
|
||||||
import org.cryptomator.domain.usecases.vault.PrepareUnlockUseCase
|
|
||||||
import org.cryptomator.domain.usecases.vault.RemoveStoredVaultPasswordsUseCase
|
|
||||||
import org.cryptomator.domain.usecases.vault.RenameVaultUseCase
|
import org.cryptomator.domain.usecases.vault.RenameVaultUseCase
|
||||||
import org.cryptomator.domain.usecases.vault.SaveVaultUseCase
|
import org.cryptomator.domain.usecases.vault.SaveVaultUseCase
|
||||||
import org.cryptomator.domain.usecases.vault.UnlockToken
|
|
||||||
import org.cryptomator.domain.usecases.vault.UnlockVaultUseCase
|
|
||||||
import org.cryptomator.domain.usecases.vault.VaultOrUnlockToken
|
|
||||||
import org.cryptomator.generator.Callback
|
import org.cryptomator.generator.Callback
|
||||||
import org.cryptomator.presentation.BuildConfig
|
import org.cryptomator.presentation.BuildConfig
|
||||||
import org.cryptomator.presentation.R
|
import org.cryptomator.presentation.R
|
||||||
import org.cryptomator.presentation.exception.ExceptionHandlers
|
import org.cryptomator.presentation.exception.ExceptionHandlers
|
||||||
import org.cryptomator.presentation.intent.Intents
|
import org.cryptomator.presentation.intent.Intents
|
||||||
|
import org.cryptomator.presentation.intent.UnlockVaultIntent
|
||||||
import org.cryptomator.presentation.model.CloudModel
|
import org.cryptomator.presentation.model.CloudModel
|
||||||
import org.cryptomator.presentation.model.CloudTypeModel
|
import org.cryptomator.presentation.model.CloudTypeModel
|
||||||
import org.cryptomator.presentation.model.ProgressModel
|
import org.cryptomator.presentation.model.ProgressModel
|
||||||
import org.cryptomator.presentation.model.ProgressStateModel
|
|
||||||
import org.cryptomator.presentation.model.VaultModel
|
import org.cryptomator.presentation.model.VaultModel
|
||||||
import org.cryptomator.presentation.model.mappers.CloudFolderModelMapper
|
import org.cryptomator.presentation.model.mappers.CloudFolderModelMapper
|
||||||
import org.cryptomator.presentation.service.AutoUploadService
|
import org.cryptomator.presentation.service.AutoUploadService
|
||||||
@ -65,7 +55,6 @@ import org.cryptomator.presentation.workflow.CreateNewVaultWorkflow
|
|||||||
import org.cryptomator.presentation.workflow.Workflow
|
import org.cryptomator.presentation.workflow.Workflow
|
||||||
import org.cryptomator.util.Optional
|
import org.cryptomator.util.Optional
|
||||||
import org.cryptomator.util.SharedPreferencesHandler
|
import org.cryptomator.util.SharedPreferencesHandler
|
||||||
import java.io.Serializable
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
@ -76,15 +65,11 @@ class VaultListPresenter @Inject constructor( //
|
|||||||
private val renameVaultUseCase: RenameVaultUseCase, //
|
private val renameVaultUseCase: RenameVaultUseCase, //
|
||||||
private val lockVaultUseCase: LockVaultUseCase, //
|
private val lockVaultUseCase: LockVaultUseCase, //
|
||||||
private val getDecryptedCloudForVaultUseCase: GetDecryptedCloudForVaultUseCase, //
|
private val getDecryptedCloudForVaultUseCase: GetDecryptedCloudForVaultUseCase, //
|
||||||
private val prepareUnlockUseCase: PrepareUnlockUseCase, //
|
|
||||||
private val unlockVaultUseCase: UnlockVaultUseCase, //
|
|
||||||
private val getRootFolderUseCase: GetRootFolderUseCase, //
|
private val getRootFolderUseCase: GetRootFolderUseCase, //
|
||||||
private val addExistingVaultWorkflow: AddExistingVaultWorkflow, //
|
private val addExistingVaultWorkflow: AddExistingVaultWorkflow, //
|
||||||
private val createNewVaultWorkflow: CreateNewVaultWorkflow, //
|
private val createNewVaultWorkflow: CreateNewVaultWorkflow, //
|
||||||
private val saveVaultUseCase: SaveVaultUseCase, //
|
private val saveVaultUseCase: SaveVaultUseCase, //
|
||||||
private val moveVaultPositionUseCase: MoveVaultPositionUseCase, //
|
private val moveVaultPositionUseCase: MoveVaultPositionUseCase, //
|
||||||
private val changePasswordUseCase: ChangePasswordUseCase, //
|
|
||||||
private val removeStoredVaultPasswordsUseCase: RemoveStoredVaultPasswordsUseCase, //
|
|
||||||
private val licenseCheckUseCase: DoLicenseCheckUseCase, //
|
private val licenseCheckUseCase: DoLicenseCheckUseCase, //
|
||||||
private val updateCheckUseCase: DoUpdateCheckUseCase, //
|
private val updateCheckUseCase: DoUpdateCheckUseCase, //
|
||||||
private val updateUseCase: DoUpdateUseCase, //
|
private val updateUseCase: DoUpdateUseCase, //
|
||||||
@ -96,31 +81,14 @@ class VaultListPresenter @Inject constructor( //
|
|||||||
exceptionMappings: ExceptionHandlers) : Presenter<VaultListView>(exceptionMappings) {
|
exceptionMappings: ExceptionHandlers) : Presenter<VaultListView>(exceptionMappings) {
|
||||||
|
|
||||||
private var vaultAction: VaultAction? = null
|
private var vaultAction: VaultAction? = null
|
||||||
private var changedVaultPassword = false
|
|
||||||
private var startedUsingPrepareUnlock = false
|
|
||||||
private var retryUnlockHandler: Handler? = null
|
|
||||||
|
|
||||||
@Volatile
|
|
||||||
private var running = false
|
|
||||||
override fun workflows(): Iterable<Workflow<*>> {
|
override fun workflows(): Iterable<Workflow<*>> {
|
||||||
return listOf(addExistingVaultWorkflow, createNewVaultWorkflow)
|
return listOf(addExistingVaultWorkflow, createNewVaultWorkflow)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun destroyed() {
|
|
||||||
super.destroyed()
|
|
||||||
if (retryUnlockHandler != null) {
|
|
||||||
running = false
|
|
||||||
retryUnlockHandler?.removeCallbacks(null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onWindowFocusChanged(hasFocus: Boolean) {
|
fun onWindowFocusChanged(hasFocus: Boolean) {
|
||||||
if (hasFocus) {
|
if (hasFocus) {
|
||||||
loadVaultList()
|
loadVaultList()
|
||||||
if (retryUnlockHandler != null) {
|
|
||||||
running = false
|
|
||||||
retryUnlockHandler?.removeCallbacks(null)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,11 +225,6 @@ class VaultListPresenter @Inject constructor( //
|
|||||||
renameVault(VaultModel(vaultWithUpdatedCloud), newVaultName)
|
renameVault(VaultModel(vaultWithUpdatedCloud), newVaultName)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onUnlockCanceled() {
|
|
||||||
prepareUnlockUseCase.unsubscribe()
|
|
||||||
unlockVaultUseCase.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun browseFilesOf(vault: VaultModel) {
|
private fun browseFilesOf(vault: VaultModel) {
|
||||||
getDecryptedCloudForVaultUseCase //
|
getDecryptedCloudForVaultUseCase //
|
||||||
.withVault(vault.toVault()) //
|
.withVault(vault.toVault()) //
|
||||||
@ -318,7 +281,6 @@ class VaultListPresenter @Inject constructor( //
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun onVaultClicked(vault: VaultModel) {
|
fun onVaultClicked(vault: VaultModel) {
|
||||||
startedUsingPrepareUnlock = sharedPreferencesHandler.backgroundUnlockPreparation()
|
|
||||||
startVaultAction(vault, VaultAction.UNLOCK)
|
startVaultAction(vault, VaultAction.UNLOCK)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -378,7 +340,6 @@ class VaultListPresenter @Inject constructor( //
|
|||||||
when (vaultAction) {
|
when (vaultAction) {
|
||||||
VaultAction.UNLOCK -> requireUserAuthentication(authenticatedVaultModel)
|
VaultAction.UNLOCK -> requireUserAuthentication(authenticatedVaultModel)
|
||||||
VaultAction.RENAME -> view?.showRenameDialog(authenticatedVaultModel)
|
VaultAction.RENAME -> view?.showRenameDialog(authenticatedVaultModel)
|
||||||
VaultAction.CHANGE_PASSWORD -> view?.showChangePasswordDialog(authenticatedVaultModel)
|
|
||||||
}
|
}
|
||||||
vaultAction = null
|
vaultAction = null
|
||||||
}
|
}
|
||||||
@ -387,83 +348,22 @@ class VaultListPresenter @Inject constructor( //
|
|||||||
view?.addOrUpdateVault(authenticatedVault)
|
view?.addOrUpdateVault(authenticatedVault)
|
||||||
if (authenticatedVault.isLocked) {
|
if (authenticatedVault.isLocked) {
|
||||||
if (!isPaused) {
|
if (!isPaused) {
|
||||||
if (canUseBiometricOn(authenticatedVault)) {
|
requestActivityResult( //
|
||||||
if (startedUsingPrepareUnlock) {
|
ActivityResultCallbacks.vaultUnlockedVaultList(), //
|
||||||
startPrepareUnlockUseCase(authenticatedVault.toVault())
|
Intents.unlockVaultIntent().withVaultModel(authenticatedVault).withVaultAction(UnlockVaultIntent.VaultAction.UNLOCK))
|
||||||
}
|
|
||||||
view?.showBiometricDialog(authenticatedVault)
|
|
||||||
} else {
|
|
||||||
startPrepareUnlockUseCase(authenticatedVault.toVault())
|
|
||||||
view?.showEnterPasswordDialog(authenticatedVault)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
browseFilesOf(authenticatedVault)
|
browseFilesOf(authenticatedVault)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startPrepareUnlockUseCase(vault: Vault) {
|
@Callback
|
||||||
pendingUnlock = null
|
fun vaultUnlockedVaultList(result: ActivityResult) {
|
||||||
prepareUnlockUseCase //
|
val cloud = result.intent().getSerializableExtra(SINGLE_RESULT) as Cloud
|
||||||
.withVault(vault) //
|
when {
|
||||||
.run(object : DefaultResultHandler<UnlockToken>() {
|
result.isResultOk -> navigateToVaultContent(cloud)
|
||||||
override fun onSuccess(unlockToken: UnlockToken) {
|
else -> TODO("Not yet implemented")
|
||||||
if (!startedUsingPrepareUnlock && vault.password != null) {
|
}
|
||||||
doUnlock(unlockToken, vault.password)
|
|
||||||
} else {
|
|
||||||
unlockTokenObtained(unlockToken)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onError(e: Throwable) {
|
|
||||||
if (e is AuthenticationException) {
|
|
||||||
view?.cancelBasicAuthIfRunning()
|
|
||||||
}
|
|
||||||
if (!authenticationExceptionHandler.handleAuthenticationException(this@VaultListPresenter, e, ActivityResultCallbacks.authenticatedAfterUnlock(vault))) {
|
|
||||||
super.onError(e)
|
|
||||||
if (e is NetworkConnectionException) {
|
|
||||||
running = true
|
|
||||||
retryUnlockHandler = Handler()
|
|
||||||
restartUnlockUseCase(vault)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun restartUnlockUseCase(vault: Vault) {
|
|
||||||
retryUnlockHandler?.postDelayed({
|
|
||||||
if (running) {
|
|
||||||
prepareUnlockUseCase //
|
|
||||||
.withVault(vault) //
|
|
||||||
.run(object : DefaultResultHandler<UnlockToken>() {
|
|
||||||
override fun onSuccess(unlockToken: UnlockToken) {
|
|
||||||
if (!startedUsingPrepareUnlock && vault.password != null) {
|
|
||||||
doUnlock(unlockToken, vault.password)
|
|
||||||
} else {
|
|
||||||
unlockTokenObtained(unlockToken)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onError(e: Throwable) {
|
|
||||||
if (e is NetworkConnectionException) {
|
|
||||||
restartUnlockUseCase(vault)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}, 1000)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun doUnlock(token: UnlockToken, password: String) {
|
|
||||||
unlockVaultUseCase //
|
|
||||||
.withVaultOrUnlockToken(VaultOrUnlockToken.from(token)) //
|
|
||||||
.andPassword(password) //
|
|
||||||
.run(object : DefaultResultHandler<Cloud>() {
|
|
||||||
override fun onSuccess(cloud: Cloud) {
|
|
||||||
navigateToVaultContent(cloud)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun navigateToVaultContent(cloud: Cloud) {
|
private fun navigateToVaultContent(cloud: Cloud) {
|
||||||
@ -488,51 +388,6 @@ class VaultListPresenter @Inject constructor( //
|
|||||||
} else false
|
} else false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun unlockTokenObtained(unlockToken: UnlockToken) {
|
|
||||||
pendingUnlockFor(unlockToken.vault)?.setUnlockToken(unlockToken, this)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onUnlockClick(vault: VaultModel, password: String?) {
|
|
||||||
view?.showProgress(ProgressModel.GENERIC)
|
|
||||||
pendingUnlockFor(vault.toVault())?.setPassword(password, this)
|
|
||||||
}
|
|
||||||
|
|
||||||
private var pendingUnlock: PendingUnlock? = null
|
|
||||||
private fun pendingUnlockFor(vault: Vault): PendingUnlock? {
|
|
||||||
if (pendingUnlock == null) {
|
|
||||||
pendingUnlock = PendingUnlock(vault)
|
|
||||||
}
|
|
||||||
return if (pendingUnlock?.belongsTo(vault) == true) {
|
|
||||||
pendingUnlock
|
|
||||||
} else {
|
|
||||||
PendingUnlock.NO_OP_PENDING_UNLOCK
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Callback(dispatchResultOkOnly = false)
|
|
||||||
fun authenticatedAfterUnlock(result: ActivityResult, vault: Vault) {
|
|
||||||
if (result.isResultOk) {
|
|
||||||
val cloud = result.getSingleResult(CloudModel::class.java).toCloud()
|
|
||||||
if (startedUsingPrepareUnlock) {
|
|
||||||
startPrepareUnlockUseCase(Vault.aCopyOf(vault).withCloud(cloud).build())
|
|
||||||
if (view?.stoppedBiometricAuthDuringCloudAuthentication() == true) {
|
|
||||||
view?.showBiometricDialog(VaultModel(vault))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
view?.showProgress(ProgressModel.GENERIC)
|
|
||||||
startPrepareUnlockUseCase(vault)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
view?.closeDialog()
|
|
||||||
val error = result.getSingleResult(Throwable::class.java)
|
|
||||||
error?.let { showError(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun canUseBiometricOn(vault: VaultModel): Boolean {
|
|
||||||
return vault.password != null && BiometricManager.from(context()).canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onAddExistingVault() {
|
fun onAddExistingVault() {
|
||||||
addExistingVaultWorkflow.start()
|
addExistingVaultWorkflow.start()
|
||||||
}
|
}
|
||||||
@ -548,60 +403,11 @@ class VaultListPresenter @Inject constructor( //
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun onChangePasswordClicked(vaultModel: VaultModel) {
|
fun onChangePasswordClicked(vaultModel: VaultModel) {
|
||||||
startVaultAction(vaultModel, VaultAction.CHANGE_PASSWORD)
|
Intents
|
||||||
}
|
.unlockVaultIntent()
|
||||||
|
.withVaultModel(vaultModel)
|
||||||
fun onChangePasswordClicked(vaultModel: VaultModel, oldPassword: String?, newPassword: String?) {
|
.withVaultAction(UnlockVaultIntent.VaultAction.CHANGE_PASSWORD)
|
||||||
view?.showProgress(ProgressModel(ProgressStateModel.CHANGING_PASSWORD))
|
.startActivity(this)
|
||||||
changePasswordUseCase.withVault(vaultModel.toVault()) //
|
|
||||||
.andOldPassword(oldPassword) //
|
|
||||||
.andNewPassword(newPassword) //
|
|
||||||
.run(object : DefaultResultHandler<Void?>() {
|
|
||||||
override fun onSuccess(void: Void?) {
|
|
||||||
view?.showProgress(ProgressModel.COMPLETED)
|
|
||||||
view?.showMessage(R.string.screen_vault_list_change_password_successful)
|
|
||||||
if (canUseBiometricOn(vaultModel)) {
|
|
||||||
changedVaultPassword = true
|
|
||||||
view?.getEncryptedPasswordWithBiometricAuthentication(VaultModel( //
|
|
||||||
Vault.aCopyOf(vaultModel.toVault()) //
|
|
||||||
.withSavedPassword(newPassword) //
|
|
||||||
.build()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onError(e: Throwable) {
|
|
||||||
if (!authenticationExceptionHandler.handleAuthenticationException( //
|
|
||||||
this@VaultListPresenter, e, //
|
|
||||||
ActivityResultCallbacks.changePasswordAfterAuthentication(vaultModel.toVault(), oldPassword, newPassword))) {
|
|
||||||
showError(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
@Callback
|
|
||||||
fun changePasswordAfterAuthentication(result: ActivityResult, vault: Vault?, oldPassword: String?, newPassword: String?) {
|
|
||||||
val cloud = result.getSingleResult(CloudModel::class.java).toCloud()
|
|
||||||
val vaultWithUpdatedCloud = Vault.aCopyOf(vault).withCloud(cloud).build()
|
|
||||||
onChangePasswordClicked(VaultModel(vaultWithUpdatedCloud), oldPassword, newPassword)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun save(vaultModel: VaultModel) {
|
|
||||||
saveVaultUseCase //
|
|
||||||
.withVault(vaultModel.toVault()) //
|
|
||||||
.run(DefaultResultHandler())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onBiometricKeyInvalidated() {
|
|
||||||
removeStoredVaultPasswordsUseCase.run(object : DefaultResultHandler<Void?>() {
|
|
||||||
override fun onSuccess(void: Void?) {
|
|
||||||
view?.showBiometricAuthKeyInvalidatedDialog()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onError(e: Throwable) {
|
|
||||||
Timber.tag("VaultListPresenter").e(e, "Error while removing vault passwords")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onVaultSettingsClicked(vaultModel: VaultModel) {
|
fun onVaultSettingsClicked(vaultModel: VaultModel) {
|
||||||
@ -654,26 +460,8 @@ class VaultListPresenter @Inject constructor( //
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onBiometricAuthenticationSucceeded(vaultModel: VaultModel) {
|
|
||||||
if (changedVaultPassword) {
|
|
||||||
changedVaultPassword = false
|
|
||||||
save(vaultModel)
|
|
||||||
} else {
|
|
||||||
if (startedUsingPrepareUnlock) {
|
|
||||||
onUnlockClick(vaultModel, vaultModel.password)
|
|
||||||
} else {
|
|
||||||
view?.showProgress(ProgressModel.GENERIC)
|
|
||||||
startPrepareUnlockUseCase(vaultModel.toVault())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun useConfirmationInFaceUnlockBiometricAuthentication(): Boolean {
|
|
||||||
return sharedPreferencesHandler.useConfirmationInFaceUnlockBiometricAuthentication()
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum class VaultAction {
|
private enum class VaultAction {
|
||||||
UNLOCK, RENAME, CHANGE_PASSWORD
|
UNLOCK, RENAME
|
||||||
}
|
}
|
||||||
|
|
||||||
fun installUpdate() {
|
fun installUpdate() {
|
||||||
@ -698,43 +486,6 @@ class VaultListPresenter @Inject constructor( //
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startedUsingPrepareUnlock(): Boolean {
|
|
||||||
return startedUsingPrepareUnlock
|
|
||||||
}
|
|
||||||
|
|
||||||
private open class PendingUnlock(private val vault: Vault?) : Serializable {
|
|
||||||
|
|
||||||
private var unlockToken: UnlockToken? = null
|
|
||||||
private var password: String? = null
|
|
||||||
|
|
||||||
fun setUnlockToken(unlockToken: UnlockToken?, presenter: VaultListPresenter) {
|
|
||||||
this.unlockToken = unlockToken
|
|
||||||
continueIfComplete(presenter)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setPassword(password: String?, presenter: VaultListPresenter) {
|
|
||||||
this.password = password
|
|
||||||
continueIfComplete(presenter)
|
|
||||||
}
|
|
||||||
|
|
||||||
open fun continueIfComplete(presenter: VaultListPresenter) {
|
|
||||||
unlockToken?.let { token -> password?.let { password -> presenter.doUnlock(token, password) } }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun belongsTo(vault: Vault): Boolean {
|
|
||||||
return vault == this.vault
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
val NO_OP_PENDING_UNLOCK: PendingUnlock = object : PendingUnlock(null) {
|
|
||||||
override fun continueIfComplete(presenter: VaultListPresenter) {
|
|
||||||
// empty
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
unsubscribeOnDestroy( //
|
unsubscribeOnDestroy( //
|
||||||
deleteVaultUseCase, //
|
deleteVaultUseCase, //
|
||||||
@ -743,9 +494,6 @@ class VaultListPresenter @Inject constructor( //
|
|||||||
getVaultListUseCase, //
|
getVaultListUseCase, //
|
||||||
saveVaultUseCase, //
|
saveVaultUseCase, //
|
||||||
moveVaultPositionUseCase, //
|
moveVaultPositionUseCase, //
|
||||||
removeStoredVaultPasswordsUseCase, //
|
|
||||||
unlockVaultUseCase, //
|
|
||||||
prepareUnlockUseCase, //
|
|
||||||
licenseCheckUseCase, //
|
licenseCheckUseCase, //
|
||||||
updateCheckUseCase, //
|
updateCheckUseCase, //
|
||||||
updateUseCase)
|
updateUseCase)
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
package org.cryptomator.presentation.ui.activity
|
package org.cryptomator.presentation.ui.activity
|
||||||
|
|
||||||
import android.os.Build
|
|
||||||
import androidx.annotation.RequiresApi
|
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import org.cryptomator.generator.Activity
|
import org.cryptomator.generator.Activity
|
||||||
import org.cryptomator.presentation.R
|
import org.cryptomator.presentation.R
|
||||||
@ -9,20 +7,15 @@ import org.cryptomator.presentation.model.CloudFolderModel
|
|||||||
import org.cryptomator.presentation.model.VaultModel
|
import org.cryptomator.presentation.model.VaultModel
|
||||||
import org.cryptomator.presentation.presenter.AutoUploadChooseVaultPresenter
|
import org.cryptomator.presentation.presenter.AutoUploadChooseVaultPresenter
|
||||||
import org.cryptomator.presentation.ui.activity.view.AutoUploadChooseVaultView
|
import org.cryptomator.presentation.ui.activity.view.AutoUploadChooseVaultView
|
||||||
import org.cryptomator.presentation.ui.dialog.BiometricAuthKeyInvalidatedDialog
|
|
||||||
import org.cryptomator.presentation.ui.dialog.EnterPasswordDialog
|
|
||||||
import org.cryptomator.presentation.ui.dialog.NotEnoughVaultsDialog
|
import org.cryptomator.presentation.ui.dialog.NotEnoughVaultsDialog
|
||||||
import org.cryptomator.presentation.ui.fragment.AutoUploadChooseVaultFragment
|
import org.cryptomator.presentation.ui.fragment.AutoUploadChooseVaultFragment
|
||||||
import org.cryptomator.presentation.util.BiometricAuthentication
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlinx.android.synthetic.main.toolbar_layout.toolbar
|
import kotlinx.android.synthetic.main.toolbar_layout.toolbar
|
||||||
|
|
||||||
@Activity
|
@Activity
|
||||||
class AutoUploadChooseVaultActivity : BaseActivity(), //
|
class AutoUploadChooseVaultActivity : BaseActivity(), //
|
||||||
AutoUploadChooseVaultView, //
|
AutoUploadChooseVaultView, //
|
||||||
NotEnoughVaultsDialog.Callback, //
|
NotEnoughVaultsDialog.Callback {
|
||||||
EnterPasswordDialog.Callback,
|
|
||||||
BiometricAuthentication.Callback {
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var presenter: AutoUploadChooseVaultPresenter
|
lateinit var presenter: AutoUploadChooseVaultPresenter
|
||||||
@ -40,7 +33,7 @@ class AutoUploadChooseVaultActivity : BaseActivity(), //
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createFragment(): Fragment? = AutoUploadChooseVaultFragment()
|
override fun createFragment(): Fragment = AutoUploadChooseVaultFragment()
|
||||||
|
|
||||||
|
|
||||||
override fun displayVaults(vaults: List<VaultModel>) {
|
override fun displayVaults(vaults: List<VaultModel>) {
|
||||||
@ -69,41 +62,5 @@ class AutoUploadChooseVaultActivity : BaseActivity(), //
|
|||||||
autoUploadChooseVaultFragment().showChosenLocation(location)
|
autoUploadChooseVaultFragment().showChosenLocation(location)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onUnlockCanceled() {
|
|
||||||
presenter.onUnlockCanceled()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onUnlockClick(vaultModel: VaultModel, password: String) {
|
|
||||||
presenter.onUnlockPressed(vaultModel, password)
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
|
||||||
override fun showEnterPasswordDialog(vaultModel: VaultModel) {
|
|
||||||
if (vaultWithBiometricAuthEnabled(vaultModel)) {
|
|
||||||
BiometricAuthentication(this, context(), BiometricAuthentication.CryptoMode.DECRYPT, presenter.useConfirmationInFaceUnlockBiometricAuthentication())
|
|
||||||
.startListening(autoUploadChooseVaultFragment(), vaultModel)
|
|
||||||
} else {
|
|
||||||
showDialog(EnterPasswordDialog.newInstance(vaultModel))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBiometricAuthenticated(vault: VaultModel) {
|
|
||||||
presenter.onUnlockPressed(vault, vault.password)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBiometricAuthenticationFailed(vault: VaultModel) {
|
|
||||||
showDialog(EnterPasswordDialog.newInstance(vault))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBiometricKeyInvalidated(vault: VaultModel) {
|
|
||||||
presenter.onBiometricKeyInvalidated(vault)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun showBiometricAuthKeyInvalidatedDialog() {
|
|
||||||
showDialog(BiometricAuthKeyInvalidatedDialog.newInstance())
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun vaultWithBiometricAuthEnabled(vault: VaultModel): Boolean = vault.password != null
|
|
||||||
|
|
||||||
private fun autoUploadChooseVaultFragment(): AutoUploadChooseVaultFragment = getCurrentFragment(R.id.fragmentContainer) as AutoUploadChooseVaultFragment
|
private fun autoUploadChooseVaultFragment(): AutoUploadChooseVaultFragment = getCurrentFragment(R.id.fragmentContainer) as AutoUploadChooseVaultFragment
|
||||||
}
|
}
|
||||||
|
@ -1,28 +1,20 @@
|
|||||||
package org.cryptomator.presentation.ui.activity
|
package org.cryptomator.presentation.ui.activity
|
||||||
|
|
||||||
import android.os.Build
|
|
||||||
import androidx.annotation.RequiresApi
|
|
||||||
import androidx.biometric.BiometricManager
|
import androidx.biometric.BiometricManager
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import org.cryptomator.domain.Vault
|
|
||||||
import org.cryptomator.generator.Activity
|
import org.cryptomator.generator.Activity
|
||||||
import org.cryptomator.presentation.R
|
import org.cryptomator.presentation.R
|
||||||
import org.cryptomator.presentation.model.VaultModel
|
import org.cryptomator.presentation.model.VaultModel
|
||||||
import org.cryptomator.presentation.presenter.BiometricAuthSettingsPresenter
|
import org.cryptomator.presentation.presenter.BiometricAuthSettingsPresenter
|
||||||
import org.cryptomator.presentation.ui.activity.view.BiometricAuthSettingsView
|
import org.cryptomator.presentation.ui.activity.view.BiometricAuthSettingsView
|
||||||
import org.cryptomator.presentation.ui.dialog.BiometricAuthKeyInvalidatedDialog
|
|
||||||
import org.cryptomator.presentation.ui.dialog.EnrollSystemBiometricDialog
|
import org.cryptomator.presentation.ui.dialog.EnrollSystemBiometricDialog
|
||||||
import org.cryptomator.presentation.ui.dialog.EnterPasswordDialog
|
|
||||||
import org.cryptomator.presentation.ui.fragment.BiometricAuthSettingsFragment
|
import org.cryptomator.presentation.ui.fragment.BiometricAuthSettingsFragment
|
||||||
import org.cryptomator.presentation.util.BiometricAuthentication
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlinx.android.synthetic.main.toolbar_layout.toolbar
|
import kotlinx.android.synthetic.main.toolbar_layout.toolbar
|
||||||
|
|
||||||
@Activity
|
@Activity
|
||||||
class BiometricAuthSettingsActivity : BaseActivity(), //
|
class BiometricAuthSettingsActivity : BaseActivity(), //
|
||||||
EnterPasswordDialog.Callback, //
|
|
||||||
BiometricAuthSettingsView, //
|
BiometricAuthSettingsView, //
|
||||||
BiometricAuthentication.Callback, //
|
|
||||||
EnrollSystemBiometricDialog.Callback {
|
EnrollSystemBiometricDialog.Callback {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@ -42,10 +34,6 @@ class BiometricAuthSettingsActivity : BaseActivity(), //
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showBiometricAuthKeyInvalidatedDialog() {
|
|
||||||
showDialog(BiometricAuthKeyInvalidatedDialog.newInstance())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun createFragment(): Fragment? = BiometricAuthSettingsFragment()
|
override fun createFragment(): Fragment? = BiometricAuthSettingsFragment()
|
||||||
|
|
||||||
override fun renderVaultList(vaultModelCollection: List<VaultModel>) {
|
override fun renderVaultList(vaultModelCollection: List<VaultModel>) {
|
||||||
@ -56,30 +44,6 @@ class BiometricAuthSettingsActivity : BaseActivity(), //
|
|||||||
biometricAuthSettingsFragment().clearVaultList()
|
biometricAuthSettingsFragment().clearVaultList()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showEnterPasswordDialog(vaultModel: VaultModel) {
|
|
||||||
showDialog(EnterPasswordDialog.newInstance(vaultModel))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onUnlockClick(vaultModel: VaultModel, password: String) {
|
|
||||||
val vaultModelWithSavedPassword = VaultModel( //
|
|
||||||
Vault //
|
|
||||||
.aCopyOf(vaultModel.toVault()) //
|
|
||||||
.withSavedPassword(password) //
|
|
||||||
.build())
|
|
||||||
|
|
||||||
presenter.verifyPassword(vaultModelWithSavedPassword)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onUnlockCanceled() {
|
|
||||||
presenter.onUnlockCanceled()
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
|
||||||
override fun showBiometricAuthenticationDialog(vaultModel: VaultModel) {
|
|
||||||
BiometricAuthentication(this, context(), BiometricAuthentication.CryptoMode.ENCRYPT, presenter.useConfirmationInFaceUnlockBiometricAuthentication())
|
|
||||||
.startListening(biometricAuthSettingsFragment(), vaultModel)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun biometricAuthSettingsFragment(): BiometricAuthSettingsFragment = getCurrentFragment(R.id.fragmentContainer) as BiometricAuthSettingsFragment
|
private fun biometricAuthSettingsFragment(): BiometricAuthSettingsFragment = getCurrentFragment(R.id.fragmentContainer) as BiometricAuthSettingsFragment
|
||||||
|
|
||||||
override fun onSetupBiometricAuthInSystemClicked() {
|
override fun onSetupBiometricAuthInSystemClicked() {
|
||||||
@ -89,17 +53,4 @@ class BiometricAuthSettingsActivity : BaseActivity(), //
|
|||||||
override fun onCancelSetupBiometricAuthInSystemClicked() {
|
override fun onCancelSetupBiometricAuthInSystemClicked() {
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBiometricAuthenticated(vault: VaultModel) {
|
|
||||||
presenter.saveVault(vault.toVault())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBiometricAuthenticationFailed(vault: VaultModel) {
|
|
||||||
showError(getString(R.string.error_biometric_auth_aborted))
|
|
||||||
biometricAuthSettingsFragment().addOrUpdateVault(vault)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBiometricKeyInvalidated(vault: VaultModel) {
|
|
||||||
presenter.onBiometricAuthKeyInvalidated(vault)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -5,8 +5,6 @@ import android.content.Intent
|
|||||||
import android.content.Intent.ACTION_SEND
|
import android.content.Intent.ACTION_SEND
|
||||||
import android.content.Intent.ACTION_SEND_MULTIPLE
|
import android.content.Intent.ACTION_SEND_MULTIPLE
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
|
||||||
import androidx.annotation.RequiresApi
|
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import org.cryptomator.generator.Activity
|
import org.cryptomator.generator.Activity
|
||||||
import org.cryptomator.presentation.R
|
import org.cryptomator.presentation.R
|
||||||
@ -16,13 +14,10 @@ import org.cryptomator.presentation.model.SharedFileModel
|
|||||||
import org.cryptomator.presentation.model.VaultModel
|
import org.cryptomator.presentation.model.VaultModel
|
||||||
import org.cryptomator.presentation.presenter.SharedFilesPresenter
|
import org.cryptomator.presentation.presenter.SharedFilesPresenter
|
||||||
import org.cryptomator.presentation.ui.activity.view.SharedFilesView
|
import org.cryptomator.presentation.ui.activity.view.SharedFilesView
|
||||||
import org.cryptomator.presentation.ui.dialog.BiometricAuthKeyInvalidatedDialog
|
|
||||||
import org.cryptomator.presentation.ui.dialog.EnterPasswordDialog
|
|
||||||
import org.cryptomator.presentation.ui.dialog.NotEnoughVaultsDialog
|
import org.cryptomator.presentation.ui.dialog.NotEnoughVaultsDialog
|
||||||
import org.cryptomator.presentation.ui.dialog.ReplaceDialog
|
import org.cryptomator.presentation.ui.dialog.ReplaceDialog
|
||||||
import org.cryptomator.presentation.ui.dialog.UploadCloudFileDialog
|
import org.cryptomator.presentation.ui.dialog.UploadCloudFileDialog
|
||||||
import org.cryptomator.presentation.ui.fragment.SharedFilesFragment
|
import org.cryptomator.presentation.ui.fragment.SharedFilesFragment
|
||||||
import org.cryptomator.presentation.util.BiometricAuthentication
|
|
||||||
import java.lang.String.format
|
import java.lang.String.format
|
||||||
import java.util.ArrayList
|
import java.util.ArrayList
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -32,8 +27,6 @@ import timber.log.Timber
|
|||||||
@Activity
|
@Activity
|
||||||
class SharedFilesActivity : BaseActivity(), //
|
class SharedFilesActivity : BaseActivity(), //
|
||||||
SharedFilesView, //
|
SharedFilesView, //
|
||||||
EnterPasswordDialog.Callback, //
|
|
||||||
BiometricAuthentication.Callback, //
|
|
||||||
ReplaceDialog.Callback, //
|
ReplaceDialog.Callback, //
|
||||||
NotEnoughVaultsDialog.Callback, //
|
NotEnoughVaultsDialog.Callback, //
|
||||||
UploadCloudFileDialog.Callback {
|
UploadCloudFileDialog.Callback {
|
||||||
@ -126,7 +119,7 @@ class SharedFilesActivity : BaseActivity(), //
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createFragment(): Fragment? = SharedFilesFragment()
|
override fun createFragment(): Fragment = SharedFilesFragment()
|
||||||
|
|
||||||
public override fun onMenuItemSelected(itemId: Int): Boolean = when (itemId) {
|
public override fun onMenuItemSelected(itemId: Int): Boolean = when (itemId) {
|
||||||
android.R.id.home -> {
|
android.R.id.home -> {
|
||||||
@ -150,16 +143,6 @@ class SharedFilesActivity : BaseActivity(), //
|
|||||||
|
|
||||||
private fun sharedFilesFragment(): SharedFilesFragment = getCurrentFragment(R.id.fragmentContainer) as SharedFilesFragment
|
private fun sharedFilesFragment(): SharedFilesFragment = getCurrentFragment(R.id.fragmentContainer) as SharedFilesFragment
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
|
||||||
override fun showEnterPasswordDialog(vault: VaultModel) {
|
|
||||||
if (vaultWithBiometricAuthEnabled(vault)) {
|
|
||||||
BiometricAuthentication(this, context(), BiometricAuthentication.CryptoMode.DECRYPT, presenter.useConfirmationInFaceUnlockBiometricAuthentication())
|
|
||||||
.startListening(sharedFilesFragment(), vault)
|
|
||||||
} else {
|
|
||||||
showDialog(EnterPasswordDialog.newInstance(vault))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun showReplaceDialog(existingFiles: List<String>, size: Int) {
|
override fun showReplaceDialog(existingFiles: List<String>, size: Int) {
|
||||||
ReplaceDialog.withContext(this).show(existingFiles, size)
|
ReplaceDialog.withContext(this).show(existingFiles, size)
|
||||||
}
|
}
|
||||||
@ -168,22 +151,10 @@ class SharedFilesActivity : BaseActivity(), //
|
|||||||
sharedFilesFragment().showChosenLocation(folder)
|
sharedFilesFragment().showChosenLocation(folder)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showBiometricAuthKeyInvalidatedDialog() {
|
|
||||||
showDialog(BiometricAuthKeyInvalidatedDialog.newInstance())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun showUploadDialog(uploadingFiles: Int) {
|
override fun showUploadDialog(uploadingFiles: Int) {
|
||||||
showDialog(UploadCloudFileDialog.newInstance(uploadingFiles))
|
showDialog(UploadCloudFileDialog.newInstance(uploadingFiles))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onUnlockClick(vaultModel: VaultModel, password: String) {
|
|
||||||
presenter.onUnlockPressed(vaultModel, password)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onUnlockCanceled() {
|
|
||||||
presenter.onUnlockCanceled()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onReplacePositiveClicked() {
|
override fun onReplacePositiveClicked() {
|
||||||
presenter.onReplaceExistingFilesPressed()
|
presenter.onReplaceExistingFilesPressed()
|
||||||
}
|
}
|
||||||
@ -209,20 +180,6 @@ class SharedFilesActivity : BaseActivity(), //
|
|||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBiometricAuthenticated(vault: VaultModel) {
|
|
||||||
presenter.onUnlockPressed(vault, vault.password)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBiometricAuthenticationFailed(vault: VaultModel) {
|
|
||||||
showDialog(EnterPasswordDialog.newInstance(vault))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBiometricKeyInvalidated(vault: VaultModel) {
|
|
||||||
presenter.onBiometricAuthKeyInvalidated()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun vaultWithBiometricAuthEnabled(vault: VaultModel): Boolean = vault.password != null
|
|
||||||
|
|
||||||
override fun onUploadCanceled() {
|
override fun onUploadCanceled() {
|
||||||
presenter.onUploadCanceled()
|
presenter.onUploadCanceled()
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,109 @@
|
|||||||
|
package org.cryptomator.presentation.ui.activity
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
|
import org.cryptomator.domain.Vault
|
||||||
|
import org.cryptomator.generator.Activity
|
||||||
|
import org.cryptomator.generator.InjectIntent
|
||||||
|
import org.cryptomator.presentation.R
|
||||||
|
import org.cryptomator.presentation.intent.UnlockVaultIntent
|
||||||
|
import org.cryptomator.presentation.model.VaultModel
|
||||||
|
import org.cryptomator.presentation.presenter.UnlockVaultPresenter
|
||||||
|
import org.cryptomator.presentation.ui.activity.view.UnlockVaultView
|
||||||
|
import org.cryptomator.presentation.ui.dialog.BiometricAuthKeyInvalidatedDialog
|
||||||
|
import org.cryptomator.presentation.ui.dialog.ChangePasswordDialog
|
||||||
|
import org.cryptomator.presentation.ui.dialog.EnterPasswordDialog
|
||||||
|
import org.cryptomator.presentation.ui.fragment.UnlockVaultFragment
|
||||||
|
import org.cryptomator.presentation.util.BiometricAuthentication
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@Activity(layout = R.layout.activity_unlock_vault)
|
||||||
|
class UnlockVaultActivity : BaseActivity(), //
|
||||||
|
UnlockVaultView, //
|
||||||
|
BiometricAuthentication.Callback,
|
||||||
|
ChangePasswordDialog.Callback {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var presenter: UnlockVaultPresenter
|
||||||
|
|
||||||
|
@InjectIntent
|
||||||
|
lateinit var unlockVaultIntent: UnlockVaultIntent
|
||||||
|
|
||||||
|
|
||||||
|
private lateinit var biometricAuthentication: BiometricAuthentication
|
||||||
|
|
||||||
|
override fun finish() {
|
||||||
|
super.finish()
|
||||||
|
overridePendingTransition(0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showEnterPasswordDialog(vault: VaultModel) {
|
||||||
|
showDialog(EnterPasswordDialog.newInstance(vault))
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||||
|
override fun showBiometricDialog(vault: VaultModel) {
|
||||||
|
biometricAuthentication = BiometricAuthentication(this, context(), BiometricAuthentication.CryptoMode.DECRYPT, presenter.useConfirmationInFaceUnlockBiometricAuthentication())
|
||||||
|
biometricAuthentication.startListening(unlockVaultFragment(), vault)
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||||
|
override fun getEncryptedPasswordWithBiometricAuthentication(vaultModel: VaultModel) {
|
||||||
|
biometricAuthentication = BiometricAuthentication(this, context(), BiometricAuthentication.CryptoMode.ENCRYPT, presenter.useConfirmationInFaceUnlockBiometricAuthentication())
|
||||||
|
biometricAuthentication.startListening(unlockVaultFragment(), vaultModel)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showBiometricAuthKeyInvalidatedDialog() {
|
||||||
|
showDialog(BiometricAuthKeyInvalidatedDialog.newInstance())
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||||
|
override fun cancelBasicAuthIfRunning() {
|
||||||
|
biometricAuthentication.stopListening()
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||||
|
override fun stoppedBiometricAuthDuringCloudAuthentication(): Boolean {
|
||||||
|
return biometricAuthentication.stoppedBiometricAuthDuringCloudAuthentication()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onUnlockClick(vaultModel: VaultModel, password: String) {
|
||||||
|
presenter.onUnlockClick(vaultModel, password)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onUnlockCanceled() {
|
||||||
|
presenter.onUnlockCanceled()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBiometricAuthenticated(vault: VaultModel) {
|
||||||
|
presenter.onBiometricAuthenticationSucceeded(vault)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBiometricAuthenticationFailed(vault: VaultModel) {
|
||||||
|
val vaultWithoutPassword = Vault.aCopyOf(vault.toVault()).withSavedPassword(null).build()
|
||||||
|
when(unlockVaultIntent.vaultAction()) {
|
||||||
|
UnlockVaultIntent.VaultAction.CHANGE_PASSWORD -> presenter.saveVaultAfterChangePasswordButFailedBiometricAuth(vaultWithoutPassword)
|
||||||
|
else -> {
|
||||||
|
if (!presenter.startedUsingPrepareUnlock()) {
|
||||||
|
presenter.startPrepareUnlockUseCase(vaultWithoutPassword)
|
||||||
|
}
|
||||||
|
showEnterPasswordDialog(VaultModel(vaultWithoutPassword))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBiometricKeyInvalidated(vault: VaultModel) {
|
||||||
|
presenter.onBiometricKeyInvalidated()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun unlockVaultFragment(): UnlockVaultFragment = //
|
||||||
|
getCurrentFragment(R.id.fragmentContainer) as UnlockVaultFragment
|
||||||
|
|
||||||
|
override fun showChangePasswordDialog(vaultModel: VaultModel) {
|
||||||
|
showDialog(ChangePasswordDialog.newInstance(vaultModel))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onChangePasswordClick(vaultModel: VaultModel, oldPassword: String, newPassword: String) {
|
||||||
|
presenter.onChangePasswordClick(vaultModel, oldPassword, newPassword)
|
||||||
|
}
|
||||||
|
}
|
@ -2,9 +2,7 @@ package org.cryptomator.presentation.ui.activity
|
|||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.annotation.RequiresApi
|
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import org.cryptomator.domain.Vault
|
import org.cryptomator.domain.Vault
|
||||||
import org.cryptomator.generator.Activity
|
import org.cryptomator.generator.Activity
|
||||||
@ -25,9 +23,6 @@ import org.cryptomator.presentation.ui.bottomsheet.SettingsVaultBottomSheet
|
|||||||
import org.cryptomator.presentation.ui.callback.VaultListCallback
|
import org.cryptomator.presentation.ui.callback.VaultListCallback
|
||||||
import org.cryptomator.presentation.ui.dialog.AskForLockScreenDialog
|
import org.cryptomator.presentation.ui.dialog.AskForLockScreenDialog
|
||||||
import org.cryptomator.presentation.ui.dialog.BetaConfirmationDialog
|
import org.cryptomator.presentation.ui.dialog.BetaConfirmationDialog
|
||||||
import org.cryptomator.presentation.ui.dialog.BiometricAuthKeyInvalidatedDialog
|
|
||||||
import org.cryptomator.presentation.ui.dialog.ChangePasswordDialog
|
|
||||||
import org.cryptomator.presentation.ui.dialog.EnterPasswordDialog
|
|
||||||
import org.cryptomator.presentation.ui.dialog.UpdateAppAvailableDialog
|
import org.cryptomator.presentation.ui.dialog.UpdateAppAvailableDialog
|
||||||
import org.cryptomator.presentation.ui.dialog.UpdateAppDialog
|
import org.cryptomator.presentation.ui.dialog.UpdateAppDialog
|
||||||
import org.cryptomator.presentation.ui.dialog.VaultDeleteConfirmationDialog
|
import org.cryptomator.presentation.ui.dialog.VaultDeleteConfirmationDialog
|
||||||
@ -35,7 +30,6 @@ import org.cryptomator.presentation.ui.dialog.VaultNotFoundDialog
|
|||||||
import org.cryptomator.presentation.ui.dialog.VaultRenameDialog
|
import org.cryptomator.presentation.ui.dialog.VaultRenameDialog
|
||||||
import org.cryptomator.presentation.ui.fragment.VaultListFragment
|
import org.cryptomator.presentation.ui.fragment.VaultListFragment
|
||||||
import org.cryptomator.presentation.ui.layout.ObscuredAwareCoordinatorLayout.Listener
|
import org.cryptomator.presentation.ui.layout.ObscuredAwareCoordinatorLayout.Listener
|
||||||
import org.cryptomator.presentation.util.BiometricAuthentication
|
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlinx.android.synthetic.main.activity_layout_obscure_aware.activityRootView
|
import kotlinx.android.synthetic.main.activity_layout_obscure_aware.activityRootView
|
||||||
@ -45,10 +39,8 @@ import kotlinx.android.synthetic.main.toolbar_layout.toolbar
|
|||||||
class VaultListActivity : BaseActivity(), //
|
class VaultListActivity : BaseActivity(), //
|
||||||
VaultListView, //
|
VaultListView, //
|
||||||
VaultListCallback, //
|
VaultListCallback, //
|
||||||
BiometricAuthentication.Callback, //
|
|
||||||
AskForLockScreenDialog.Callback, //
|
AskForLockScreenDialog.Callback, //
|
||||||
ChangePasswordDialog.Callback, //
|
VaultNotFoundDialog.Callback, //
|
||||||
VaultNotFoundDialog.Callback,
|
|
||||||
UpdateAppAvailableDialog.Callback, //
|
UpdateAppAvailableDialog.Callback, //
|
||||||
UpdateAppDialog.Callback, //
|
UpdateAppDialog.Callback, //
|
||||||
BetaConfirmationDialog.Callback {
|
BetaConfirmationDialog.Callback {
|
||||||
@ -59,8 +51,6 @@ class VaultListActivity : BaseActivity(), //
|
|||||||
@InjectIntent
|
@InjectIntent
|
||||||
lateinit var vaultListIntent: VaultListIntent
|
lateinit var vaultListIntent: VaultListIntent
|
||||||
|
|
||||||
private var biometricAuthentication: BiometricAuthentication? = null
|
|
||||||
|
|
||||||
override fun onWindowFocusChanged(hasFocus: Boolean) {
|
override fun onWindowFocusChanged(hasFocus: Boolean) {
|
||||||
super.onWindowFocusChanged(hasFocus)
|
super.onWindowFocusChanged(hasFocus)
|
||||||
vaultListPresenter.onWindowFocusChanged(hasFocus)
|
vaultListPresenter.onWindowFocusChanged(hasFocus)
|
||||||
@ -125,40 +115,6 @@ class VaultListActivity : BaseActivity(), //
|
|||||||
showDialog(VaultRenameDialog.newInstance(vaultModel))
|
showDialog(VaultRenameDialog.newInstance(vaultModel))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showEnterPasswordDialog(vault: VaultModel) {
|
|
||||||
showDialog(EnterPasswordDialog.newInstance(vault))
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
|
||||||
override fun showBiometricDialog(vault: VaultModel) {
|
|
||||||
biometricAuthentication = BiometricAuthentication(this, context(), BiometricAuthentication.CryptoMode.DECRYPT, vaultListPresenter.useConfirmationInFaceUnlockBiometricAuthentication())
|
|
||||||
biometricAuthentication?.startListening(vaultListFragment(), vault)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun showChangePasswordDialog(vaultModel: VaultModel) {
|
|
||||||
showDialog(ChangePasswordDialog.newInstance(vaultModel))
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
|
||||||
override fun getEncryptedPasswordWithBiometricAuthentication(vaultModel: VaultModel) {
|
|
||||||
biometricAuthentication = BiometricAuthentication(this, context(), BiometricAuthentication.CryptoMode.ENCRYPT, vaultListPresenter.useConfirmationInFaceUnlockBiometricAuthentication())
|
|
||||||
biometricAuthentication?.startListening(vaultListFragment(), vaultModel)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun showBiometricAuthKeyInvalidatedDialog() {
|
|
||||||
showDialog(BiometricAuthKeyInvalidatedDialog.newInstance())
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
|
||||||
override fun cancelBasicAuthIfRunning() {
|
|
||||||
biometricAuthentication?.stopListening()
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
|
||||||
override fun stoppedBiometricAuthDuringCloudAuthentication(): Boolean {
|
|
||||||
return biometricAuthentication?.stoppedBiometricAuthDuringCloudAuthentication() == true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun rowMoved(fromPosition: Int, toPosition: Int) {
|
override fun rowMoved(fromPosition: Int, toPosition: Int) {
|
||||||
vaultListFragment().rowMoved(fromPosition, toPosition)
|
vaultListFragment().rowMoved(fromPosition, toPosition)
|
||||||
}
|
}
|
||||||
@ -209,14 +165,6 @@ class VaultListActivity : BaseActivity(), //
|
|||||||
vaultListPresenter.onCreateVault()
|
vaultListPresenter.onCreateVault()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onUnlockClick(vaultModel: VaultModel, password: String) {
|
|
||||||
vaultListPresenter.onUnlockClick(vaultModel, password)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onUnlockCanceled() {
|
|
||||||
vaultListPresenter.onUnlockCanceled()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDeleteVaultClick(vaultModel: VaultModel) {
|
override fun onDeleteVaultClick(vaultModel: VaultModel) {
|
||||||
VaultDeleteConfirmationDialog.newInstance(vaultModel) //
|
VaultDeleteConfirmationDialog.newInstance(vaultModel) //
|
||||||
.show(supportFragmentManager, "VaultDeleteConfirmationDialog")
|
.show(supportFragmentManager, "VaultDeleteConfirmationDialog")
|
||||||
@ -249,10 +197,6 @@ class VaultListActivity : BaseActivity(), //
|
|||||||
private fun vaultListFragment(): VaultListFragment = //
|
private fun vaultListFragment(): VaultListFragment = //
|
||||||
getCurrentFragment(R.id.fragmentContainer) as VaultListFragment
|
getCurrentFragment(R.id.fragmentContainer) as VaultListFragment
|
||||||
|
|
||||||
override fun onChangePasswordClick(vaultModel: VaultModel, oldPassword: String, newPassword: String) {
|
|
||||||
vaultListPresenter.onChangePasswordClicked(vaultModel, oldPassword, newPassword)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDeleteMissingVaultClicked(vault: Vault) {
|
override fun onDeleteMissingVaultClicked(vault: Vault) {
|
||||||
vaultListPresenter.onDeleteMissingVaultClicked(vault)
|
vaultListPresenter.onDeleteMissingVaultClicked(vault)
|
||||||
}
|
}
|
||||||
@ -279,21 +223,4 @@ class VaultListActivity : BaseActivity(), //
|
|||||||
override fun onAskForBetaConfirmationFinished() {
|
override fun onAskForBetaConfirmationFinished() {
|
||||||
sharedPreferencesHandler.setBetaScreenDialogAlreadyShown()
|
sharedPreferencesHandler.setBetaScreenDialogAlreadyShown()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBiometricAuthenticated(vault: VaultModel) {
|
|
||||||
vaultListPresenter.onBiometricAuthenticationSucceeded(vault)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBiometricAuthenticationFailed(vault: VaultModel) {
|
|
||||||
val vaultWithoutPassword = Vault.aCopyOf(vault.toVault()).withSavedPassword(null).build()
|
|
||||||
if (!vaultListPresenter.startedUsingPrepareUnlock()) {
|
|
||||||
vaultListPresenter.startPrepareUnlockUseCase(vaultWithoutPassword)
|
|
||||||
}
|
|
||||||
showEnterPasswordDialog(VaultModel(vaultWithoutPassword))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBiometricKeyInvalidated(vault: VaultModel) {
|
|
||||||
vaultListPresenter.onBiometricKeyInvalidated()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,5 @@ interface AutoUploadChooseVaultView : View {
|
|||||||
fun displayDialogUnableToUploadFiles()
|
fun displayDialogUnableToUploadFiles()
|
||||||
fun displayVaults(vaults: List<VaultModel>)
|
fun displayVaults(vaults: List<VaultModel>)
|
||||||
fun showChosenLocation(location: CloudFolderModel)
|
fun showChosenLocation(location: CloudFolderModel)
|
||||||
fun showEnterPasswordDialog(vaultModel: VaultModel)
|
|
||||||
fun showBiometricAuthKeyInvalidatedDialog()
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,6 @@ interface BiometricAuthSettingsView : View {
|
|||||||
|
|
||||||
fun renderVaultList(vaultModelCollection: List<VaultModel>)
|
fun renderVaultList(vaultModelCollection: List<VaultModel>)
|
||||||
fun clearVaultList()
|
fun clearVaultList()
|
||||||
fun showBiometricAuthenticationDialog(vaultModel: VaultModel)
|
|
||||||
fun showEnterPasswordDialog(vaultModel: VaultModel)
|
|
||||||
fun showSetupBiometricAuthDialog()
|
fun showSetupBiometricAuthDialog()
|
||||||
fun showBiometricAuthKeyInvalidatedDialog()
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -11,10 +11,8 @@ interface SharedFilesView : View {
|
|||||||
fun displayVaults(vaults: List<VaultModel>)
|
fun displayVaults(vaults: List<VaultModel>)
|
||||||
fun displayFilesToUpload(sharedFiles: List<SharedFileModel>)
|
fun displayFilesToUpload(sharedFiles: List<SharedFileModel>)
|
||||||
fun displayDialogUnableToUploadFiles()
|
fun displayDialogUnableToUploadFiles()
|
||||||
fun showEnterPasswordDialog(vault: VaultModel)
|
|
||||||
fun showReplaceDialog(existingFiles: List<String>, size: Int)
|
fun showReplaceDialog(existingFiles: List<String>, size: Int)
|
||||||
fun showChosenLocation(folder: CloudFolderModel)
|
fun showChosenLocation(folder: CloudFolderModel)
|
||||||
fun showBiometricAuthKeyInvalidatedDialog()
|
|
||||||
fun showUploadDialog(uploadingFiles: Int)
|
fun showUploadDialog(uploadingFiles: Int)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
package org.cryptomator.presentation.ui.activity.view
|
||||||
|
|
||||||
|
import org.cryptomator.presentation.model.VaultModel
|
||||||
|
import org.cryptomator.presentation.ui.dialog.EnterPasswordDialog
|
||||||
|
|
||||||
|
interface UnlockVaultView : View, EnterPasswordDialog.Callback {
|
||||||
|
|
||||||
|
fun showEnterPasswordDialog(vault: VaultModel)
|
||||||
|
fun showBiometricDialog(vault: VaultModel)
|
||||||
|
fun getEncryptedPasswordWithBiometricAuthentication(vaultModel: VaultModel)
|
||||||
|
fun showBiometricAuthKeyInvalidatedDialog()
|
||||||
|
fun cancelBasicAuthIfRunning()
|
||||||
|
fun stoppedBiometricAuthDuringCloudAuthentication(): Boolean
|
||||||
|
fun showChangePasswordDialog(vaultModel: VaultModel)
|
||||||
|
|
||||||
|
}
|
@ -12,17 +12,10 @@ interface VaultListView : View {
|
|||||||
fun addOrUpdateVault(vault: VaultModel)
|
fun addOrUpdateVault(vault: VaultModel)
|
||||||
fun renameVault(vaultModel: VaultModel)
|
fun renameVault(vaultModel: VaultModel)
|
||||||
fun navigateToVaultContent(vault: VaultModel, decryptedRoot: CloudFolderModel)
|
fun navigateToVaultContent(vault: VaultModel, decryptedRoot: CloudFolderModel)
|
||||||
fun showEnterPasswordDialog(vault: VaultModel)
|
|
||||||
fun showBiometricDialog(vault: VaultModel)
|
|
||||||
fun showChangePasswordDialog(vaultModel: VaultModel)
|
|
||||||
fun getEncryptedPasswordWithBiometricAuthentication(vaultModel: VaultModel)
|
|
||||||
fun showVaultSettingsDialog(vaultModel: VaultModel)
|
fun showVaultSettingsDialog(vaultModel: VaultModel)
|
||||||
fun showAddVaultBottomSheet()
|
fun showAddVaultBottomSheet()
|
||||||
fun showRenameDialog(vaultModel: VaultModel)
|
fun showRenameDialog(vaultModel: VaultModel)
|
||||||
fun showBiometricAuthKeyInvalidatedDialog()
|
|
||||||
fun isVaultLocked(vaultModel: VaultModel): Boolean
|
fun isVaultLocked(vaultModel: VaultModel): Boolean
|
||||||
fun cancelBasicAuthIfRunning()
|
|
||||||
fun stoppedBiometricAuthDuringCloudAuthentication(): Boolean
|
|
||||||
fun rowMoved(fromPosition: Int, toPosition: Int)
|
fun rowMoved(fromPosition: Int, toPosition: Int)
|
||||||
fun vaultMoved(vaults: List<VaultModel>)
|
fun vaultMoved(vaults: List<VaultModel>)
|
||||||
|
|
||||||
|
@ -2,8 +2,11 @@ package org.cryptomator.presentation.ui.callback
|
|||||||
|
|
||||||
import org.cryptomator.presentation.ui.bottomsheet.AddVaultBottomSheet
|
import org.cryptomator.presentation.ui.bottomsheet.AddVaultBottomSheet
|
||||||
import org.cryptomator.presentation.ui.bottomsheet.SettingsVaultBottomSheet
|
import org.cryptomator.presentation.ui.bottomsheet.SettingsVaultBottomSheet
|
||||||
import org.cryptomator.presentation.ui.dialog.EnterPasswordDialog
|
|
||||||
import org.cryptomator.presentation.ui.dialog.VaultDeleteConfirmationDialog
|
import org.cryptomator.presentation.ui.dialog.VaultDeleteConfirmationDialog
|
||||||
import org.cryptomator.presentation.ui.dialog.VaultRenameDialog
|
import org.cryptomator.presentation.ui.dialog.VaultRenameDialog
|
||||||
|
|
||||||
interface VaultListCallback : AddVaultBottomSheet.Callback, EnterPasswordDialog.Callback, SettingsVaultBottomSheet.Callback, VaultDeleteConfirmationDialog.Callback, VaultRenameDialog.Callback
|
// FIXME delete this file and add this interfaces to VaultListView.kt
|
||||||
|
interface VaultListCallback : AddVaultBottomSheet.Callback, //
|
||||||
|
SettingsVaultBottomSheet.Callback, //
|
||||||
|
VaultDeleteConfirmationDialog.Callback, //
|
||||||
|
VaultRenameDialog.Callback
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
package org.cryptomator.presentation.ui.fragment
|
||||||
|
|
||||||
|
import org.cryptomator.generator.Fragment
|
||||||
|
import org.cryptomator.presentation.R
|
||||||
|
import org.cryptomator.presentation.presenter.UnlockVaultPresenter
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@Fragment(R.layout.fragment_unlock_vault)
|
||||||
|
class UnlockVaultFragment : BaseFragment() {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var presenter: UnlockVaultPresenter
|
||||||
|
|
||||||
|
override fun setupView() {
|
||||||
|
presenter.setup()
|
||||||
|
}
|
||||||
|
}
|
15
presentation/src/main/res/layout/activity_unlock_vault.xml
Normal file
15
presentation/src/main/res/layout/activity_unlock_vault.xml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/activityRootView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<androidx.fragment.app.FragmentContainerView
|
||||||
|
android:id="@+id/fragmentContainer"
|
||||||
|
android:name="org.cryptomator.presentation.ui.fragment.UnlockVaultFragment"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:tag="UnlockVaultFragment" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
</LinearLayout>
|
@ -29,6 +29,10 @@
|
|||||||
<item name="colorAccent">@color/colorPrimary</item>
|
<item name="colorAccent">@color/colorPrimary</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="TransparentAlertDialogCustom" parent="AlertDialogCustom">
|
||||||
|
<item name="android:windowBackground">@android:color/transparent</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
<style name="Toolbar.Theme" parent="ThemeOverlay.AppCompat.Dark.ActionBar">
|
<style name="Toolbar.Theme" parent="ThemeOverlay.AppCompat.Dark.ActionBar">
|
||||||
<item name="colorAccent">@color/textColorWhite</item>
|
<item name="colorAccent">@color/textColorWhite</item>
|
||||||
</style>
|
</style>
|
||||||
|
@ -13,17 +13,13 @@ import org.cryptomator.domain.usecases.DoUpdateUseCase;
|
|||||||
import org.cryptomator.domain.usecases.GetDecryptedCloudForVaultUseCase;
|
import org.cryptomator.domain.usecases.GetDecryptedCloudForVaultUseCase;
|
||||||
import org.cryptomator.domain.usecases.ResultHandler;
|
import org.cryptomator.domain.usecases.ResultHandler;
|
||||||
import org.cryptomator.domain.usecases.cloud.GetRootFolderUseCase;
|
import org.cryptomator.domain.usecases.cloud.GetRootFolderUseCase;
|
||||||
import org.cryptomator.domain.usecases.vault.ChangePasswordUseCase;
|
|
||||||
import org.cryptomator.domain.usecases.vault.DeleteVaultUseCase;
|
import org.cryptomator.domain.usecases.vault.DeleteVaultUseCase;
|
||||||
import org.cryptomator.domain.usecases.vault.GetVaultListUseCase;
|
import org.cryptomator.domain.usecases.vault.GetVaultListUseCase;
|
||||||
import org.cryptomator.domain.usecases.vault.LockVaultUseCase;
|
import org.cryptomator.domain.usecases.vault.LockVaultUseCase;
|
||||||
import org.cryptomator.domain.usecases.vault.MoveVaultPositionUseCase;
|
import org.cryptomator.domain.usecases.vault.MoveVaultPositionUseCase;
|
||||||
import org.cryptomator.domain.usecases.vault.PrepareUnlockUseCase;
|
|
||||||
import org.cryptomator.domain.usecases.vault.RemoveStoredVaultPasswordsUseCase;
|
|
||||||
import org.cryptomator.domain.usecases.vault.RenameVaultUseCase;
|
import org.cryptomator.domain.usecases.vault.RenameVaultUseCase;
|
||||||
import org.cryptomator.domain.usecases.vault.SaveVaultUseCase;
|
import org.cryptomator.domain.usecases.vault.SaveVaultUseCase;
|
||||||
import org.cryptomator.domain.usecases.vault.UnlockToken;
|
import org.cryptomator.domain.usecases.vault.UnlockToken;
|
||||||
import org.cryptomator.domain.usecases.vault.UnlockVaultUseCase;
|
|
||||||
import org.cryptomator.presentation.exception.ExceptionHandlers;
|
import org.cryptomator.presentation.exception.ExceptionHandlers;
|
||||||
import org.cryptomator.presentation.model.VaultModel;
|
import org.cryptomator.presentation.model.VaultModel;
|
||||||
import org.cryptomator.presentation.model.mappers.CloudFolderModelMapper;
|
import org.cryptomator.presentation.model.mappers.CloudFolderModelMapper;
|
||||||
@ -98,17 +94,12 @@ public class VaultListPresenterTest {
|
|||||||
private LockVaultUseCase lockVaultUseCase = Mockito.mock(LockVaultUseCase.class);
|
private LockVaultUseCase lockVaultUseCase = Mockito.mock(LockVaultUseCase.class);
|
||||||
private LockVaultUseCase.Launcher lockVaultUseCaseLauncher = Mockito.mock(LockVaultUseCase.Launcher.class);
|
private LockVaultUseCase.Launcher lockVaultUseCaseLauncher = Mockito.mock(LockVaultUseCase.Launcher.class);
|
||||||
private GetDecryptedCloudForVaultUseCase getDecryptedCloudForVaultUseCase = Mockito.mock(GetDecryptedCloudForVaultUseCase.class);
|
private GetDecryptedCloudForVaultUseCase getDecryptedCloudForVaultUseCase = Mockito.mock(GetDecryptedCloudForVaultUseCase.class);
|
||||||
private PrepareUnlockUseCase prepareUnlockUseCase = Mockito.mock(PrepareUnlockUseCase.class);
|
|
||||||
private PrepareUnlockUseCase.Launcher prepareUnlockUseCaseLauncher = Mockito.mock(PrepareUnlockUseCase.Launcher.class);
|
|
||||||
private UnlockToken unlockToken = Mockito.mock(UnlockToken.class);
|
private UnlockToken unlockToken = Mockito.mock(UnlockToken.class);
|
||||||
private UnlockVaultUseCase unlockVaultUseCase = Mockito.mock(UnlockVaultUseCase.class);
|
|
||||||
private GetRootFolderUseCase getRootFolderUseCase = Mockito.mock(GetRootFolderUseCase.class);
|
private GetRootFolderUseCase getRootFolderUseCase = Mockito.mock(GetRootFolderUseCase.class);
|
||||||
private AddExistingVaultWorkflow addExistingVaultWorkflow = Mockito.mock(AddExistingVaultWorkflow.class);
|
private AddExistingVaultWorkflow addExistingVaultWorkflow = Mockito.mock(AddExistingVaultWorkflow.class);
|
||||||
private CreateNewVaultWorkflow createNewVaultWorkflow = Mockito.mock(CreateNewVaultWorkflow.class);
|
private CreateNewVaultWorkflow createNewVaultWorkflow = Mockito.mock(CreateNewVaultWorkflow.class);
|
||||||
private SaveVaultUseCase saveVaultUseCase = Mockito.mock(SaveVaultUseCase.class);
|
private SaveVaultUseCase saveVaultUseCase = Mockito.mock(SaveVaultUseCase.class);
|
||||||
private MoveVaultPositionUseCase moveVaultPositionUseCase = Mockito.mock(MoveVaultPositionUseCase.class);
|
private MoveVaultPositionUseCase moveVaultPositionUseCase = Mockito.mock(MoveVaultPositionUseCase.class);
|
||||||
private ChangePasswordUseCase changePasswordUseCase = Mockito.mock(ChangePasswordUseCase.class);
|
|
||||||
private RemoveStoredVaultPasswordsUseCase removeStoredVaultPasswordsUseCase = Mockito.mock(RemoveStoredVaultPasswordsUseCase.class);
|
|
||||||
private DoLicenseCheckUseCase doLicenceCheckUsecase = Mockito.mock(DoLicenseCheckUseCase.class);
|
private DoLicenseCheckUseCase doLicenceCheckUsecase = Mockito.mock(DoLicenseCheckUseCase.class);
|
||||||
private DoUpdateCheckUseCase updateCheckUseCase = Mockito.mock(DoUpdateCheckUseCase.class);
|
private DoUpdateCheckUseCase updateCheckUseCase = Mockito.mock(DoUpdateCheckUseCase.class);
|
||||||
private DoUpdateUseCase updateUseCase = Mockito.mock(DoUpdateUseCase.class);
|
private DoUpdateUseCase updateUseCase = Mockito.mock(DoUpdateUseCase.class);
|
||||||
@ -126,15 +117,11 @@ public class VaultListPresenterTest {
|
|||||||
renameVaultUseCase, //
|
renameVaultUseCase, //
|
||||||
lockVaultUseCase, //
|
lockVaultUseCase, //
|
||||||
getDecryptedCloudForVaultUseCase, //
|
getDecryptedCloudForVaultUseCase, //
|
||||||
prepareUnlockUseCase, //
|
|
||||||
unlockVaultUseCase, //
|
|
||||||
getRootFolderUseCase, //
|
getRootFolderUseCase, //
|
||||||
addExistingVaultWorkflow, //
|
addExistingVaultWorkflow, //
|
||||||
createNewVaultWorkflow, //
|
createNewVaultWorkflow, //
|
||||||
saveVaultUseCase, //
|
saveVaultUseCase, //
|
||||||
moveVaultPositionUseCase, //
|
moveVaultPositionUseCase, //
|
||||||
changePasswordUseCase, //
|
|
||||||
removeStoredVaultPasswordsUseCase, //
|
|
||||||
doLicenceCheckUsecase, //
|
doLicenceCheckUsecase, //
|
||||||
updateCheckUseCase, //
|
updateCheckUseCase, //
|
||||||
updateUseCase, //
|
updateUseCase, //
|
||||||
@ -241,43 +228,4 @@ public class VaultListPresenterTest {
|
|||||||
Mockito.any());
|
Mockito.any());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testOnUnlockCanceled() {
|
|
||||||
inTest.onUnlockCanceled();
|
|
||||||
|
|
||||||
verify(prepareUnlockUseCase).unsubscribe();
|
|
||||||
verify(unlockVaultUseCase).cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testOnVaultLockedClicked() {
|
|
||||||
ArgumentCaptor<ResultHandler<Vault>> captor = ArgumentCaptor.forClass(ResultHandler.class);
|
|
||||||
|
|
||||||
when(lockVaultUseCase.withVault(AN_UNLOCKED_VAULT_MODEL.toVault())).thenReturn(lockVaultUseCaseLauncher);
|
|
||||||
|
|
||||||
inTest.onVaultLockClicked(AN_UNLOCKED_VAULT_MODEL);
|
|
||||||
|
|
||||||
verify(lockVaultUseCaseLauncher).run(captor.capture());
|
|
||||||
captor.getValue().onSuccess(AN_UNLOCKED_VAULT_MODEL.toVault());
|
|
||||||
verify(vaultListView).addOrUpdateVault(AN_UNLOCKED_VAULT_MODEL);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onVaultClickedWithCloudAndLocked() {
|
|
||||||
ArgumentCaptor<ResultHandler<UnlockToken>> captor = ArgumentCaptor.forClass(ResultHandler.class);
|
|
||||||
|
|
||||||
when(prepareUnlockUseCase.withVault(ANOTHER_VAULT_MODEL_WITH_CLOUD.toVault())) //
|
|
||||||
.thenReturn(prepareUnlockUseCaseLauncher);
|
|
||||||
when(unlockToken.getVault()) //
|
|
||||||
.thenReturn(ANOTHER_VAULT_MODEL_WITH_CLOUD.toVault());
|
|
||||||
|
|
||||||
inTest.onVaultClicked(ANOTHER_VAULT_MODEL_WITH_CLOUD);
|
|
||||||
|
|
||||||
verify(prepareUnlockUseCaseLauncher).run(captor.capture());
|
|
||||||
captor.getValue().onSuccess(unlockToken);
|
|
||||||
|
|
||||||
verify(vaultListView).addOrUpdateVault(ANOTHER_VAULT_MODEL_WITH_CLOUD);
|
|
||||||
verify(vaultListView).showEnterPasswordDialog(ANOTHER_VAULT_MODEL_WITH_CLOUD);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user