Preparations for F-Droid main repo
This commit is contained in:
parent
33dfaed9d7
commit
7ad0178e8b
@ -53,19 +53,27 @@ android {
|
|||||||
fdroid {
|
fdroid {
|
||||||
dimension "version"
|
dimension "version"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fdroidmain {
|
||||||
|
dimension "version"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
playstore {
|
playstore {
|
||||||
java.srcDirs = ['src/main/java', 'src/main/java/', 'src/notFoss/java', 'src/notFoss/java/']
|
java.srcDirs = ['src/main/java/', 'src/apiKey/java/', 'src/apkStorePlaystore/java/']
|
||||||
}
|
}
|
||||||
|
|
||||||
apkstore {
|
apkstore {
|
||||||
java.srcDirs = ['src/main/java', 'src/main/java/', 'src/notFoss/java', 'src/notFoss/java/']
|
java.srcDirs = ['src/main/java/', 'src/apiKey/java/', 'src/apkStorePlaystore/java/']
|
||||||
}
|
}
|
||||||
|
|
||||||
fdroid {
|
fdroid {
|
||||||
java.srcDirs = ['src/main/java', 'src/main/java/', 'src/foss/java', 'src/foss/java/']
|
java.srcDirs = ['src/main/java/', 'src/apiKey/java/', 'src/fdroid/java/']
|
||||||
|
}
|
||||||
|
|
||||||
|
fdroidmain {
|
||||||
|
java.srcDirs = ['src/main/java/', 'src/fdroidmain/java/']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
packagingOptions {
|
packagingOptions {
|
||||||
@ -95,7 +103,9 @@ dependencies {
|
|||||||
|
|
||||||
implementation project(':domain')
|
implementation project(':domain')
|
||||||
implementation project(':util')
|
implementation project(':util')
|
||||||
implementation project(':pcloud-sdk-java')
|
playstoreImplementation project(':pcloud-sdk-java')
|
||||||
|
apkstoreImplementation project(':pcloud-sdk-java')
|
||||||
|
fdroidImplementation project(':pcloud-sdk-java')
|
||||||
|
|
||||||
coreLibraryDesugaring dependencies.coreDesugaring
|
coreLibraryDesugaring dependencies.coreDesugaring
|
||||||
|
|
||||||
@ -113,9 +123,16 @@ dependencies {
|
|||||||
implementation dependencies.jsonWebTokenJson
|
implementation dependencies.jsonWebTokenJson
|
||||||
|
|
||||||
// cloud
|
// cloud
|
||||||
implementation dependencies.dropbox
|
playstoreImplementation dependencies.dropbox
|
||||||
implementation dependencies.msgraphAuth
|
apkstoreImplementation dependencies.dropbox
|
||||||
implementation dependencies.msgraph
|
fdroidImplementation dependencies.dropbox
|
||||||
|
|
||||||
|
playstoreImplementation dependencies.msgraphAuth
|
||||||
|
apkstoreImplementation dependencies.msgraphAuth
|
||||||
|
fdroidImplementation dependencies.msgraphAuth
|
||||||
|
playstoreImplementation dependencies.msgraph
|
||||||
|
apkstoreImplementation dependencies.msgraph
|
||||||
|
fdroidImplementation dependencies.msgraph
|
||||||
|
|
||||||
implementation dependencies.stax
|
implementation dependencies.stax
|
||||||
api dependencies.minIo
|
api dependencies.minIo
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package org.cryptomator.data.cloud;
|
package org.cryptomator.data.cloud;
|
||||||
|
|
||||||
|
import static java.util.Arrays.asList;
|
||||||
|
|
||||||
import org.cryptomator.data.cloud.crypto.CryptoCloudContentRepositoryFactory;
|
import org.cryptomator.data.cloud.crypto.CryptoCloudContentRepositoryFactory;
|
||||||
import org.cryptomator.data.cloud.dropbox.DropboxCloudContentRepositoryFactory;
|
import org.cryptomator.data.cloud.dropbox.DropboxCloudContentRepositoryFactory;
|
||||||
import org.cryptomator.data.cloud.googledrive.GoogleDriveCloudContentRepositoryFactory;
|
import org.cryptomator.data.cloud.googledrive.GoogleDriveCloudContentRepositoryFactory;
|
||||||
@ -16,8 +18,6 @@ import java.util.Iterator;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import static java.util.Arrays.asList;
|
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class CloudContentRepositoryFactories implements Iterable<CloudContentRepositoryFactory> {
|
public class CloudContentRepositoryFactories implements Iterable<CloudContentRepositoryFactory> {
|
||||||
|
|
@ -0,0 +1,40 @@
|
|||||||
|
package org.cryptomator.data.cloud;
|
||||||
|
|
||||||
|
import static java.util.Arrays.asList;
|
||||||
|
|
||||||
|
import org.cryptomator.data.cloud.crypto.CryptoCloudContentRepositoryFactory;
|
||||||
|
import org.cryptomator.data.cloud.local.LocalStorageContentRepositoryFactory;
|
||||||
|
import org.cryptomator.data.cloud.s3.S3CloudContentRepositoryFactory;
|
||||||
|
import org.cryptomator.data.cloud.webdav.WebDavCloudContentRepositoryFactory;
|
||||||
|
import org.cryptomator.data.repository.CloudContentRepositoryFactory;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
public class CloudContentRepositoryFactories implements Iterable<CloudContentRepositoryFactory> {
|
||||||
|
|
||||||
|
private final Iterable<CloudContentRepositoryFactory> factories;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public CloudContentRepositoryFactories(
|
||||||
|
S3CloudContentRepositoryFactory s3Factory, //
|
||||||
|
CryptoCloudContentRepositoryFactory cryptoFactory, //
|
||||||
|
LocalStorageContentRepositoryFactory localStorageFactory, //
|
||||||
|
WebDavCloudContentRepositoryFactory webDavFactory) {
|
||||||
|
|
||||||
|
factories = asList(s3Factory, //
|
||||||
|
cryptoFactory, //
|
||||||
|
localStorageFactory, //
|
||||||
|
webDavFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Iterator<CloudContentRepositoryFactory> iterator() {
|
||||||
|
return factories.iterator();
|
||||||
|
}
|
||||||
|
}
|
@ -88,19 +88,27 @@ android {
|
|||||||
fdroid {
|
fdroid {
|
||||||
dimension "version"
|
dimension "version"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fdroidmain {
|
||||||
|
dimension "version"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
playstore {
|
playstore {
|
||||||
java.srcDirs = ['src/main/java', 'src/main/java/', 'src/notFoss/java', 'src/notFoss/java/']
|
java.srcDirs = ['src/main/java', 'src/apiKey/java/', 'src/apkStorePlaystore/java/']
|
||||||
}
|
}
|
||||||
|
|
||||||
apkstore {
|
apkstore {
|
||||||
java.srcDirs = ['src/main/java', 'src/main/java/', 'src/notFoss/java', 'src/notFoss/java/']
|
java.srcDirs = ['src/main/java/', 'src/apiKey/java/', 'src/apkStorePlaystore/java/']
|
||||||
}
|
}
|
||||||
|
|
||||||
fdroid {
|
fdroid {
|
||||||
java.srcDirs = ['src/main/java', 'src/main/java/', 'src/foss/java', 'src/foss/java/']
|
java.srcDirs = ['src/main/java/', 'src/apiKey/java/', 'src/fdroid/java/', 'src/fdroidAndfdroidmain/java/']
|
||||||
|
}
|
||||||
|
|
||||||
|
fdroidmain {
|
||||||
|
java.srcDirs = ['src/main/java/', 'src/fdroidmain/java/', 'src/fdroidAndfdroidmain/java/']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
packagingOptions {
|
packagingOptions {
|
||||||
@ -147,9 +155,16 @@ dependencies {
|
|||||||
implementation dependencies.androidxBiometric
|
implementation dependencies.androidxBiometric
|
||||||
|
|
||||||
// cloud
|
// cloud
|
||||||
implementation dependencies.dropbox
|
playstoreImplementation dependencies.dropbox
|
||||||
implementation dependencies.msgraph
|
apkstoreImplementation dependencies.dropbox
|
||||||
implementation dependencies.msgraphAuth
|
fdroidImplementation dependencies.dropbox
|
||||||
|
|
||||||
|
playstoreImplementation dependencies.msgraphAuth
|
||||||
|
apkstoreImplementation dependencies.msgraphAuth
|
||||||
|
fdroidImplementation dependencies.msgraphAuth
|
||||||
|
playstoreImplementation dependencies.msgraph
|
||||||
|
apkstoreImplementation dependencies.msgraph
|
||||||
|
fdroidImplementation dependencies.msgraph
|
||||||
|
|
||||||
playstoreImplementation(dependencies.googleApiServicesDrive) {
|
playstoreImplementation(dependencies.googleApiServicesDrive) {
|
||||||
exclude module: 'guava-jdk5'
|
exclude module: 'guava-jdk5'
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
package org.cryptomator.presentation.presenter
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.dropbox.core.android.Auth
|
||||||
|
import org.cryptomator.presentation.BuildConfig
|
||||||
|
|
||||||
|
object DropboxAuthHelper {
|
||||||
|
|
||||||
|
fun startOAuth2Authentication(context: Context) {
|
||||||
|
Auth.startOAuth2Authentication(context, BuildConfig.DROPBOX_API_KEY)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getOAuth2Token(): String? {
|
||||||
|
return Auth.getOAuth2Token()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,139 @@
|
|||||||
|
package org.cryptomator.presentation.presenter
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import com.microsoft.identity.client.AuthenticationCallback
|
||||||
|
import com.microsoft.identity.client.IAccount
|
||||||
|
import com.microsoft.identity.client.IAuthenticationResult
|
||||||
|
import com.microsoft.identity.client.IMultipleAccountPublicClientApplication
|
||||||
|
import com.microsoft.identity.client.IPublicClientApplication
|
||||||
|
import com.microsoft.identity.client.PublicClientApplication
|
||||||
|
import com.microsoft.identity.client.exception.MsalException
|
||||||
|
import com.microsoft.identity.client.exception.MsalUiRequiredException
|
||||||
|
import org.cryptomator.domain.OnedriveCloud
|
||||||
|
import org.cryptomator.domain.exception.FatalBackendException
|
||||||
|
import org.cryptomator.presentation.R
|
||||||
|
import org.cryptomator.util.crypto.CredentialCryptor
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
|
object OnedriveAuthentication {
|
||||||
|
|
||||||
|
fun refreshOrCheckAuth(activity: Activity, cloud: OnedriveCloud, success: (cloud: OnedriveCloud) -> Unit, failed: (e: FatalBackendException) -> Unit) {
|
||||||
|
PublicClientApplication.createMultipleAccountPublicClientApplication(
|
||||||
|
activity.applicationContext,
|
||||||
|
R.raw.auth_config_onedrive,
|
||||||
|
object : IPublicClientApplication.IMultipleAccountApplicationCreatedListener {
|
||||||
|
override fun onCreated(application: IMultipleAccountPublicClientApplication) {
|
||||||
|
application.getAccounts(object : IPublicClientApplication.LoadAccountsCallback {
|
||||||
|
override fun onTaskCompleted(accounts: List<IAccount>) {
|
||||||
|
if (accounts.isEmpty()) {
|
||||||
|
application.acquireToken(activity, AuthenticateCloudPresenter.onedriveScopes(), getAuthInteractiveCallback(activity.applicationContext, cloud, success, failed))
|
||||||
|
} else {
|
||||||
|
accounts.find { account -> account.username == cloud.username() }?.let {
|
||||||
|
application.acquireTokenSilentAsync(
|
||||||
|
AuthenticateCloudPresenter.onedriveScopes(),
|
||||||
|
it,
|
||||||
|
"https://login.microsoftonline.com/common",
|
||||||
|
getAuthSilentCallback(activity, cloud, success, failed, application)
|
||||||
|
)
|
||||||
|
} ?: application.acquireToken(activity, AuthenticateCloudPresenter.onedriveScopes(), getAuthInteractiveCallback(activity.applicationContext, cloud, success, failed))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onError(e: MsalException) {
|
||||||
|
Timber.tag("AuthenticateCloudPresenter").e(e, "Error to get accounts")
|
||||||
|
failed(FatalBackendException(e))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onError(e: MsalException) {
|
||||||
|
Timber.tag("AuthenticateCloudPresenter").i(e, "Error in configuration")
|
||||||
|
failed(FatalBackendException(e))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getAuthSilentCallback(
|
||||||
|
activity: Activity,
|
||||||
|
cloud: OnedriveCloud,
|
||||||
|
success: (cloud: OnedriveCloud) -> Unit,
|
||||||
|
failed: (e: FatalBackendException) -> Unit,
|
||||||
|
application: IMultipleAccountPublicClientApplication
|
||||||
|
): AuthenticationCallback {
|
||||||
|
return object : AuthenticationCallback {
|
||||||
|
|
||||||
|
override fun onSuccess(authenticationResult: IAuthenticationResult) {
|
||||||
|
onTokenObtained(activity.applicationContext, cloud, authenticationResult, success)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onError(e: MsalException) {
|
||||||
|
Timber.tag("AuthenticateCloudPresenter").e(e, "Failed to acquireToken")
|
||||||
|
when (e) {
|
||||||
|
is MsalUiRequiredException -> {
|
||||||
|
/* Tokens expired or no session, retry with interactive */
|
||||||
|
application.acquireToken(activity, AuthenticateCloudPresenter.onedriveScopes(), getAuthInteractiveCallback(activity.applicationContext, cloud, success, failed))
|
||||||
|
}
|
||||||
|
else -> failed(FatalBackendException(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCancel() {
|
||||||
|
Timber.tag("AuthenticateCloudPresenter").i("User cancelled login")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onTokenObtained(context: Context, cloud: OnedriveCloud?, authenticationResult: IAuthenticationResult, success: (cloud: OnedriveCloud) -> Unit) {
|
||||||
|
Timber.tag("AuthenticateCloudPresenter").i("Successfully authenticated")
|
||||||
|
val accessToken = CredentialCryptor.getInstance(context).encrypt(authenticationResult.accessToken)
|
||||||
|
val cloudBuilder = cloud?.let { OnedriveCloud.aCopyOf(it) } ?: OnedriveCloud.aOnedriveCloud()
|
||||||
|
val onedriveSkeleton = cloudBuilder.withAccessToken(accessToken).withUsername(authenticationResult.account.username).build()
|
||||||
|
success(onedriveSkeleton)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAuthenticatedOnedriveCloud(activity: Activity, success: (cloud: OnedriveCloud) -> Unit, failed: (e: FatalBackendException) -> Unit) {
|
||||||
|
PublicClientApplication.createMultipleAccountPublicClientApplication(
|
||||||
|
activity.applicationContext,
|
||||||
|
R.raw.auth_config_onedrive,
|
||||||
|
object : IPublicClientApplication.IMultipleAccountApplicationCreatedListener {
|
||||||
|
override fun onCreated(application: IMultipleAccountPublicClientApplication) {
|
||||||
|
application.getAccounts(object : IPublicClientApplication.LoadAccountsCallback {
|
||||||
|
override fun onTaskCompleted(accounts: List<IAccount>) {
|
||||||
|
application.acquireToken(activity, AuthenticateCloudPresenter.onedriveScopes(), getAuthInteractiveCallback(activity.applicationContext, null, success, failed))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onError(e: MsalException) {
|
||||||
|
Timber.tag("AuthenticateCloudPresenter").e(e, "Error to get accounts")
|
||||||
|
failed(FatalBackendException(e))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onError(e: MsalException) {
|
||||||
|
Timber.tag("AuthenticateCloudPresenter").i(e, "Error in configuration")
|
||||||
|
failed(FatalBackendException(e))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getAuthInteractiveCallback(context: Context, cloud: OnedriveCloud?, success: (cloud: OnedriveCloud) -> Unit, failed: (e: FatalBackendException) -> Unit): AuthenticationCallback {
|
||||||
|
return object : AuthenticationCallback {
|
||||||
|
|
||||||
|
override fun onSuccess(authenticationResult: IAuthenticationResult) {
|
||||||
|
onTokenObtained(context, cloud, authenticationResult, success)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onError(e: MsalException) {
|
||||||
|
Timber.tag("AuthenticateCloudPresenter").e(e, "Successfully authenticated")
|
||||||
|
failed(FatalBackendException(e))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCancel() {
|
||||||
|
Timber.tag("AuthenticateCloudPresenter").i("User cancelled login")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
|||||||
|
package org.cryptomator.presentation.presenter
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential
|
||||||
|
import com.google.api.services.drive.DriveScopes
|
||||||
|
|
||||||
|
object GoogleAuthHelper {
|
||||||
|
|
||||||
|
fun getChooseAccountIntent(context: Context): Intent? {
|
||||||
|
return GoogleAccountCredential.usingOAuth2(context, setOf(DriveScopes.DRIVE)).newChooseAccountIntent()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package org.cryptomator.presentation.presenter
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
|
||||||
|
object GoogleAuthHelper {
|
||||||
|
|
||||||
|
fun getChooseAccountIntent(context: Context): Intent? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package org.cryptomator.presentation.presenter
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
|
||||||
|
object DropboxAuthHelper {
|
||||||
|
|
||||||
|
fun startOAuth2Authentication(context: Context) {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getOAuth2Token(): String? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package org.cryptomator.presentation.presenter
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import org.cryptomator.domain.OnedriveCloud
|
||||||
|
import org.cryptomator.domain.exception.FatalBackendException
|
||||||
|
|
||||||
|
object OnedriveAuthentication {
|
||||||
|
|
||||||
|
fun getAuthenticatedOnedriveCloud(activity: Activity, success: (cloud: OnedriveCloud) -> Unit, failed: (e: FatalBackendException) -> Unit) {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
fun refreshOrCheckAuth(activity: Activity, cloud: OnedriveCloud, success: (cloud: OnedriveCloud) -> Unit, failed: (e: FatalBackendException) -> Unit) {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -1,610 +0,0 @@
|
|||||||
package org.cryptomator.presentation.presenter
|
|
||||||
|
|
||||||
import android.accounts.AccountManager
|
|
||||||
import android.content.Intent
|
|
||||||
import android.content.Intent.ACTION_OPEN_DOCUMENT_TREE
|
|
||||||
import android.provider.DocumentsContract
|
|
||||||
import android.widget.Toast
|
|
||||||
import com.dropbox.core.android.Auth
|
|
||||||
import com.microsoft.identity.client.AuthenticationCallback
|
|
||||||
import com.microsoft.identity.client.IAccount
|
|
||||||
import com.microsoft.identity.client.IAuthenticationResult
|
|
||||||
import com.microsoft.identity.client.IMultipleAccountPublicClientApplication
|
|
||||||
import com.microsoft.identity.client.IPublicClientApplication
|
|
||||||
import com.microsoft.identity.client.PublicClientApplication
|
|
||||||
import com.microsoft.identity.client.exception.MsalClientException
|
|
||||||
import com.microsoft.identity.client.exception.MsalException
|
|
||||||
import com.microsoft.identity.client.exception.MsalServiceException
|
|
||||||
import com.microsoft.identity.client.exception.MsalUiRequiredException
|
|
||||||
import org.cryptomator.data.util.X509CertificateHelper
|
|
||||||
import org.cryptomator.domain.Cloud
|
|
||||||
import org.cryptomator.domain.CloudType
|
|
||||||
import org.cryptomator.domain.DropboxCloud
|
|
||||||
import org.cryptomator.domain.GoogleDriveCloud
|
|
||||||
import org.cryptomator.domain.OnedriveCloud
|
|
||||||
import org.cryptomator.domain.PCloud
|
|
||||||
import org.cryptomator.domain.WebDavCloud
|
|
||||||
import org.cryptomator.domain.di.PerView
|
|
||||||
import org.cryptomator.domain.exception.FatalBackendException
|
|
||||||
import org.cryptomator.domain.exception.NetworkConnectionException
|
|
||||||
import org.cryptomator.domain.exception.authentication.AuthenticationException
|
|
||||||
import org.cryptomator.domain.exception.authentication.WebDavCertificateUntrustedAuthenticationException
|
|
||||||
import org.cryptomator.domain.exception.authentication.WebDavNotSupportedException
|
|
||||||
import org.cryptomator.domain.exception.authentication.WebDavServerNotFoundException
|
|
||||||
import org.cryptomator.domain.exception.authentication.WrongCredentialsException
|
|
||||||
import org.cryptomator.domain.usecases.cloud.AddOrChangeCloudConnectionUseCase
|
|
||||||
import org.cryptomator.domain.usecases.cloud.GetCloudsUseCase
|
|
||||||
import org.cryptomator.domain.usecases.cloud.GetUsernameUseCase
|
|
||||||
import org.cryptomator.generator.Callback
|
|
||||||
import org.cryptomator.presentation.BuildConfig
|
|
||||||
import org.cryptomator.presentation.R
|
|
||||||
import org.cryptomator.presentation.exception.ExceptionHandlers
|
|
||||||
import org.cryptomator.presentation.exception.PermissionNotGrantedException
|
|
||||||
import org.cryptomator.presentation.intent.AuthenticateCloudIntent
|
|
||||||
import org.cryptomator.presentation.intent.Intents
|
|
||||||
import org.cryptomator.presentation.model.CloudModel
|
|
||||||
import org.cryptomator.presentation.model.CloudTypeModel
|
|
||||||
import org.cryptomator.presentation.model.LocalStorageModel
|
|
||||||
import org.cryptomator.presentation.model.ProgressModel
|
|
||||||
import org.cryptomator.presentation.model.ProgressStateModel
|
|
||||||
import org.cryptomator.presentation.model.S3CloudModel
|
|
||||||
import org.cryptomator.presentation.model.WebDavCloudModel
|
|
||||||
import org.cryptomator.presentation.model.mappers.CloudModelMapper
|
|
||||||
import org.cryptomator.presentation.ui.activity.view.AuthenticateCloudView
|
|
||||||
import org.cryptomator.presentation.workflow.ActivityResult
|
|
||||||
import org.cryptomator.presentation.workflow.AddExistingVaultWorkflow
|
|
||||||
import org.cryptomator.presentation.workflow.CreateNewVaultWorkflow
|
|
||||||
import org.cryptomator.presentation.workflow.Workflow
|
|
||||||
import org.cryptomator.util.ExceptionUtil
|
|
||||||
import org.cryptomator.util.crypto.CredentialCryptor
|
|
||||||
import java.security.cert.CertificateEncodingException
|
|
||||||
import java.security.cert.CertificateException
|
|
||||||
import java.security.cert.X509Certificate
|
|
||||||
import javax.inject.Inject
|
|
||||||
import timber.log.Timber
|
|
||||||
|
|
||||||
@PerView
|
|
||||||
class AuthenticateCloudPresenter @Inject constructor( //
|
|
||||||
exceptionHandlers: ExceptionHandlers, //
|
|
||||||
private val cloudModelMapper: CloudModelMapper, //
|
|
||||||
private val addOrChangeCloudConnectionUseCase: AddOrChangeCloudConnectionUseCase, //
|
|
||||||
private val getCloudsUseCase: GetCloudsUseCase, //
|
|
||||||
private val getUsernameUseCase: GetUsernameUseCase, //
|
|
||||||
private val addExistingVaultWorkflow: AddExistingVaultWorkflow, //
|
|
||||||
private val createNewVaultWorkflow: CreateNewVaultWorkflow
|
|
||||||
) : Presenter<AuthenticateCloudView>(exceptionHandlers) {
|
|
||||||
|
|
||||||
private val strategies = arrayOf( //
|
|
||||||
DropboxAuthStrategy(), //
|
|
||||||
OnedriveAuthStrategy(), //
|
|
||||||
PCloudAuthStrategy(), //
|
|
||||||
WebDAVAuthStrategy(), //
|
|
||||||
S3AuthStrategy(), //
|
|
||||||
LocalStorageAuthStrategy() //
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun workflows(): Iterable<Workflow<*>> {
|
|
||||||
return listOf(createNewVaultWorkflow, addExistingVaultWorkflow)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun resumed() {
|
|
||||||
val cloud = view?.intent()?.cloud()
|
|
||||||
val error = view?.intent()?.error()
|
|
||||||
handleNetworkConnectionExceptionIfRequired(error)
|
|
||||||
view?.intent()?.let { cloud?.let { cloud -> authStrategyFor(cloud).resumed(it) } }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleNetworkConnectionExceptionIfRequired(error: AuthenticationException?) {
|
|
||||||
if (error != null && ExceptionUtil.contains(error, NetworkConnectionException::class.java)) {
|
|
||||||
view?.showMessage(R.string.error_no_network_connection)
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun authStrategyFor(cloud: CloudModel): AuthStrategy {
|
|
||||||
strategies.forEach { strategy ->
|
|
||||||
if (strategy.supports(cloud)) {
|
|
||||||
return strategy
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return FailingAuthStrategy()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getUsernameAndSuceedAuthentication(cloud: Cloud) {
|
|
||||||
getUsernameUseCase.withCloud(cloud).run(object : DefaultResultHandler<String>() {
|
|
||||||
override fun onSuccess(username: String) {
|
|
||||||
succeedAuthenticationWith(updateUsernameOf(cloud, username))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onError(e: Throwable) {
|
|
||||||
super.onError(e)
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateUsernameOf(cloud: Cloud, username: String): Cloud {
|
|
||||||
when (cloud.type()) {
|
|
||||||
CloudType.DROPBOX -> return DropboxCloud.aCopyOf(cloud as DropboxCloud).withUsername(username).build()
|
|
||||||
CloudType.ONEDRIVE -> return OnedriveCloud.aCopyOf(cloud as OnedriveCloud).withUsername(username).build()
|
|
||||||
}
|
|
||||||
throw IllegalStateException("Cloud " + cloud.type() + " is not supported")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun succeedAuthenticationWith(cloud: Cloud) {
|
|
||||||
addOrChangeCloudConnectionUseCase //
|
|
||||||
.withCloud(cloud) //
|
|
||||||
.run(object : DefaultResultHandler<Void?>() {
|
|
||||||
override fun onSuccess(void: Void?) {
|
|
||||||
finishWithResult(cloudModelMapper.toModel(cloud))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onError(e: Throwable) {
|
|
||||||
super.onError(e)
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun failAuthentication(cloudName: Int) {
|
|
||||||
activity().runOnUiThread {
|
|
||||||
view?.showMessage(String.format(getString(R.string.screen_authenticate_auth_authentication_failed), getString(cloudName)))
|
|
||||||
}
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun failAuthentication(error: PermissionNotGrantedException) {
|
|
||||||
finishWithResult(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
private inner class DropboxAuthStrategy : AuthStrategy {
|
|
||||||
|
|
||||||
private var authenticationStarted = false
|
|
||||||
override fun supports(cloud: CloudModel): Boolean {
|
|
||||||
return cloud.cloudType() == CloudTypeModel.DROPBOX
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun resumed(intent: AuthenticateCloudIntent) {
|
|
||||||
if (authenticationStarted) {
|
|
||||||
handleAuthenticationResult(intent.cloud())
|
|
||||||
} else {
|
|
||||||
startAuthentication()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun startAuthentication() {
|
|
||||||
showProgress(ProgressModel(ProgressStateModel.AUTHENTICATION))
|
|
||||||
authenticationStarted = true
|
|
||||||
Auth.startOAuth2Authentication(context(), BuildConfig.DROPBOX_API_KEY)
|
|
||||||
view?.skipTransition()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleAuthenticationResult(cloudModel: CloudModel) {
|
|
||||||
val authToken = Auth.getOAuth2Token()
|
|
||||||
if (authToken == null) {
|
|
||||||
failAuthentication(cloudModel.name())
|
|
||||||
} else {
|
|
||||||
getUsernameAndSuceedAuthentication( //
|
|
||||||
DropboxCloud.aCopyOf(cloudModel.toCloud() as DropboxCloud) //
|
|
||||||
.withAccessToken(encrypt(authToken)) //
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Callback(dispatchResultOkOnly = false)
|
|
||||||
fun onUserRecoveryFinished(result: ActivityResult, cloud: CloudModel) {
|
|
||||||
if (result.isResultOk) {
|
|
||||||
succeedAuthenticationWith(cloud.toCloud())
|
|
||||||
} else {
|
|
||||||
failAuthentication(cloud.name())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Callback(dispatchResultOkOnly = false)
|
|
||||||
fun onGoogleDriveAuthenticated(result: ActivityResult, cloud: CloudModel) {
|
|
||||||
if (result.isResultOk) {
|
|
||||||
val accountName = result.intent()?.extras?.getString(AccountManager.KEY_ACCOUNT_NAME)
|
|
||||||
succeedAuthenticationWith(
|
|
||||||
GoogleDriveCloud.aCopyOf(cloud.toCloud() as GoogleDriveCloud) //
|
|
||||||
.withUsername(accountName) //
|
|
||||||
.withAccessToken(accountName) //
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
failAuthentication(cloud.name())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private inner class OnedriveAuthStrategy : AuthStrategy {
|
|
||||||
|
|
||||||
private var authenticationStarted = false
|
|
||||||
override fun supports(cloud: CloudModel): Boolean {
|
|
||||||
return cloud.cloudType() == CloudTypeModel.ONEDRIVE
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun resumed(intent: AuthenticateCloudIntent) {
|
|
||||||
if (!authenticationStarted) {
|
|
||||||
startAuthentication(intent.cloud())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun startAuthentication(cloud: CloudModel) {
|
|
||||||
authenticationStarted = true
|
|
||||||
|
|
||||||
Toast.makeText(context(), R.string.notification_authenticating, Toast.LENGTH_SHORT).show()
|
|
||||||
|
|
||||||
PublicClientApplication.createMultipleAccountPublicClientApplication(
|
|
||||||
context(),
|
|
||||||
R.raw.auth_config_onedrive,
|
|
||||||
object : IPublicClientApplication.IMultipleAccountApplicationCreatedListener {
|
|
||||||
override fun onCreated(application: IMultipleAccountPublicClientApplication) {
|
|
||||||
application.getAccounts(object : IPublicClientApplication.LoadAccountsCallback {
|
|
||||||
override fun onTaskCompleted(accounts: List<IAccount>) {
|
|
||||||
if (accounts.isEmpty()) {
|
|
||||||
application.acquireToken(activity(), onedriveScopes(), getAuthInteractiveCallback(cloud))
|
|
||||||
} else {
|
|
||||||
accounts.find { account -> account.username == cloud.username() }?.let {
|
|
||||||
application.acquireTokenSilentAsync(
|
|
||||||
onedriveScopes(),
|
|
||||||
it,
|
|
||||||
"https://login.microsoftonline.com/common",
|
|
||||||
getAuthSilentCallback(cloud, application)
|
|
||||||
)
|
|
||||||
} ?: application.acquireToken(activity(), onedriveScopes(), getAuthInteractiveCallback(cloud))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onError(e: MsalException) {
|
|
||||||
Timber.tag("AuthenticateCloudPresenter").e(e, "Error to get accounts")
|
|
||||||
failAuthentication(cloud.name())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onError(e: MsalException) {
|
|
||||||
Timber.tag("AuthenticateCloudPresenter").i(e, "Error in configuration")
|
|
||||||
failAuthentication(cloud.name())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getAuthSilentCallback(cloud: CloudModel, application: IMultipleAccountPublicClientApplication): AuthenticationCallback {
|
|
||||||
return object : AuthenticationCallback {
|
|
||||||
|
|
||||||
override fun onSuccess(authenticationResult: IAuthenticationResult) {
|
|
||||||
Timber.tag("AuthenticateCloudPresenter").i("Successfully authenticated")
|
|
||||||
handleAuthenticationResult(cloud, authenticationResult.accessToken)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onError(e: MsalException) {
|
|
||||||
Timber.tag("AuthenticateCloudPresenter").e(e, "Failed to acquireToken")
|
|
||||||
when (e) {
|
|
||||||
is MsalClientException -> {
|
|
||||||
/* Exception inside MSAL, more info inside MsalError.java */
|
|
||||||
failAuthentication(cloud.name())
|
|
||||||
}
|
|
||||||
is MsalServiceException -> {
|
|
||||||
/* Exception when communicating with the STS, likely config issue */
|
|
||||||
failAuthentication(cloud.name())
|
|
||||||
}
|
|
||||||
is MsalUiRequiredException -> {
|
|
||||||
/* Tokens expired or no session, retry with interactive */
|
|
||||||
application.acquireToken(activity(), onedriveScopes(), getAuthInteractiveCallback(cloud))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCancel() {
|
|
||||||
Timber.tag("AuthenticateCloudPresenter").i("User cancelled login")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getAuthInteractiveCallback(cloud: CloudModel): AuthenticationCallback {
|
|
||||||
return object : AuthenticationCallback {
|
|
||||||
|
|
||||||
override fun onSuccess(authenticationResult: IAuthenticationResult) {
|
|
||||||
Timber.tag("AuthenticateCloudPresenter").i("Successfully authenticated")
|
|
||||||
handleAuthenticationResult(cloud, authenticationResult.accessToken, authenticationResult.account.username)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onError(e: MsalException) {
|
|
||||||
Timber.tag("AuthenticateCloudPresenter").e(e, "Successfully authenticated")
|
|
||||||
failAuthentication(cloud.name())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCancel() {
|
|
||||||
Timber.tag("AuthenticateCloudPresenter").i("User cancelled login")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleAuthenticationResult(cloud: CloudModel, accessToken: String) {
|
|
||||||
getUsernameAndSuceedAuthentication( //
|
|
||||||
OnedriveCloud.aCopyOf(cloud.toCloud() as OnedriveCloud) //
|
|
||||||
.withAccessToken(encrypt(accessToken)) //
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleAuthenticationResult(cloud: CloudModel, accessToken: String, username: String) {
|
|
||||||
getUsernameAndSuceedAuthentication( //
|
|
||||||
OnedriveCloud.aCopyOf(cloud.toCloud() as OnedriveCloud) //
|
|
||||||
.withAccessToken(encrypt(accessToken)) //
|
|
||||||
.withUsername(username)
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private inner class PCloudAuthStrategy : AuthStrategy {
|
|
||||||
|
|
||||||
private var authenticationStarted = false
|
|
||||||
|
|
||||||
override fun supports(cloud: CloudModel): Boolean {
|
|
||||||
return cloud.cloudType() == CloudTypeModel.PCLOUD
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun resumed(intent: AuthenticateCloudIntent) {
|
|
||||||
if (authenticationStarted) {
|
|
||||||
finish()
|
|
||||||
} else {
|
|
||||||
startAuthentication(intent.cloud())
|
|
||||||
Toast.makeText(
|
|
||||||
context(),
|
|
||||||
String.format(getString(R.string.error_authentication_failed_re_authenticate), intent.cloud().username()),
|
|
||||||
Toast.LENGTH_LONG
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun startAuthentication(cloud: CloudModel) {
|
|
||||||
authenticationStarted = true
|
|
||||||
showProgress(ProgressModel(ProgressStateModel.AUTHENTICATION))
|
|
||||||
view?.skipTransition()
|
|
||||||
requestActivityResult(
|
|
||||||
ActivityResultCallbacks.pCloudReAuthenticationFinished(cloud), //
|
|
||||||
Intents.cloudConnectionListIntent() //
|
|
||||||
.withCloudType(CloudTypeModel.PCLOUD) //
|
|
||||||
.withDialogTitle(context().getString(R.string.screen_update_pcloud_connections_title)) //
|
|
||||||
.withFinishOnCloudItemClick(false) //
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Callback
|
|
||||||
fun pCloudReAuthenticationFinished(activityResult: ActivityResult, cloud: CloudModel) {
|
|
||||||
val code = activityResult.intent().extras?.getString(CloudConnectionListPresenter.PCLOUD_OAUTH_AUTH_CODE, "")
|
|
||||||
val hostname = activityResult.intent().extras?.getString(CloudConnectionListPresenter.PCLOUD_HOSTNAME, "")
|
|
||||||
|
|
||||||
if (!code.isNullOrEmpty() && !hostname.isNullOrEmpty()) {
|
|
||||||
Timber.tag("CloudConnectionListPresenter").i("PCloud OAuth code successfully retrieved")
|
|
||||||
|
|
||||||
val accessToken = CredentialCryptor //
|
|
||||||
.getInstance(this.context()) //
|
|
||||||
.encrypt(code)
|
|
||||||
val pCloudSkeleton = PCloud.aPCloud() //
|
|
||||||
.withAccessToken(accessToken)
|
|
||||||
.withUrl(hostname)
|
|
||||||
.build();
|
|
||||||
getUsernameUseCase //
|
|
||||||
.withCloud(pCloudSkeleton) //
|
|
||||||
.run(object : DefaultResultHandler<String>() {
|
|
||||||
override fun onSuccess(username: String) {
|
|
||||||
Timber.tag("CloudConnectionListPresenter").i("PCloud Authentication successfully")
|
|
||||||
prepareForSavingPCloud(PCloud.aCopyOf(pCloudSkeleton).withUsername(username).build())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Timber.tag("CloudConnectionListPresenter").i("PCloud Authentication not successful")
|
|
||||||
failAuthentication(cloud.name())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun prepareForSavingPCloud(cloud: PCloud) {
|
|
||||||
getCloudsUseCase //
|
|
||||||
.withCloudType(cloud.type()) //
|
|
||||||
.run(object : DefaultResultHandler<List<Cloud>>() {
|
|
||||||
override fun onSuccess(clouds: List<Cloud>) {
|
|
||||||
clouds.firstOrNull {
|
|
||||||
(it as PCloud).username() == cloud.username()
|
|
||||||
}?.let {
|
|
||||||
it as PCloud
|
|
||||||
succeedAuthenticationWith(
|
|
||||||
PCloud.aCopyOf(it) //
|
|
||||||
.withUrl(cloud.url())
|
|
||||||
.withAccessToken(cloud.accessToken())
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
} ?: succeedAuthenticationWith(cloud)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private inner class WebDAVAuthStrategy : AuthStrategy {
|
|
||||||
|
|
||||||
override fun supports(cloud: CloudModel): Boolean {
|
|
||||||
return cloud.cloudType() == CloudTypeModel.WEBDAV
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun resumed(intent: AuthenticateCloudIntent) {
|
|
||||||
handleWebDavAuthenticationExceptionIfRequired(intent.cloud() as WebDavCloudModel, intent.error())
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleWebDavAuthenticationExceptionIfRequired(cloud: WebDavCloudModel, e: AuthenticationException) {
|
|
||||||
Timber.tag("AuthicateCloudPrester").e(e)
|
|
||||||
when {
|
|
||||||
ExceptionUtil.contains(e, WrongCredentialsException::class.java) -> {
|
|
||||||
failAuthentication(cloud.name())
|
|
||||||
}
|
|
||||||
ExceptionUtil.contains(e, WebDavCertificateUntrustedAuthenticationException::class.java) -> {
|
|
||||||
handleCertificateUntrustedExceptionIfRequired(cloud, e)
|
|
||||||
}
|
|
||||||
ExceptionUtil.contains(e, WebDavServerNotFoundException::class.java) -> {
|
|
||||||
view?.showMessage(R.string.error_server_not_found)
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
ExceptionUtil.contains(e, WebDavNotSupportedException::class.java) -> {
|
|
||||||
view?.showMessage(R.string.screen_cloud_error_webdav_not_supported)
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleCertificateUntrustedExceptionIfRequired(cloud: WebDavCloudModel, e: AuthenticationException) {
|
|
||||||
val untrustedException = ExceptionUtil.extract(e, WebDavCertificateUntrustedAuthenticationException::class.java)
|
|
||||||
try {
|
|
||||||
val certificate = X509CertificateHelper.convertFromPem(untrustedException.get().certificate)
|
|
||||||
view?.showUntrustedCertificateDialog(cloud.toCloud() as WebDavCloud, certificate)
|
|
||||||
} catch (ex: CertificateException) {
|
|
||||||
Timber.tag("AuthicateCloudPrester").e(ex)
|
|
||||||
throw FatalBackendException(ex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onAcceptWebDavCertificateClicked(cloud: WebDavCloud, certificate: X509Certificate) {
|
|
||||||
try {
|
|
||||||
val webDavCloudWithAcceptedCert = WebDavCloud.aCopyOf(cloud) //
|
|
||||||
.withCertificate(X509CertificateHelper.convertToPem(certificate)) //
|
|
||||||
.build()
|
|
||||||
finishWithResultAndExtra(
|
|
||||||
cloudModelMapper.toModel(webDavCloudWithAcceptedCert), //
|
|
||||||
WEBDAV_ACCEPTED_UNTRUSTED_CERTIFICATE, //
|
|
||||||
true
|
|
||||||
)
|
|
||||||
} catch (e: CertificateEncodingException) {
|
|
||||||
Timber.tag("AuthicateCloudPrester").e(e)
|
|
||||||
throw FatalBackendException(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onAcceptWebDavCertificateDenied() {
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
private inner class S3AuthStrategy : AuthStrategy {
|
|
||||||
|
|
||||||
private var authenticationStarted = false
|
|
||||||
|
|
||||||
override fun supports(cloud: CloudModel): Boolean {
|
|
||||||
return cloud.cloudType() == CloudTypeModel.S3
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun resumed(intent: AuthenticateCloudIntent) {
|
|
||||||
when {
|
|
||||||
ExceptionUtil.contains(intent.error(), WrongCredentialsException::class.java) -> {
|
|
||||||
if (!authenticationStarted) {
|
|
||||||
startAuthentication(intent.cloud())
|
|
||||||
Toast.makeText(
|
|
||||||
context(),
|
|
||||||
String.format(getString(R.string.error_authentication_failed), intent.cloud().username()),
|
|
||||||
Toast.LENGTH_LONG
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
Timber.tag("AuthicateCloudPrester").e(intent.error())
|
|
||||||
failAuthentication(intent.cloud().name())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun startAuthentication(cloud: CloudModel) {
|
|
||||||
authenticationStarted = true
|
|
||||||
startIntent(Intents.s3AddOrChangeIntent().withS3Cloud(cloud as S3CloudModel))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private inner class LocalStorageAuthStrategy : AuthStrategy {
|
|
||||||
|
|
||||||
private var authenticationStarted = false
|
|
||||||
|
|
||||||
override fun supports(cloud: CloudModel): Boolean {
|
|
||||||
return cloud.cloudType() == CloudTypeModel.LOCAL
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun resumed(intent: AuthenticateCloudIntent) {
|
|
||||||
if (!authenticationStarted) {
|
|
||||||
startAuthentication(intent.cloud())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun startAuthentication(cloud: CloudModel) {
|
|
||||||
authenticationStarted = true
|
|
||||||
|
|
||||||
val uri = (cloud as LocalStorageModel).uri()
|
|
||||||
|
|
||||||
val permissions = context().contentResolver.persistedUriPermissions
|
|
||||||
for (permission in permissions) {
|
|
||||||
if (permission.uri.toString() == uri) {
|
|
||||||
succeedAuthenticationWith(cloud.toCloud())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Timber.tag("AuthicateCloudPrester").e("Permission revoked, ask to re-pick location")
|
|
||||||
|
|
||||||
Toast.makeText(context(), getString(R.string.permission_revoked_re_request_permission), Toast.LENGTH_LONG).show()
|
|
||||||
|
|
||||||
val openDocumentTree = Intent(ACTION_OPEN_DOCUMENT_TREE).apply {
|
|
||||||
putExtra(DocumentsContract.EXTRA_INITIAL_URI, uri)
|
|
||||||
}
|
|
||||||
|
|
||||||
requestActivityResult(ActivityResultCallbacks.rePickedLocalStorageLocation(cloud), openDocumentTree)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Callback
|
|
||||||
fun rePickedLocalStorageLocation(result: ActivityResult, cloud: LocalStorageModel) {
|
|
||||||
val rootTreeUriOfLocalStorage = result.intent().data
|
|
||||||
rootTreeUriOfLocalStorage?.let {
|
|
||||||
context() //
|
|
||||||
.contentResolver //
|
|
||||||
.takePersistableUriPermission( //
|
|
||||||
it, //
|
|
||||||
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Timber.tag("AuthicateCloudPrester").e("Permission granted again")
|
|
||||||
succeedAuthenticationWith(cloud.toCloud())
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun encrypt(password: String): String {
|
|
||||||
return CredentialCryptor //
|
|
||||||
.getInstance(context()) //
|
|
||||||
.encrypt(password)
|
|
||||||
}
|
|
||||||
|
|
||||||
private inner class FailingAuthStrategy : AuthStrategy {
|
|
||||||
|
|
||||||
override fun supports(cloud: CloudModel): Boolean {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun resumed(intent: AuthenticateCloudIntent) {
|
|
||||||
view?.showError(R.string.error_authentication_failed)
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private interface AuthStrategy {
|
|
||||||
|
|
||||||
fun supports(cloud: CloudModel): Boolean
|
|
||||||
fun resumed(intent: AuthenticateCloudIntent)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
const val WEBDAV_ACCEPTED_UNTRUSTED_CERTIFICATE = "acceptedUntrustedCertificate"
|
|
||||||
|
|
||||||
fun onedriveScopes(): Array<String> {
|
|
||||||
return arrayOf("User.Read", "Files.ReadWrite")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
unsubscribeOnDestroy(addOrChangeCloudConnectionUseCase, getCloudsUseCase, getUsernameUseCase)
|
|
||||||
}
|
|
||||||
}
|
|
@ -51,6 +51,9 @@ class CryptomatorApp : MultiDexApplication(), HasComponent<ApplicationComponent>
|
|||||||
"fdroid" -> {
|
"fdroid" -> {
|
||||||
"F-Droid Edition"
|
"F-Droid Edition"
|
||||||
}
|
}
|
||||||
|
"fdroidmain" -> {
|
||||||
|
"F-Droid Main Repo Edition"
|
||||||
|
}
|
||||||
else -> "Google Play Edition"
|
else -> "Google Play Edition"
|
||||||
}
|
}
|
||||||
Timber.tag("App").i(
|
Timber.tag("App").i(
|
||||||
|
@ -6,19 +6,6 @@ import android.content.Intent
|
|||||||
import android.content.Intent.ACTION_OPEN_DOCUMENT_TREE
|
import android.content.Intent.ACTION_OPEN_DOCUMENT_TREE
|
||||||
import android.provider.DocumentsContract
|
import android.provider.DocumentsContract
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import com.dropbox.core.android.Auth
|
|
||||||
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential
|
|
||||||
import com.google.api.services.drive.DriveScopes
|
|
||||||
import com.microsoft.identity.client.AuthenticationCallback
|
|
||||||
import com.microsoft.identity.client.IAccount
|
|
||||||
import com.microsoft.identity.client.IAuthenticationResult
|
|
||||||
import com.microsoft.identity.client.IMultipleAccountPublicClientApplication
|
|
||||||
import com.microsoft.identity.client.IPublicClientApplication
|
|
||||||
import com.microsoft.identity.client.PublicClientApplication
|
|
||||||
import com.microsoft.identity.client.exception.MsalClientException
|
|
||||||
import com.microsoft.identity.client.exception.MsalException
|
|
||||||
import com.microsoft.identity.client.exception.MsalServiceException
|
|
||||||
import com.microsoft.identity.client.exception.MsalUiRequiredException
|
|
||||||
import org.cryptomator.data.util.X509CertificateHelper
|
import org.cryptomator.data.util.X509CertificateHelper
|
||||||
import org.cryptomator.domain.Cloud
|
import org.cryptomator.domain.Cloud
|
||||||
import org.cryptomator.domain.CloudType
|
import org.cryptomator.domain.CloudType
|
||||||
@ -39,7 +26,6 @@ import org.cryptomator.domain.usecases.cloud.AddOrChangeCloudConnectionUseCase
|
|||||||
import org.cryptomator.domain.usecases.cloud.GetCloudsUseCase
|
import org.cryptomator.domain.usecases.cloud.GetCloudsUseCase
|
||||||
import org.cryptomator.domain.usecases.cloud.GetUsernameUseCase
|
import org.cryptomator.domain.usecases.cloud.GetUsernameUseCase
|
||||||
import org.cryptomator.generator.Callback
|
import org.cryptomator.generator.Callback
|
||||||
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.AuthenticateCloudIntent
|
import org.cryptomator.presentation.intent.AuthenticateCloudIntent
|
||||||
@ -175,12 +161,12 @@ class AuthenticateCloudPresenter @Inject constructor( //
|
|||||||
private fun startAuthentication() {
|
private fun startAuthentication() {
|
||||||
showProgress(ProgressModel(ProgressStateModel.AUTHENTICATION))
|
showProgress(ProgressModel(ProgressStateModel.AUTHENTICATION))
|
||||||
authenticationStarted = true
|
authenticationStarted = true
|
||||||
Auth.startOAuth2Authentication(context(), BuildConfig.DROPBOX_API_KEY)
|
DropboxAuthHelper.startOAuth2Authentication(context())
|
||||||
view?.skipTransition()
|
view?.skipTransition()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleAuthenticationResult(cloudModel: CloudModel) {
|
private fun handleAuthenticationResult(cloudModel: CloudModel) {
|
||||||
val authToken = Auth.getOAuth2Token()
|
val authToken = DropboxAuthHelper.getOAuth2Token()
|
||||||
if (authToken == null) {
|
if (authToken == null) {
|
||||||
failAuthentication(cloudModel.name())
|
failAuthentication(cloudModel.name())
|
||||||
} else {
|
} else {
|
||||||
@ -221,11 +207,10 @@ class AuthenticateCloudPresenter @Inject constructor( //
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun chooseAccount(cloud: CloudModel) {
|
private fun chooseAccount(cloud: CloudModel) {
|
||||||
val chooseAccountIntent = GoogleAccountCredential.usingOAuth2(context(), setOf(DriveScopes.DRIVE)).newChooseAccountIntent()
|
|
||||||
try {
|
try {
|
||||||
requestActivityResult( //
|
requestActivityResult( //
|
||||||
ActivityResultCallbacks.onGoogleDriveAuthenticated(cloud), //
|
ActivityResultCallbacks.onGoogleDriveAuthenticated(cloud), //
|
||||||
chooseAccountIntent
|
GoogleAuthHelper.getChooseAccountIntent(context())
|
||||||
)
|
)
|
||||||
} catch (e: ActivityNotFoundException) {
|
} catch (e: ActivityNotFoundException) {
|
||||||
view?.showMessage(R.string.error_play_services_not_available)
|
view?.showMessage(R.string.error_play_services_not_available)
|
||||||
@ -261,6 +246,7 @@ class AuthenticateCloudPresenter @Inject constructor( //
|
|||||||
private inner class OnedriveAuthStrategy : AuthStrategy {
|
private inner class OnedriveAuthStrategy : AuthStrategy {
|
||||||
|
|
||||||
private var authenticationStarted = false
|
private var authenticationStarted = false
|
||||||
|
|
||||||
override fun supports(cloud: CloudModel): Boolean {
|
override fun supports(cloud: CloudModel): Boolean {
|
||||||
return cloud.cloudType() == CloudTypeModel.ONEDRIVE
|
return cloud.cloudType() == CloudTypeModel.ONEDRIVE
|
||||||
}
|
}
|
||||||
@ -276,107 +262,11 @@ class AuthenticateCloudPresenter @Inject constructor( //
|
|||||||
|
|
||||||
Toast.makeText(context(), R.string.notification_authenticating, Toast.LENGTH_SHORT).show()
|
Toast.makeText(context(), R.string.notification_authenticating, Toast.LENGTH_SHORT).show()
|
||||||
|
|
||||||
PublicClientApplication.createMultipleAccountPublicClientApplication(
|
OnedriveAuthentication.refreshOrCheckAuth(activity(), cloud.toCloud() as OnedriveCloud, { authenticatedCloud ->
|
||||||
context(),
|
getUsernameAndSuceedAuthentication(authenticatedCloud)
|
||||||
R.raw.auth_config_onedrive,
|
}, {
|
||||||
object : IPublicClientApplication.IMultipleAccountApplicationCreatedListener {
|
failAuthentication(cloud.name())
|
||||||
override fun onCreated(application: IMultipleAccountPublicClientApplication) {
|
})
|
||||||
application.getAccounts(object : IPublicClientApplication.LoadAccountsCallback {
|
|
||||||
override fun onTaskCompleted(accounts: List<IAccount>) {
|
|
||||||
if (accounts.isEmpty()) {
|
|
||||||
application.acquireToken(activity(), onedriveScopes(), getAuthInteractiveCallback(cloud))
|
|
||||||
} else {
|
|
||||||
accounts.find { account -> account.username == cloud.username() }?.let {
|
|
||||||
application.acquireTokenSilentAsync(
|
|
||||||
onedriveScopes(),
|
|
||||||
it,
|
|
||||||
"https://login.microsoftonline.com/common",
|
|
||||||
getAuthSilentCallback(cloud, application)
|
|
||||||
)
|
|
||||||
} ?: application.acquireToken(activity(), onedriveScopes(), getAuthInteractiveCallback(cloud))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onError(e: MsalException) {
|
|
||||||
Timber.tag("AuthenticateCloudPresenter").e(e, "Error to get accounts")
|
|
||||||
failAuthentication(cloud.name())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onError(e: MsalException) {
|
|
||||||
Timber.tag("AuthenticateCloudPresenter").i(e, "Error in configuration")
|
|
||||||
failAuthentication(cloud.name())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getAuthSilentCallback(cloud: CloudModel, application: IMultipleAccountPublicClientApplication): AuthenticationCallback {
|
|
||||||
return object : AuthenticationCallback {
|
|
||||||
|
|
||||||
override fun onSuccess(authenticationResult: IAuthenticationResult) {
|
|
||||||
Timber.tag("AuthenticateCloudPresenter").i("Successfully authenticated")
|
|
||||||
handleAuthenticationResult(cloud, authenticationResult.accessToken)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onError(e: MsalException) {
|
|
||||||
Timber.tag("AuthenticateCloudPresenter").e(e, "Failed to acquireToken")
|
|
||||||
when (e) {
|
|
||||||
is MsalClientException -> {
|
|
||||||
/* Exception inside MSAL, more info inside MsalError.java */
|
|
||||||
failAuthentication(cloud.name())
|
|
||||||
}
|
|
||||||
is MsalServiceException -> {
|
|
||||||
/* Exception when communicating with the STS, likely config issue */
|
|
||||||
failAuthentication(cloud.name())
|
|
||||||
}
|
|
||||||
is MsalUiRequiredException -> {
|
|
||||||
/* Tokens expired or no session, retry with interactive */
|
|
||||||
application.acquireToken(activity(), onedriveScopes(), getAuthInteractiveCallback(cloud))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCancel() {
|
|
||||||
Timber.tag("AuthenticateCloudPresenter").i("User cancelled login")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getAuthInteractiveCallback(cloud: CloudModel): AuthenticationCallback {
|
|
||||||
return object : AuthenticationCallback {
|
|
||||||
|
|
||||||
override fun onSuccess(authenticationResult: IAuthenticationResult) {
|
|
||||||
Timber.tag("AuthenticateCloudPresenter").i("Successfully authenticated")
|
|
||||||
handleAuthenticationResult(cloud, authenticationResult.accessToken, authenticationResult.account.username)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onError(e: MsalException) {
|
|
||||||
Timber.tag("AuthenticateCloudPresenter").e(e, "Successfully authenticated")
|
|
||||||
failAuthentication(cloud.name())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCancel() {
|
|
||||||
Timber.tag("AuthenticateCloudPresenter").i("User cancelled login")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleAuthenticationResult(cloud: CloudModel, accessToken: String) {
|
|
||||||
getUsernameAndSuceedAuthentication( //
|
|
||||||
OnedriveCloud.aCopyOf(cloud.toCloud() as OnedriveCloud) //
|
|
||||||
.withAccessToken(encrypt(accessToken)) //
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleAuthenticationResult(cloud: CloudModel, accessToken: String, username: String) {
|
|
||||||
getUsernameAndSuceedAuthentication( //
|
|
||||||
OnedriveCloud.aCopyOf(cloud.toCloud() as OnedriveCloud) //
|
|
||||||
.withAccessToken(encrypt(accessToken)) //
|
|
||||||
.withUsername(username)
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -37,6 +37,11 @@ class ChooseCloudServicePresenter @Inject constructor( //
|
|||||||
|
|
||||||
if (BuildConfig.FLAVOR == "fdroid") {
|
if (BuildConfig.FLAVOR == "fdroid") {
|
||||||
cloudTypeModels.remove(CloudTypeModel.GOOGLE_DRIVE)
|
cloudTypeModels.remove(CloudTypeModel.GOOGLE_DRIVE)
|
||||||
|
} else if (BuildConfig.FLAVOR == "fdroidmain") {
|
||||||
|
cloudTypeModels.remove(CloudTypeModel.GOOGLE_DRIVE)
|
||||||
|
cloudTypeModels.remove(CloudTypeModel.DROPBOX)
|
||||||
|
cloudTypeModels.remove(CloudTypeModel.ONEDRIVE)
|
||||||
|
cloudTypeModels.remove(CloudTypeModel.PCLOUD)
|
||||||
}
|
}
|
||||||
|
|
||||||
view?.render(cloudTypeModels)
|
view?.render(cloudTypeModels)
|
||||||
|
@ -4,13 +4,6 @@ import android.content.ActivityNotFoundException
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import com.microsoft.identity.client.AuthenticationCallback
|
|
||||||
import com.microsoft.identity.client.IAccount
|
|
||||||
import com.microsoft.identity.client.IAuthenticationResult
|
|
||||||
import com.microsoft.identity.client.IMultipleAccountPublicClientApplication
|
|
||||||
import com.microsoft.identity.client.IPublicClientApplication
|
|
||||||
import com.microsoft.identity.client.PublicClientApplication
|
|
||||||
import com.microsoft.identity.client.exception.MsalException
|
|
||||||
import org.cryptomator.domain.Cloud
|
import org.cryptomator.domain.Cloud
|
||||||
import org.cryptomator.domain.LocalStorageCloud
|
import org.cryptomator.domain.LocalStorageCloud
|
||||||
import org.cryptomator.domain.OnedriveCloud
|
import org.cryptomator.domain.OnedriveCloud
|
||||||
@ -137,49 +130,11 @@ class CloudConnectionListPresenter @Inject constructor( //
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun addOnedriveCloud() {
|
private fun addOnedriveCloud() {
|
||||||
PublicClientApplication.createMultipleAccountPublicClientApplication(
|
OnedriveAuthentication.getAuthenticatedOnedriveCloud(activity(), { cloud ->
|
||||||
context(),
|
saveOnedriveCloud(cloud)
|
||||||
R.raw.auth_config_onedrive,
|
}, { e ->
|
||||||
object : IPublicClientApplication.IMultipleAccountApplicationCreatedListener {
|
showError(e)
|
||||||
override fun onCreated(application: IMultipleAccountPublicClientApplication) {
|
})
|
||||||
application.getAccounts(object : IPublicClientApplication.LoadAccountsCallback {
|
|
||||||
override fun onTaskCompleted(accounts: List<IAccount>) {
|
|
||||||
application.acquireToken(activity(), AuthenticateCloudPresenter.onedriveScopes(), getAuthInteractiveCallback())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onError(e: MsalException) {
|
|
||||||
Timber.tag("AuthenticateCloudPresenter").e(e, "Error to get accounts")
|
|
||||||
showError(e);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onError(e: MsalException) {
|
|
||||||
Timber.tag("AuthenticateCloudPresenter").i(e, "Error in configuration")
|
|
||||||
showError(e);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getAuthInteractiveCallback(): AuthenticationCallback {
|
|
||||||
return object : AuthenticationCallback {
|
|
||||||
|
|
||||||
override fun onSuccess(authenticationResult: IAuthenticationResult) {
|
|
||||||
Timber.tag("AuthenticateCloudPresenter").i("Successfully authenticated")
|
|
||||||
val accessToken = CredentialCryptor.getInstance(context()).encrypt(authenticationResult.accessToken)
|
|
||||||
val onedriveSkeleton = OnedriveCloud.aOnedriveCloud().withAccessToken(accessToken).withUsername(authenticationResult.account.username).build()
|
|
||||||
saveOnedriveCloud(onedriveSkeleton)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onError(e: MsalException) {
|
|
||||||
Timber.tag("AuthenticateCloudPresenter").e(e, "Successfully authenticated")
|
|
||||||
showError(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCancel() {
|
|
||||||
Timber.tag("AuthenticateCloudPresenter").i("User cancelled login")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun saveOnedriveCloud(onedriveSkeleton: OnedriveCloud) {
|
private fun saveOnedriveCloud(onedriveSkeleton: OnedriveCloud) {
|
||||||
|
@ -140,9 +140,28 @@ class CloudSettingsPresenter @Inject constructor( //
|
|||||||
it.add(aS3Cloud())
|
it.add(aS3Cloud())
|
||||||
it.add(aLocalCloud())
|
it.add(aLocalCloud())
|
||||||
}
|
}
|
||||||
|
.filter { cloud -> !(BuildConfig.FLAVOR == "fdroidmain" && excludeApiCloudsInFdroidMain(cloud.cloudType())) } //
|
||||||
view?.render(cloudModel)
|
view?.render(cloudModel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun excludeApiCloudsInFdroidMain(cloudType: CloudTypeModel): Boolean {
|
||||||
|
return when (cloudType) {
|
||||||
|
CloudTypeModel.GOOGLE_DRIVE -> {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
CloudTypeModel.ONEDRIVE -> {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
CloudTypeModel.DROPBOX -> {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
CloudTypeModel.PCLOUD -> {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun aOnedriveCloud(): OnedriveCloudModel {
|
private fun aOnedriveCloud(): OnedriveCloudModel {
|
||||||
return OnedriveCloudModel(OnedriveCloud.aOnedriveCloud().build())
|
return OnedriveCloudModel(OnedriveCloud.aOnedriveCloud().build())
|
||||||
}
|
}
|
||||||
|
@ -83,6 +83,9 @@ class SettingsPresenter @Inject internal constructor(
|
|||||||
"fdroid" -> {
|
"fdroid" -> {
|
||||||
"F-Droid"
|
"F-Droid"
|
||||||
}
|
}
|
||||||
|
"fdroidmain" -> {
|
||||||
|
"F-Droid Main Repo Edition"
|
||||||
|
}
|
||||||
else -> "Google Play"
|
else -> "Google Play"
|
||||||
}
|
}
|
||||||
return StringBuilder().append("## ").append(context().getString(R.string.error_report_subject)).append("\n\n") //
|
return StringBuilder().append("## ").append(context().getString(R.string.error_report_subject)).append("\n\n") //
|
||||||
|
@ -123,7 +123,7 @@ class VaultListPresenter @Inject constructor( //
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun checkLicense() {
|
private fun checkLicense() {
|
||||||
if (BuildConfig.FLAVOR == "apkstore" || BuildConfig.FLAVOR == "fdroid") {
|
if (BuildConfig.FLAVOR == "apkstore" || BuildConfig.FLAVOR == "fdroid" || BuildConfig.FLAVOR == "fdroidmain") {
|
||||||
licenseCheckUseCase //
|
licenseCheckUseCase //
|
||||||
.withLicense("") //
|
.withLicense("") //
|
||||||
.run(object : NoOpResultHandler<LicenseCheck>() {
|
.run(object : NoOpResultHandler<LicenseCheck>() {
|
||||||
|
@ -157,7 +157,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
|
|||||||
(findPreference(SharedPreferencesHandler.MAIL) as Preference?)?.title = format(getString(R.string.screen_settings_license_mail), sharedPreferencesHandler.mail())
|
(findPreference(SharedPreferencesHandler.MAIL) as Preference?)?.title = format(getString(R.string.screen_settings_license_mail), sharedPreferencesHandler.mail())
|
||||||
setupUpdateCheck()
|
setupUpdateCheck()
|
||||||
}
|
}
|
||||||
"fdroid" -> {
|
"fdroid", "fdroidmain" -> {
|
||||||
(findPreference(SharedPreferencesHandler.MAIL) as Preference?)?.title = format(getString(R.string.screen_settings_license_mail), sharedPreferencesHandler.mail())
|
(findPreference(SharedPreferencesHandler.MAIL) as Preference?)?.title = format(getString(R.string.screen_settings_license_mail), sharedPreferencesHandler.mail())
|
||||||
removeUpdateCheck()
|
removeUpdateCheck()
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user