From 7ad0178e8b93683b7c664810c13752f7e14ad95b Mon Sep 17 00:00:00 2001 From: Julian Raufelder Date: Tue, 17 May 2022 00:24:18 +0200 Subject: [PATCH 01/24] Preparations for F-Droid main repo --- data/build.gradle | 31 +- .../cloud/dropbox/DropboxClientFactory.kt | 0 .../dropbox/DropboxCloudContentRepository.kt | 0 .../DropboxCloudContentRepositoryFactory.java | 0 .../cloud/dropbox/DropboxCloudNodeFactory.kt | 0 .../data/cloud/dropbox/DropboxFile.kt | 0 .../data/cloud/dropbox/DropboxFolder.kt | 0 .../data/cloud/dropbox/DropboxImpl.kt | 0 .../data/cloud/dropbox/DropboxNode.kt | 0 .../data/cloud/dropbox/RootDropboxFolder.kt | 0 .../cloud/onedrive/OnedriveClientFactory.kt | 0 .../OnedriveCloudContentRepository.kt | 0 ...OnedriveCloudContentRepositoryFactory.java | 0 .../onedrive/OnedriveCloudNodeFactory.kt | 0 .../data/cloud/onedrive/OnedriveFile.kt | 0 .../data/cloud/onedrive/OnedriveFolder.kt | 0 .../data/cloud/onedrive/OnedriveIdCache.kt | 0 .../cloud/onedrive/OnedriveIdCloudNode.kt | 0 .../data/cloud/onedrive/OnedriveImpl.kt | 0 .../data/cloud/onedrive/OnedriveNode.kt | 0 .../data/cloud/onedrive/RootOnedriveFolder.kt | 0 .../data/cloud/pcloud/PCloudApiError.kt | 0 .../data/cloud/pcloud/PCloudClientFactory.kt | 0 .../cloud/pcloud/PCloudContentRepository.kt | 0 .../PCloudContentRepositoryFactory.java | 0 .../data/cloud/pcloud/PCloudFile.kt | 0 .../data/cloud/pcloud/PCloudFolder.kt | 0 .../data/cloud/pcloud/PCloudImpl.kt | 0 .../data/cloud/pcloud/PCloudNode.kt | 0 .../data/cloud/pcloud/PCloudNodeFactory.kt | 0 .../data/cloud/pcloud/RootPCloudFolder.kt | 0 .../CloudContentRepositoryFactories.java | 4 +- .../FixedGoogleAccountCredential.java | 0 .../googledrive/GoogleDriveClientFactory.kt | 0 .../GoogleDriveCloudContentRepository.kt | 0 ...gleDriveCloudContentRepositoryFactory.java | 0 .../GoogleDriveCloudNodeFactory.kt | 0 .../data/cloud/googledrive/GoogleDriveFile.kt | 0 .../cloud/googledrive/GoogleDriveFolder.kt | 0 .../cloud/googledrive/GoogleDriveIdCache.kt | 0 .../googledrive/GoogleDriveIdCloudNode.kt | 0 .../data/cloud/googledrive/GoogleDriveImpl.kt | 0 .../data/cloud/googledrive/GoogleDriveNode.kt | 0 .../googledrive/RootGoogleDriveFolder.kt | 0 ...erredBytesAwareGoogleContentInputStream.kt | 0 .../CloudContentRepositoryFactories.java | 0 .../CloudContentRepositoryFactories.java | 40 ++ presentation/build.gradle | 27 +- .../presenter/DropboxAuthHelper.kt | 17 + .../presenter/OnedriveAuthentication.kt | 139 ++++ .../presenter/GoogleAuthHelper.kt | 13 + .../presenter/GoogleAuthHelper.kt | 11 + .../presenter/DropboxAuthHelper.kt | 15 + .../presenter/OnedriveAuthentication.kt | 18 + .../presenter/AuthenticateCloudPresenter.kt | 610 ------------------ .../presentation/CryptomatorApp.kt | 3 + .../presenter/AuthenticateCloudPresenter.kt | 128 +--- .../presenter/ChooseCloudServicePresenter.kt | 5 + .../presenter/CloudConnectionListPresenter.kt | 55 +- .../presenter/CloudSettingsPresenter.kt | 19 + .../presenter/SettingsPresenter.kt | 3 + .../presenter/VaultListPresenter.kt | 2 +- .../ui/fragment/SettingsFragment.kt | 2 +- 63 files changed, 346 insertions(+), 796 deletions(-) rename data/src/{main => apiKey}/java/org/cryptomator/data/cloud/dropbox/DropboxClientFactory.kt (100%) rename data/src/{main => apiKey}/java/org/cryptomator/data/cloud/dropbox/DropboxCloudContentRepository.kt (100%) rename data/src/{main => apiKey}/java/org/cryptomator/data/cloud/dropbox/DropboxCloudContentRepositoryFactory.java (100%) rename data/src/{main => apiKey}/java/org/cryptomator/data/cloud/dropbox/DropboxCloudNodeFactory.kt (100%) rename data/src/{main => apiKey}/java/org/cryptomator/data/cloud/dropbox/DropboxFile.kt (100%) rename data/src/{main => apiKey}/java/org/cryptomator/data/cloud/dropbox/DropboxFolder.kt (100%) rename data/src/{main => apiKey}/java/org/cryptomator/data/cloud/dropbox/DropboxImpl.kt (100%) rename data/src/{main => apiKey}/java/org/cryptomator/data/cloud/dropbox/DropboxNode.kt (100%) rename data/src/{main => apiKey}/java/org/cryptomator/data/cloud/dropbox/RootDropboxFolder.kt (100%) rename data/src/{main => apiKey}/java/org/cryptomator/data/cloud/onedrive/OnedriveClientFactory.kt (100%) rename data/src/{main => apiKey}/java/org/cryptomator/data/cloud/onedrive/OnedriveCloudContentRepository.kt (100%) rename data/src/{main => apiKey}/java/org/cryptomator/data/cloud/onedrive/OnedriveCloudContentRepositoryFactory.java (100%) rename data/src/{main => apiKey}/java/org/cryptomator/data/cloud/onedrive/OnedriveCloudNodeFactory.kt (100%) rename data/src/{main => apiKey}/java/org/cryptomator/data/cloud/onedrive/OnedriveFile.kt (100%) rename data/src/{main => apiKey}/java/org/cryptomator/data/cloud/onedrive/OnedriveFolder.kt (100%) rename data/src/{main => apiKey}/java/org/cryptomator/data/cloud/onedrive/OnedriveIdCache.kt (100%) rename data/src/{main => apiKey}/java/org/cryptomator/data/cloud/onedrive/OnedriveIdCloudNode.kt (100%) rename data/src/{main => apiKey}/java/org/cryptomator/data/cloud/onedrive/OnedriveImpl.kt (100%) rename data/src/{main => apiKey}/java/org/cryptomator/data/cloud/onedrive/OnedriveNode.kt (100%) rename data/src/{main => apiKey}/java/org/cryptomator/data/cloud/onedrive/RootOnedriveFolder.kt (100%) rename data/src/{main => apiKey}/java/org/cryptomator/data/cloud/pcloud/PCloudApiError.kt (100%) rename data/src/{main => apiKey}/java/org/cryptomator/data/cloud/pcloud/PCloudClientFactory.kt (100%) rename data/src/{main => apiKey}/java/org/cryptomator/data/cloud/pcloud/PCloudContentRepository.kt (100%) rename data/src/{main => apiKey}/java/org/cryptomator/data/cloud/pcloud/PCloudContentRepositoryFactory.java (100%) rename data/src/{main => apiKey}/java/org/cryptomator/data/cloud/pcloud/PCloudFile.kt (100%) rename data/src/{main => apiKey}/java/org/cryptomator/data/cloud/pcloud/PCloudFolder.kt (100%) rename data/src/{main => apiKey}/java/org/cryptomator/data/cloud/pcloud/PCloudImpl.kt (100%) rename data/src/{main => apiKey}/java/org/cryptomator/data/cloud/pcloud/PCloudNode.kt (100%) rename data/src/{main => apiKey}/java/org/cryptomator/data/cloud/pcloud/PCloudNodeFactory.kt (100%) rename data/src/{main => apiKey}/java/org/cryptomator/data/cloud/pcloud/RootPCloudFolder.kt (100%) rename data/src/{notFoss => apkStorePlaystore}/java/org/cryptomator/data/cloud/CloudContentRepositoryFactories.java (100%) rename data/src/{notFoss => apkStorePlaystore}/java/org/cryptomator/data/cloud/googledrive/FixedGoogleAccountCredential.java (100%) rename data/src/{notFoss => apkStorePlaystore}/java/org/cryptomator/data/cloud/googledrive/GoogleDriveClientFactory.kt (100%) rename data/src/{notFoss => apkStorePlaystore}/java/org/cryptomator/data/cloud/googledrive/GoogleDriveCloudContentRepository.kt (100%) rename data/src/{notFoss => apkStorePlaystore}/java/org/cryptomator/data/cloud/googledrive/GoogleDriveCloudContentRepositoryFactory.java (100%) rename data/src/{notFoss => apkStorePlaystore}/java/org/cryptomator/data/cloud/googledrive/GoogleDriveCloudNodeFactory.kt (100%) rename data/src/{notFoss => apkStorePlaystore}/java/org/cryptomator/data/cloud/googledrive/GoogleDriveFile.kt (100%) rename data/src/{notFoss => apkStorePlaystore}/java/org/cryptomator/data/cloud/googledrive/GoogleDriveFolder.kt (100%) rename data/src/{notFoss => apkStorePlaystore}/java/org/cryptomator/data/cloud/googledrive/GoogleDriveIdCache.kt (100%) rename data/src/{notFoss => apkStorePlaystore}/java/org/cryptomator/data/cloud/googledrive/GoogleDriveIdCloudNode.kt (100%) rename data/src/{notFoss => apkStorePlaystore}/java/org/cryptomator/data/cloud/googledrive/GoogleDriveImpl.kt (100%) rename data/src/{notFoss => apkStorePlaystore}/java/org/cryptomator/data/cloud/googledrive/GoogleDriveNode.kt (100%) rename data/src/{notFoss => apkStorePlaystore}/java/org/cryptomator/data/cloud/googledrive/RootGoogleDriveFolder.kt (100%) rename data/src/{notFoss => apkStorePlaystore}/java/org/cryptomator/data/cloud/googledrive/TransferredBytesAwareGoogleContentInputStream.kt (100%) rename data/src/{foss => fdroid}/java/org/cryptomator/data/cloud/CloudContentRepositoryFactories.java (100%) create mode 100644 data/src/fdroidmain/java/org/cryptomator/data/cloud/CloudContentRepositoryFactories.java create mode 100644 presentation/src/apiKey/java/org/cryptomator/presentation/presenter/DropboxAuthHelper.kt create mode 100644 presentation/src/apiKey/java/org/cryptomator/presentation/presenter/OnedriveAuthentication.kt create mode 100644 presentation/src/apkStorePlaystore/java/org/cryptomator/presentation/presenter/GoogleAuthHelper.kt create mode 100644 presentation/src/fdroidAndfdroidmain/java/org/cryptomator/presentation/presenter/GoogleAuthHelper.kt create mode 100644 presentation/src/fdroidmain/java/org/cryptomator/presentation/presenter/DropboxAuthHelper.kt create mode 100644 presentation/src/fdroidmain/java/org/cryptomator/presentation/presenter/OnedriveAuthentication.kt delete mode 100644 presentation/src/foss/java/org/cryptomator/presentation/presenter/AuthenticateCloudPresenter.kt rename presentation/src/{notFoss => main}/java/org/cryptomator/presentation/presenter/AuthenticateCloudPresenter.kt (78%) diff --git a/data/build.gradle b/data/build.gradle index cd56b4ef..bbd9f47e 100644 --- a/data/build.gradle +++ b/data/build.gradle @@ -53,19 +53,27 @@ android { fdroid { dimension "version" } + + fdroidmain { + dimension "version" + } } sourceSets { 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 { - 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 { - 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 { @@ -95,7 +103,9 @@ dependencies { implementation project(':domain') 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 @@ -113,9 +123,16 @@ dependencies { implementation dependencies.jsonWebTokenJson // cloud - implementation dependencies.dropbox - implementation dependencies.msgraphAuth - implementation dependencies.msgraph + playstoreImplementation dependencies.dropbox + apkstoreImplementation dependencies.dropbox + fdroidImplementation dependencies.dropbox + + playstoreImplementation dependencies.msgraphAuth + apkstoreImplementation dependencies.msgraphAuth + fdroidImplementation dependencies.msgraphAuth + playstoreImplementation dependencies.msgraph + apkstoreImplementation dependencies.msgraph + fdroidImplementation dependencies.msgraph implementation dependencies.stax api dependencies.minIo diff --git a/data/src/main/java/org/cryptomator/data/cloud/dropbox/DropboxClientFactory.kt b/data/src/apiKey/java/org/cryptomator/data/cloud/dropbox/DropboxClientFactory.kt similarity index 100% rename from data/src/main/java/org/cryptomator/data/cloud/dropbox/DropboxClientFactory.kt rename to data/src/apiKey/java/org/cryptomator/data/cloud/dropbox/DropboxClientFactory.kt diff --git a/data/src/main/java/org/cryptomator/data/cloud/dropbox/DropboxCloudContentRepository.kt b/data/src/apiKey/java/org/cryptomator/data/cloud/dropbox/DropboxCloudContentRepository.kt similarity index 100% rename from data/src/main/java/org/cryptomator/data/cloud/dropbox/DropboxCloudContentRepository.kt rename to data/src/apiKey/java/org/cryptomator/data/cloud/dropbox/DropboxCloudContentRepository.kt diff --git a/data/src/main/java/org/cryptomator/data/cloud/dropbox/DropboxCloudContentRepositoryFactory.java b/data/src/apiKey/java/org/cryptomator/data/cloud/dropbox/DropboxCloudContentRepositoryFactory.java similarity index 100% rename from data/src/main/java/org/cryptomator/data/cloud/dropbox/DropboxCloudContentRepositoryFactory.java rename to data/src/apiKey/java/org/cryptomator/data/cloud/dropbox/DropboxCloudContentRepositoryFactory.java diff --git a/data/src/main/java/org/cryptomator/data/cloud/dropbox/DropboxCloudNodeFactory.kt b/data/src/apiKey/java/org/cryptomator/data/cloud/dropbox/DropboxCloudNodeFactory.kt similarity index 100% rename from data/src/main/java/org/cryptomator/data/cloud/dropbox/DropboxCloudNodeFactory.kt rename to data/src/apiKey/java/org/cryptomator/data/cloud/dropbox/DropboxCloudNodeFactory.kt diff --git a/data/src/main/java/org/cryptomator/data/cloud/dropbox/DropboxFile.kt b/data/src/apiKey/java/org/cryptomator/data/cloud/dropbox/DropboxFile.kt similarity index 100% rename from data/src/main/java/org/cryptomator/data/cloud/dropbox/DropboxFile.kt rename to data/src/apiKey/java/org/cryptomator/data/cloud/dropbox/DropboxFile.kt diff --git a/data/src/main/java/org/cryptomator/data/cloud/dropbox/DropboxFolder.kt b/data/src/apiKey/java/org/cryptomator/data/cloud/dropbox/DropboxFolder.kt similarity index 100% rename from data/src/main/java/org/cryptomator/data/cloud/dropbox/DropboxFolder.kt rename to data/src/apiKey/java/org/cryptomator/data/cloud/dropbox/DropboxFolder.kt diff --git a/data/src/main/java/org/cryptomator/data/cloud/dropbox/DropboxImpl.kt b/data/src/apiKey/java/org/cryptomator/data/cloud/dropbox/DropboxImpl.kt similarity index 100% rename from data/src/main/java/org/cryptomator/data/cloud/dropbox/DropboxImpl.kt rename to data/src/apiKey/java/org/cryptomator/data/cloud/dropbox/DropboxImpl.kt diff --git a/data/src/main/java/org/cryptomator/data/cloud/dropbox/DropboxNode.kt b/data/src/apiKey/java/org/cryptomator/data/cloud/dropbox/DropboxNode.kt similarity index 100% rename from data/src/main/java/org/cryptomator/data/cloud/dropbox/DropboxNode.kt rename to data/src/apiKey/java/org/cryptomator/data/cloud/dropbox/DropboxNode.kt diff --git a/data/src/main/java/org/cryptomator/data/cloud/dropbox/RootDropboxFolder.kt b/data/src/apiKey/java/org/cryptomator/data/cloud/dropbox/RootDropboxFolder.kt similarity index 100% rename from data/src/main/java/org/cryptomator/data/cloud/dropbox/RootDropboxFolder.kt rename to data/src/apiKey/java/org/cryptomator/data/cloud/dropbox/RootDropboxFolder.kt diff --git a/data/src/main/java/org/cryptomator/data/cloud/onedrive/OnedriveClientFactory.kt b/data/src/apiKey/java/org/cryptomator/data/cloud/onedrive/OnedriveClientFactory.kt similarity index 100% rename from data/src/main/java/org/cryptomator/data/cloud/onedrive/OnedriveClientFactory.kt rename to data/src/apiKey/java/org/cryptomator/data/cloud/onedrive/OnedriveClientFactory.kt diff --git a/data/src/main/java/org/cryptomator/data/cloud/onedrive/OnedriveCloudContentRepository.kt b/data/src/apiKey/java/org/cryptomator/data/cloud/onedrive/OnedriveCloudContentRepository.kt similarity index 100% rename from data/src/main/java/org/cryptomator/data/cloud/onedrive/OnedriveCloudContentRepository.kt rename to data/src/apiKey/java/org/cryptomator/data/cloud/onedrive/OnedriveCloudContentRepository.kt diff --git a/data/src/main/java/org/cryptomator/data/cloud/onedrive/OnedriveCloudContentRepositoryFactory.java b/data/src/apiKey/java/org/cryptomator/data/cloud/onedrive/OnedriveCloudContentRepositoryFactory.java similarity index 100% rename from data/src/main/java/org/cryptomator/data/cloud/onedrive/OnedriveCloudContentRepositoryFactory.java rename to data/src/apiKey/java/org/cryptomator/data/cloud/onedrive/OnedriveCloudContentRepositoryFactory.java diff --git a/data/src/main/java/org/cryptomator/data/cloud/onedrive/OnedriveCloudNodeFactory.kt b/data/src/apiKey/java/org/cryptomator/data/cloud/onedrive/OnedriveCloudNodeFactory.kt similarity index 100% rename from data/src/main/java/org/cryptomator/data/cloud/onedrive/OnedriveCloudNodeFactory.kt rename to data/src/apiKey/java/org/cryptomator/data/cloud/onedrive/OnedriveCloudNodeFactory.kt diff --git a/data/src/main/java/org/cryptomator/data/cloud/onedrive/OnedriveFile.kt b/data/src/apiKey/java/org/cryptomator/data/cloud/onedrive/OnedriveFile.kt similarity index 100% rename from data/src/main/java/org/cryptomator/data/cloud/onedrive/OnedriveFile.kt rename to data/src/apiKey/java/org/cryptomator/data/cloud/onedrive/OnedriveFile.kt diff --git a/data/src/main/java/org/cryptomator/data/cloud/onedrive/OnedriveFolder.kt b/data/src/apiKey/java/org/cryptomator/data/cloud/onedrive/OnedriveFolder.kt similarity index 100% rename from data/src/main/java/org/cryptomator/data/cloud/onedrive/OnedriveFolder.kt rename to data/src/apiKey/java/org/cryptomator/data/cloud/onedrive/OnedriveFolder.kt diff --git a/data/src/main/java/org/cryptomator/data/cloud/onedrive/OnedriveIdCache.kt b/data/src/apiKey/java/org/cryptomator/data/cloud/onedrive/OnedriveIdCache.kt similarity index 100% rename from data/src/main/java/org/cryptomator/data/cloud/onedrive/OnedriveIdCache.kt rename to data/src/apiKey/java/org/cryptomator/data/cloud/onedrive/OnedriveIdCache.kt diff --git a/data/src/main/java/org/cryptomator/data/cloud/onedrive/OnedriveIdCloudNode.kt b/data/src/apiKey/java/org/cryptomator/data/cloud/onedrive/OnedriveIdCloudNode.kt similarity index 100% rename from data/src/main/java/org/cryptomator/data/cloud/onedrive/OnedriveIdCloudNode.kt rename to data/src/apiKey/java/org/cryptomator/data/cloud/onedrive/OnedriveIdCloudNode.kt diff --git a/data/src/main/java/org/cryptomator/data/cloud/onedrive/OnedriveImpl.kt b/data/src/apiKey/java/org/cryptomator/data/cloud/onedrive/OnedriveImpl.kt similarity index 100% rename from data/src/main/java/org/cryptomator/data/cloud/onedrive/OnedriveImpl.kt rename to data/src/apiKey/java/org/cryptomator/data/cloud/onedrive/OnedriveImpl.kt diff --git a/data/src/main/java/org/cryptomator/data/cloud/onedrive/OnedriveNode.kt b/data/src/apiKey/java/org/cryptomator/data/cloud/onedrive/OnedriveNode.kt similarity index 100% rename from data/src/main/java/org/cryptomator/data/cloud/onedrive/OnedriveNode.kt rename to data/src/apiKey/java/org/cryptomator/data/cloud/onedrive/OnedriveNode.kt diff --git a/data/src/main/java/org/cryptomator/data/cloud/onedrive/RootOnedriveFolder.kt b/data/src/apiKey/java/org/cryptomator/data/cloud/onedrive/RootOnedriveFolder.kt similarity index 100% rename from data/src/main/java/org/cryptomator/data/cloud/onedrive/RootOnedriveFolder.kt rename to data/src/apiKey/java/org/cryptomator/data/cloud/onedrive/RootOnedriveFolder.kt diff --git a/data/src/main/java/org/cryptomator/data/cloud/pcloud/PCloudApiError.kt b/data/src/apiKey/java/org/cryptomator/data/cloud/pcloud/PCloudApiError.kt similarity index 100% rename from data/src/main/java/org/cryptomator/data/cloud/pcloud/PCloudApiError.kt rename to data/src/apiKey/java/org/cryptomator/data/cloud/pcloud/PCloudApiError.kt diff --git a/data/src/main/java/org/cryptomator/data/cloud/pcloud/PCloudClientFactory.kt b/data/src/apiKey/java/org/cryptomator/data/cloud/pcloud/PCloudClientFactory.kt similarity index 100% rename from data/src/main/java/org/cryptomator/data/cloud/pcloud/PCloudClientFactory.kt rename to data/src/apiKey/java/org/cryptomator/data/cloud/pcloud/PCloudClientFactory.kt diff --git a/data/src/main/java/org/cryptomator/data/cloud/pcloud/PCloudContentRepository.kt b/data/src/apiKey/java/org/cryptomator/data/cloud/pcloud/PCloudContentRepository.kt similarity index 100% rename from data/src/main/java/org/cryptomator/data/cloud/pcloud/PCloudContentRepository.kt rename to data/src/apiKey/java/org/cryptomator/data/cloud/pcloud/PCloudContentRepository.kt diff --git a/data/src/main/java/org/cryptomator/data/cloud/pcloud/PCloudContentRepositoryFactory.java b/data/src/apiKey/java/org/cryptomator/data/cloud/pcloud/PCloudContentRepositoryFactory.java similarity index 100% rename from data/src/main/java/org/cryptomator/data/cloud/pcloud/PCloudContentRepositoryFactory.java rename to data/src/apiKey/java/org/cryptomator/data/cloud/pcloud/PCloudContentRepositoryFactory.java diff --git a/data/src/main/java/org/cryptomator/data/cloud/pcloud/PCloudFile.kt b/data/src/apiKey/java/org/cryptomator/data/cloud/pcloud/PCloudFile.kt similarity index 100% rename from data/src/main/java/org/cryptomator/data/cloud/pcloud/PCloudFile.kt rename to data/src/apiKey/java/org/cryptomator/data/cloud/pcloud/PCloudFile.kt diff --git a/data/src/main/java/org/cryptomator/data/cloud/pcloud/PCloudFolder.kt b/data/src/apiKey/java/org/cryptomator/data/cloud/pcloud/PCloudFolder.kt similarity index 100% rename from data/src/main/java/org/cryptomator/data/cloud/pcloud/PCloudFolder.kt rename to data/src/apiKey/java/org/cryptomator/data/cloud/pcloud/PCloudFolder.kt diff --git a/data/src/main/java/org/cryptomator/data/cloud/pcloud/PCloudImpl.kt b/data/src/apiKey/java/org/cryptomator/data/cloud/pcloud/PCloudImpl.kt similarity index 100% rename from data/src/main/java/org/cryptomator/data/cloud/pcloud/PCloudImpl.kt rename to data/src/apiKey/java/org/cryptomator/data/cloud/pcloud/PCloudImpl.kt diff --git a/data/src/main/java/org/cryptomator/data/cloud/pcloud/PCloudNode.kt b/data/src/apiKey/java/org/cryptomator/data/cloud/pcloud/PCloudNode.kt similarity index 100% rename from data/src/main/java/org/cryptomator/data/cloud/pcloud/PCloudNode.kt rename to data/src/apiKey/java/org/cryptomator/data/cloud/pcloud/PCloudNode.kt diff --git a/data/src/main/java/org/cryptomator/data/cloud/pcloud/PCloudNodeFactory.kt b/data/src/apiKey/java/org/cryptomator/data/cloud/pcloud/PCloudNodeFactory.kt similarity index 100% rename from data/src/main/java/org/cryptomator/data/cloud/pcloud/PCloudNodeFactory.kt rename to data/src/apiKey/java/org/cryptomator/data/cloud/pcloud/PCloudNodeFactory.kt diff --git a/data/src/main/java/org/cryptomator/data/cloud/pcloud/RootPCloudFolder.kt b/data/src/apiKey/java/org/cryptomator/data/cloud/pcloud/RootPCloudFolder.kt similarity index 100% rename from data/src/main/java/org/cryptomator/data/cloud/pcloud/RootPCloudFolder.kt rename to data/src/apiKey/java/org/cryptomator/data/cloud/pcloud/RootPCloudFolder.kt diff --git a/data/src/notFoss/java/org/cryptomator/data/cloud/CloudContentRepositoryFactories.java b/data/src/apkStorePlaystore/java/org/cryptomator/data/cloud/CloudContentRepositoryFactories.java similarity index 100% rename from data/src/notFoss/java/org/cryptomator/data/cloud/CloudContentRepositoryFactories.java rename to data/src/apkStorePlaystore/java/org/cryptomator/data/cloud/CloudContentRepositoryFactories.java index 918d4dfb..4e7a7284 100644 --- a/data/src/notFoss/java/org/cryptomator/data/cloud/CloudContentRepositoryFactories.java +++ b/data/src/apkStorePlaystore/java/org/cryptomator/data/cloud/CloudContentRepositoryFactories.java @@ -1,5 +1,7 @@ package org.cryptomator.data.cloud; +import static java.util.Arrays.asList; + import org.cryptomator.data.cloud.crypto.CryptoCloudContentRepositoryFactory; import org.cryptomator.data.cloud.dropbox.DropboxCloudContentRepositoryFactory; import org.cryptomator.data.cloud.googledrive.GoogleDriveCloudContentRepositoryFactory; @@ -16,8 +18,6 @@ import java.util.Iterator; import javax.inject.Inject; import javax.inject.Singleton; -import static java.util.Arrays.asList; - @Singleton public class CloudContentRepositoryFactories implements Iterable { diff --git a/data/src/notFoss/java/org/cryptomator/data/cloud/googledrive/FixedGoogleAccountCredential.java b/data/src/apkStorePlaystore/java/org/cryptomator/data/cloud/googledrive/FixedGoogleAccountCredential.java similarity index 100% rename from data/src/notFoss/java/org/cryptomator/data/cloud/googledrive/FixedGoogleAccountCredential.java rename to data/src/apkStorePlaystore/java/org/cryptomator/data/cloud/googledrive/FixedGoogleAccountCredential.java diff --git a/data/src/notFoss/java/org/cryptomator/data/cloud/googledrive/GoogleDriveClientFactory.kt b/data/src/apkStorePlaystore/java/org/cryptomator/data/cloud/googledrive/GoogleDriveClientFactory.kt similarity index 100% rename from data/src/notFoss/java/org/cryptomator/data/cloud/googledrive/GoogleDriveClientFactory.kt rename to data/src/apkStorePlaystore/java/org/cryptomator/data/cloud/googledrive/GoogleDriveClientFactory.kt diff --git a/data/src/notFoss/java/org/cryptomator/data/cloud/googledrive/GoogleDriveCloudContentRepository.kt b/data/src/apkStorePlaystore/java/org/cryptomator/data/cloud/googledrive/GoogleDriveCloudContentRepository.kt similarity index 100% rename from data/src/notFoss/java/org/cryptomator/data/cloud/googledrive/GoogleDriveCloudContentRepository.kt rename to data/src/apkStorePlaystore/java/org/cryptomator/data/cloud/googledrive/GoogleDriveCloudContentRepository.kt diff --git a/data/src/notFoss/java/org/cryptomator/data/cloud/googledrive/GoogleDriveCloudContentRepositoryFactory.java b/data/src/apkStorePlaystore/java/org/cryptomator/data/cloud/googledrive/GoogleDriveCloudContentRepositoryFactory.java similarity index 100% rename from data/src/notFoss/java/org/cryptomator/data/cloud/googledrive/GoogleDriveCloudContentRepositoryFactory.java rename to data/src/apkStorePlaystore/java/org/cryptomator/data/cloud/googledrive/GoogleDriveCloudContentRepositoryFactory.java diff --git a/data/src/notFoss/java/org/cryptomator/data/cloud/googledrive/GoogleDriveCloudNodeFactory.kt b/data/src/apkStorePlaystore/java/org/cryptomator/data/cloud/googledrive/GoogleDriveCloudNodeFactory.kt similarity index 100% rename from data/src/notFoss/java/org/cryptomator/data/cloud/googledrive/GoogleDriveCloudNodeFactory.kt rename to data/src/apkStorePlaystore/java/org/cryptomator/data/cloud/googledrive/GoogleDriveCloudNodeFactory.kt diff --git a/data/src/notFoss/java/org/cryptomator/data/cloud/googledrive/GoogleDriveFile.kt b/data/src/apkStorePlaystore/java/org/cryptomator/data/cloud/googledrive/GoogleDriveFile.kt similarity index 100% rename from data/src/notFoss/java/org/cryptomator/data/cloud/googledrive/GoogleDriveFile.kt rename to data/src/apkStorePlaystore/java/org/cryptomator/data/cloud/googledrive/GoogleDriveFile.kt diff --git a/data/src/notFoss/java/org/cryptomator/data/cloud/googledrive/GoogleDriveFolder.kt b/data/src/apkStorePlaystore/java/org/cryptomator/data/cloud/googledrive/GoogleDriveFolder.kt similarity index 100% rename from data/src/notFoss/java/org/cryptomator/data/cloud/googledrive/GoogleDriveFolder.kt rename to data/src/apkStorePlaystore/java/org/cryptomator/data/cloud/googledrive/GoogleDriveFolder.kt diff --git a/data/src/notFoss/java/org/cryptomator/data/cloud/googledrive/GoogleDriveIdCache.kt b/data/src/apkStorePlaystore/java/org/cryptomator/data/cloud/googledrive/GoogleDriveIdCache.kt similarity index 100% rename from data/src/notFoss/java/org/cryptomator/data/cloud/googledrive/GoogleDriveIdCache.kt rename to data/src/apkStorePlaystore/java/org/cryptomator/data/cloud/googledrive/GoogleDriveIdCache.kt diff --git a/data/src/notFoss/java/org/cryptomator/data/cloud/googledrive/GoogleDriveIdCloudNode.kt b/data/src/apkStorePlaystore/java/org/cryptomator/data/cloud/googledrive/GoogleDriveIdCloudNode.kt similarity index 100% rename from data/src/notFoss/java/org/cryptomator/data/cloud/googledrive/GoogleDriveIdCloudNode.kt rename to data/src/apkStorePlaystore/java/org/cryptomator/data/cloud/googledrive/GoogleDriveIdCloudNode.kt diff --git a/data/src/notFoss/java/org/cryptomator/data/cloud/googledrive/GoogleDriveImpl.kt b/data/src/apkStorePlaystore/java/org/cryptomator/data/cloud/googledrive/GoogleDriveImpl.kt similarity index 100% rename from data/src/notFoss/java/org/cryptomator/data/cloud/googledrive/GoogleDriveImpl.kt rename to data/src/apkStorePlaystore/java/org/cryptomator/data/cloud/googledrive/GoogleDriveImpl.kt diff --git a/data/src/notFoss/java/org/cryptomator/data/cloud/googledrive/GoogleDriveNode.kt b/data/src/apkStorePlaystore/java/org/cryptomator/data/cloud/googledrive/GoogleDriveNode.kt similarity index 100% rename from data/src/notFoss/java/org/cryptomator/data/cloud/googledrive/GoogleDriveNode.kt rename to data/src/apkStorePlaystore/java/org/cryptomator/data/cloud/googledrive/GoogleDriveNode.kt diff --git a/data/src/notFoss/java/org/cryptomator/data/cloud/googledrive/RootGoogleDriveFolder.kt b/data/src/apkStorePlaystore/java/org/cryptomator/data/cloud/googledrive/RootGoogleDriveFolder.kt similarity index 100% rename from data/src/notFoss/java/org/cryptomator/data/cloud/googledrive/RootGoogleDriveFolder.kt rename to data/src/apkStorePlaystore/java/org/cryptomator/data/cloud/googledrive/RootGoogleDriveFolder.kt diff --git a/data/src/notFoss/java/org/cryptomator/data/cloud/googledrive/TransferredBytesAwareGoogleContentInputStream.kt b/data/src/apkStorePlaystore/java/org/cryptomator/data/cloud/googledrive/TransferredBytesAwareGoogleContentInputStream.kt similarity index 100% rename from data/src/notFoss/java/org/cryptomator/data/cloud/googledrive/TransferredBytesAwareGoogleContentInputStream.kt rename to data/src/apkStorePlaystore/java/org/cryptomator/data/cloud/googledrive/TransferredBytesAwareGoogleContentInputStream.kt diff --git a/data/src/foss/java/org/cryptomator/data/cloud/CloudContentRepositoryFactories.java b/data/src/fdroid/java/org/cryptomator/data/cloud/CloudContentRepositoryFactories.java similarity index 100% rename from data/src/foss/java/org/cryptomator/data/cloud/CloudContentRepositoryFactories.java rename to data/src/fdroid/java/org/cryptomator/data/cloud/CloudContentRepositoryFactories.java diff --git a/data/src/fdroidmain/java/org/cryptomator/data/cloud/CloudContentRepositoryFactories.java b/data/src/fdroidmain/java/org/cryptomator/data/cloud/CloudContentRepositoryFactories.java new file mode 100644 index 00000000..748dea6e --- /dev/null +++ b/data/src/fdroidmain/java/org/cryptomator/data/cloud/CloudContentRepositoryFactories.java @@ -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 { + + private final Iterable factories; + + @Inject + public CloudContentRepositoryFactories( + S3CloudContentRepositoryFactory s3Factory, // + CryptoCloudContentRepositoryFactory cryptoFactory, // + LocalStorageContentRepositoryFactory localStorageFactory, // + WebDavCloudContentRepositoryFactory webDavFactory) { + + factories = asList(s3Factory, // + cryptoFactory, // + localStorageFactory, // + webDavFactory); + } + + @NotNull + @Override + public Iterator iterator() { + return factories.iterator(); + } +} diff --git a/presentation/build.gradle b/presentation/build.gradle index c3bef375..d5289996 100644 --- a/presentation/build.gradle +++ b/presentation/build.gradle @@ -88,19 +88,27 @@ android { fdroid { dimension "version" } + + fdroidmain { + dimension "version" + } } sourceSets { 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 { - 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 { - 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 { @@ -147,9 +155,16 @@ dependencies { implementation dependencies.androidxBiometric // cloud - implementation dependencies.dropbox - implementation dependencies.msgraph - implementation dependencies.msgraphAuth + playstoreImplementation dependencies.dropbox + apkstoreImplementation dependencies.dropbox + fdroidImplementation dependencies.dropbox + + playstoreImplementation dependencies.msgraphAuth + apkstoreImplementation dependencies.msgraphAuth + fdroidImplementation dependencies.msgraphAuth + playstoreImplementation dependencies.msgraph + apkstoreImplementation dependencies.msgraph + fdroidImplementation dependencies.msgraph playstoreImplementation(dependencies.googleApiServicesDrive) { exclude module: 'guava-jdk5' diff --git a/presentation/src/apiKey/java/org/cryptomator/presentation/presenter/DropboxAuthHelper.kt b/presentation/src/apiKey/java/org/cryptomator/presentation/presenter/DropboxAuthHelper.kt new file mode 100644 index 00000000..bb44e3af --- /dev/null +++ b/presentation/src/apiKey/java/org/cryptomator/presentation/presenter/DropboxAuthHelper.kt @@ -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() + } + +} diff --git a/presentation/src/apiKey/java/org/cryptomator/presentation/presenter/OnedriveAuthentication.kt b/presentation/src/apiKey/java/org/cryptomator/presentation/presenter/OnedriveAuthentication.kt new file mode 100644 index 00000000..102af8ec --- /dev/null +++ b/presentation/src/apiKey/java/org/cryptomator/presentation/presenter/OnedriveAuthentication.kt @@ -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) { + 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) { + 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") + } + } + } +} + + diff --git a/presentation/src/apkStorePlaystore/java/org/cryptomator/presentation/presenter/GoogleAuthHelper.kt b/presentation/src/apkStorePlaystore/java/org/cryptomator/presentation/presenter/GoogleAuthHelper.kt new file mode 100644 index 00000000..f85cade2 --- /dev/null +++ b/presentation/src/apkStorePlaystore/java/org/cryptomator/presentation/presenter/GoogleAuthHelper.kt @@ -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() + } +} diff --git a/presentation/src/fdroidAndfdroidmain/java/org/cryptomator/presentation/presenter/GoogleAuthHelper.kt b/presentation/src/fdroidAndfdroidmain/java/org/cryptomator/presentation/presenter/GoogleAuthHelper.kt new file mode 100644 index 00000000..622fa4db --- /dev/null +++ b/presentation/src/fdroidAndfdroidmain/java/org/cryptomator/presentation/presenter/GoogleAuthHelper.kt @@ -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 + } +} diff --git a/presentation/src/fdroidmain/java/org/cryptomator/presentation/presenter/DropboxAuthHelper.kt b/presentation/src/fdroidmain/java/org/cryptomator/presentation/presenter/DropboxAuthHelper.kt new file mode 100644 index 00000000..eb63ac60 --- /dev/null +++ b/presentation/src/fdroidmain/java/org/cryptomator/presentation/presenter/DropboxAuthHelper.kt @@ -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 + } + +} diff --git a/presentation/src/fdroidmain/java/org/cryptomator/presentation/presenter/OnedriveAuthentication.kt b/presentation/src/fdroidmain/java/org/cryptomator/presentation/presenter/OnedriveAuthentication.kt new file mode 100644 index 00000000..56b44f1f --- /dev/null +++ b/presentation/src/fdroidmain/java/org/cryptomator/presentation/presenter/OnedriveAuthentication.kt @@ -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 + } +} + + diff --git a/presentation/src/foss/java/org/cryptomator/presentation/presenter/AuthenticateCloudPresenter.kt b/presentation/src/foss/java/org/cryptomator/presentation/presenter/AuthenticateCloudPresenter.kt deleted file mode 100644 index a7871af4..00000000 --- a/presentation/src/foss/java/org/cryptomator/presentation/presenter/AuthenticateCloudPresenter.kt +++ /dev/null @@ -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(exceptionHandlers) { - - private val strategies = arrayOf( // - DropboxAuthStrategy(), // - OnedriveAuthStrategy(), // - PCloudAuthStrategy(), // - WebDAVAuthStrategy(), // - S3AuthStrategy(), // - LocalStorageAuthStrategy() // - ) - - override fun workflows(): Iterable> { - 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() { - 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() { - 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) { - 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() { - 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>() { - override fun onSuccess(clouds: List) { - 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 { - return arrayOf("User.Read", "Files.ReadWrite") - } - } - - init { - unsubscribeOnDestroy(addOrChangeCloudConnectionUseCase, getCloudsUseCase, getUsernameUseCase) - } -} diff --git a/presentation/src/main/java/org/cryptomator/presentation/CryptomatorApp.kt b/presentation/src/main/java/org/cryptomator/presentation/CryptomatorApp.kt index 3ba4a15e..fc01215c 100644 --- a/presentation/src/main/java/org/cryptomator/presentation/CryptomatorApp.kt +++ b/presentation/src/main/java/org/cryptomator/presentation/CryptomatorApp.kt @@ -51,6 +51,9 @@ class CryptomatorApp : MultiDexApplication(), HasComponent "fdroid" -> { "F-Droid Edition" } + "fdroidmain" -> { + "F-Droid Main Repo Edition" + } else -> "Google Play Edition" } Timber.tag("App").i( diff --git a/presentation/src/notFoss/java/org/cryptomator/presentation/presenter/AuthenticateCloudPresenter.kt b/presentation/src/main/java/org/cryptomator/presentation/presenter/AuthenticateCloudPresenter.kt similarity index 78% rename from presentation/src/notFoss/java/org/cryptomator/presentation/presenter/AuthenticateCloudPresenter.kt rename to presentation/src/main/java/org/cryptomator/presentation/presenter/AuthenticateCloudPresenter.kt index f84f0af1..9b29202a 100644 --- a/presentation/src/notFoss/java/org/cryptomator/presentation/presenter/AuthenticateCloudPresenter.kt +++ b/presentation/src/main/java/org/cryptomator/presentation/presenter/AuthenticateCloudPresenter.kt @@ -6,19 +6,6 @@ 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.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.domain.Cloud 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.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.intent.AuthenticateCloudIntent @@ -175,12 +161,12 @@ class AuthenticateCloudPresenter @Inject constructor( // private fun startAuthentication() { showProgress(ProgressModel(ProgressStateModel.AUTHENTICATION)) authenticationStarted = true - Auth.startOAuth2Authentication(context(), BuildConfig.DROPBOX_API_KEY) + DropboxAuthHelper.startOAuth2Authentication(context()) view?.skipTransition() } private fun handleAuthenticationResult(cloudModel: CloudModel) { - val authToken = Auth.getOAuth2Token() + val authToken = DropboxAuthHelper.getOAuth2Token() if (authToken == null) { failAuthentication(cloudModel.name()) } else { @@ -221,11 +207,10 @@ class AuthenticateCloudPresenter @Inject constructor( // } private fun chooseAccount(cloud: CloudModel) { - val chooseAccountIntent = GoogleAccountCredential.usingOAuth2(context(), setOf(DriveScopes.DRIVE)).newChooseAccountIntent() try { requestActivityResult( // ActivityResultCallbacks.onGoogleDriveAuthenticated(cloud), // - chooseAccountIntent + GoogleAuthHelper.getChooseAccountIntent(context()) ) } catch (e: ActivityNotFoundException) { view?.showMessage(R.string.error_play_services_not_available) @@ -261,6 +246,7 @@ class AuthenticateCloudPresenter @Inject constructor( // private inner class OnedriveAuthStrategy : AuthStrategy { private var authenticationStarted = false + override fun supports(cloud: CloudModel): Boolean { return cloud.cloudType() == CloudTypeModel.ONEDRIVE } @@ -276,107 +262,11 @@ class AuthenticateCloudPresenter @Inject constructor( // 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) { - 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() - ) + OnedriveAuthentication.refreshOrCheckAuth(activity(), cloud.toCloud() as OnedriveCloud, { authenticatedCloud -> + getUsernameAndSuceedAuthentication(authenticatedCloud) + }, { + failAuthentication(cloud.name()) + }) } } diff --git a/presentation/src/main/java/org/cryptomator/presentation/presenter/ChooseCloudServicePresenter.kt b/presentation/src/main/java/org/cryptomator/presentation/presenter/ChooseCloudServicePresenter.kt index 67a49acb..d28d24bc 100644 --- a/presentation/src/main/java/org/cryptomator/presentation/presenter/ChooseCloudServicePresenter.kt +++ b/presentation/src/main/java/org/cryptomator/presentation/presenter/ChooseCloudServicePresenter.kt @@ -37,6 +37,11 @@ class ChooseCloudServicePresenter @Inject constructor( // if (BuildConfig.FLAVOR == "fdroid") { 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) diff --git a/presentation/src/main/java/org/cryptomator/presentation/presenter/CloudConnectionListPresenter.kt b/presentation/src/main/java/org/cryptomator/presentation/presenter/CloudConnectionListPresenter.kt index 935bcff4..4d1a36e1 100644 --- a/presentation/src/main/java/org/cryptomator/presentation/presenter/CloudConnectionListPresenter.kt +++ b/presentation/src/main/java/org/cryptomator/presentation/presenter/CloudConnectionListPresenter.kt @@ -4,13 +4,6 @@ import android.content.ActivityNotFoundException import android.content.Intent import android.net.Uri 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.LocalStorageCloud import org.cryptomator.domain.OnedriveCloud @@ -137,49 +130,11 @@ class CloudConnectionListPresenter @Inject constructor( // } private fun addOnedriveCloud() { - 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) { - 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") - } - } + OnedriveAuthentication.getAuthenticatedOnedriveCloud(activity(), { cloud -> + saveOnedriveCloud(cloud) + }, { e -> + showError(e) + }) } private fun saveOnedriveCloud(onedriveSkeleton: OnedriveCloud) { diff --git a/presentation/src/main/java/org/cryptomator/presentation/presenter/CloudSettingsPresenter.kt b/presentation/src/main/java/org/cryptomator/presentation/presenter/CloudSettingsPresenter.kt index 79ca7a35..c6fa6263 100644 --- a/presentation/src/main/java/org/cryptomator/presentation/presenter/CloudSettingsPresenter.kt +++ b/presentation/src/main/java/org/cryptomator/presentation/presenter/CloudSettingsPresenter.kt @@ -140,9 +140,28 @@ class CloudSettingsPresenter @Inject constructor( // it.add(aS3Cloud()) it.add(aLocalCloud()) } + .filter { cloud -> !(BuildConfig.FLAVOR == "fdroidmain" && excludeApiCloudsInFdroidMain(cloud.cloudType())) } // 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 { return OnedriveCloudModel(OnedriveCloud.aOnedriveCloud().build()) } diff --git a/presentation/src/main/java/org/cryptomator/presentation/presenter/SettingsPresenter.kt b/presentation/src/main/java/org/cryptomator/presentation/presenter/SettingsPresenter.kt index 2a9602b9..6a81f530 100644 --- a/presentation/src/main/java/org/cryptomator/presentation/presenter/SettingsPresenter.kt +++ b/presentation/src/main/java/org/cryptomator/presentation/presenter/SettingsPresenter.kt @@ -83,6 +83,9 @@ class SettingsPresenter @Inject internal constructor( "fdroid" -> { "F-Droid" } + "fdroidmain" -> { + "F-Droid Main Repo Edition" + } else -> "Google Play" } return StringBuilder().append("## ").append(context().getString(R.string.error_report_subject)).append("\n\n") // diff --git a/presentation/src/main/java/org/cryptomator/presentation/presenter/VaultListPresenter.kt b/presentation/src/main/java/org/cryptomator/presentation/presenter/VaultListPresenter.kt index 521083a2..92d50204 100644 --- a/presentation/src/main/java/org/cryptomator/presentation/presenter/VaultListPresenter.kt +++ b/presentation/src/main/java/org/cryptomator/presentation/presenter/VaultListPresenter.kt @@ -123,7 +123,7 @@ class VaultListPresenter @Inject constructor( // } private fun checkLicense() { - if (BuildConfig.FLAVOR == "apkstore" || BuildConfig.FLAVOR == "fdroid") { + if (BuildConfig.FLAVOR == "apkstore" || BuildConfig.FLAVOR == "fdroid" || BuildConfig.FLAVOR == "fdroidmain") { licenseCheckUseCase // .withLicense("") // .run(object : NoOpResultHandler() { diff --git a/presentation/src/main/java/org/cryptomator/presentation/ui/fragment/SettingsFragment.kt b/presentation/src/main/java/org/cryptomator/presentation/ui/fragment/SettingsFragment.kt index d40769ff..6e4733e9 100644 --- a/presentation/src/main/java/org/cryptomator/presentation/ui/fragment/SettingsFragment.kt +++ b/presentation/src/main/java/org/cryptomator/presentation/ui/fragment/SettingsFragment.kt @@ -157,7 +157,7 @@ class SettingsFragment : PreferenceFragmentCompat() { (findPreference(SharedPreferencesHandler.MAIL) as Preference?)?.title = format(getString(R.string.screen_settings_license_mail), sharedPreferencesHandler.mail()) setupUpdateCheck() } - "fdroid" -> { + "fdroid", "fdroidmain" -> { (findPreference(SharedPreferencesHandler.MAIL) as Preference?)?.title = format(getString(R.string.screen_settings_license_mail), sharedPreferencesHandler.mail()) removeUpdateCheck() } From fa8a9cac27026348680c73a712563c5e3efe04f5 Mon Sep 17 00:00:00 2001 From: Julian Raufelder Date: Fri, 20 May 2022 14:36:09 +0200 Subject: [PATCH 02/24] Add Cryptomator variants screen --- presentation/build.gradle | 3 + .../src/fdroidmain/AndroidManifest.xml | 13 + .../src/fdroidmain/res/values/strings.xml | 7 + presentation/src/main/AndroidManifest.xml | 3 + .../di/component/ActivityComponent.java | 4 + .../intent/CryptomatorVariantsIntent.java | 9 + .../presenter/ChooseCloudServicePresenter.kt | 14 + .../presenter/CryptomatorVariantsPresenter.kt | 103 ++++++ .../presentation/ui/activity/BaseActivity.kt | 1 + .../activity/CryptomatorVariantsActivity.kt | 51 +++ .../activity/view/CryptomatorVariantsView.kt | 3 + .../ui/fragment/ChooseCloudServiceFragment.kt | 1 + .../main/res/drawable-night/ic_clear_gray.xml | 9 + .../main/res/drawable-night/ic_done_gray.xml | 9 + .../src/main/res/drawable/ic_clear_gray.xml | 9 + .../src/main/res/drawable/ic_done_gray.xml | 9 + .../layout/activity_cryptomator_variants.xml | 314 ++++++++++++++++++ .../layout/fragment_choose_cloud_service.xml | 3 +- presentation/src/main/res/values/strings.xml | 20 +- 19 files changed, 583 insertions(+), 2 deletions(-) create mode 100644 presentation/src/fdroidmain/AndroidManifest.xml create mode 100644 presentation/src/fdroidmain/res/values/strings.xml create mode 100644 presentation/src/main/java/org/cryptomator/presentation/intent/CryptomatorVariantsIntent.java create mode 100644 presentation/src/main/java/org/cryptomator/presentation/presenter/CryptomatorVariantsPresenter.kt create mode 100644 presentation/src/main/java/org/cryptomator/presentation/ui/activity/CryptomatorVariantsActivity.kt create mode 100644 presentation/src/main/java/org/cryptomator/presentation/ui/activity/view/CryptomatorVariantsView.kt create mode 100644 presentation/src/main/res/drawable-night/ic_clear_gray.xml create mode 100644 presentation/src/main/res/drawable-night/ic_done_gray.xml create mode 100644 presentation/src/main/res/drawable/ic_clear_gray.xml create mode 100644 presentation/src/main/res/drawable/ic_done_gray.xml create mode 100644 presentation/src/main/res/layout/activity_cryptomator_variants.xml diff --git a/presentation/build.gradle b/presentation/build.gradle index d5289996..be4b4997 100644 --- a/presentation/build.gradle +++ b/presentation/build.gradle @@ -91,6 +91,9 @@ android { fdroidmain { dimension "version" + + applicationIdSuffix ".lite" + resValue "string", "app_id", androidApplicationId + applicationIdSuffix } } diff --git a/presentation/src/fdroidmain/AndroidManifest.xml b/presentation/src/fdroidmain/AndroidManifest.xml new file mode 100644 index 00000000..e341d057 --- /dev/null +++ b/presentation/src/fdroidmain/AndroidManifest.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + diff --git a/presentation/src/fdroidmain/res/values/strings.xml b/presentation/src/fdroidmain/res/values/strings.xml new file mode 100644 index 00000000..31a7ed2d --- /dev/null +++ b/presentation/src/fdroidmain/res/values/strings.xml @@ -0,0 +1,7 @@ + + + + + Cryptomator Lite + + diff --git a/presentation/src/main/AndroidManifest.xml b/presentation/src/main/AndroidManifest.xml index 359fb2ec..d485812b 100644 --- a/presentation/src/main/AndroidManifest.xml +++ b/presentation/src/main/AndroidManifest.xml @@ -119,6 +119,9 @@ + diff --git a/presentation/src/main/java/org/cryptomator/presentation/di/component/ActivityComponent.java b/presentation/src/main/java/org/cryptomator/presentation/di/component/ActivityComponent.java index 07aec966..a8018384 100644 --- a/presentation/src/main/java/org/cryptomator/presentation/di/component/ActivityComponent.java +++ b/presentation/src/main/java/org/cryptomator/presentation/di/component/ActivityComponent.java @@ -13,6 +13,7 @@ import org.cryptomator.presentation.ui.activity.ChooseCloudServiceActivity; import org.cryptomator.presentation.ui.activity.CloudConnectionListActivity; import org.cryptomator.presentation.ui.activity.CloudSettingsActivity; import org.cryptomator.presentation.ui.activity.CreateVaultActivity; +import org.cryptomator.presentation.ui.activity.CryptomatorVariantsActivity; import org.cryptomator.presentation.ui.activity.ImagePreviewActivity; import org.cryptomator.presentation.ui.activity.LicenseCheckActivity; import org.cryptomator.presentation.ui.activity.LicensesActivity; @@ -123,4 +124,7 @@ public interface ActivityComponent { void inject(S3AddOrChangeActivity s3AddOrChangeActivity); void inject(S3AddOrChangeFragment s3AddOrChangeFragment); + + void inject(CryptomatorVariantsActivity cryptomatorVariantsActivity); + } diff --git a/presentation/src/main/java/org/cryptomator/presentation/intent/CryptomatorVariantsIntent.java b/presentation/src/main/java/org/cryptomator/presentation/intent/CryptomatorVariantsIntent.java new file mode 100644 index 00000000..87f8c284 --- /dev/null +++ b/presentation/src/main/java/org/cryptomator/presentation/intent/CryptomatorVariantsIntent.java @@ -0,0 +1,9 @@ +package org.cryptomator.presentation.intent; + +import org.cryptomator.generator.Intent; +import org.cryptomator.presentation.ui.activity.CryptomatorVariantsActivity; + +@Intent(CryptomatorVariantsActivity.class) +public interface CryptomatorVariantsIntent { + +} diff --git a/presentation/src/main/java/org/cryptomator/presentation/presenter/ChooseCloudServicePresenter.kt b/presentation/src/main/java/org/cryptomator/presentation/presenter/ChooseCloudServicePresenter.kt index d28d24bc..e1d14874 100644 --- a/presentation/src/main/java/org/cryptomator/presentation/presenter/ChooseCloudServicePresenter.kt +++ b/presentation/src/main/java/org/cryptomator/presentation/presenter/ChooseCloudServicePresenter.kt @@ -1,5 +1,6 @@ package org.cryptomator.presentation.presenter +import android.view.View import org.cryptomator.domain.Cloud import org.cryptomator.domain.di.PerView import org.cryptomator.domain.exception.FatalBackendException @@ -12,6 +13,7 @@ import org.cryptomator.presentation.intent.Intents import org.cryptomator.presentation.model.CloudTypeModel import org.cryptomator.presentation.model.mappers.CloudModelMapper import org.cryptomator.presentation.ui.activity.view.ChooseCloudServiceView +import org.cryptomator.presentation.ui.snackbar.SnackbarAction import org.cryptomator.presentation.workflow.ActivityResult import org.cryptomator.presentation.workflow.AddExistingVaultWorkflow import org.cryptomator.presentation.workflow.CreateNewVaultWorkflow @@ -92,6 +94,18 @@ class ChooseCloudServicePresenter @Inject constructor( // finishWithResult(cloudModelMapper.toModel(cloud)) } + fun showCloudMissingSnackbarHintInFDroidMainVariant() { + if (BuildConfig.FLAVOR == "fdroidmain") { + view?.showSnackbar(R.string.snack_bar_cryptomator_variants_hint, object: SnackbarAction { + override fun onClick(v: View?) { + startIntent(Intents.cryptomatorVariantsIntent()) + } + override val text: Int + get() = R.string.snack_bar_cryptomator_variants_title + }) + } + } + init { unsubscribeOnDestroy(getCloudsUseCase) } diff --git a/presentation/src/main/java/org/cryptomator/presentation/presenter/CryptomatorVariantsPresenter.kt b/presentation/src/main/java/org/cryptomator/presentation/presenter/CryptomatorVariantsPresenter.kt new file mode 100644 index 00000000..1c4b0cf8 --- /dev/null +++ b/presentation/src/main/java/org/cryptomator/presentation/presenter/CryptomatorVariantsPresenter.kt @@ -0,0 +1,103 @@ +package org.cryptomator.presentation.presenter + +import android.content.Intent +import android.net.Uri +import android.widget.Toast +import com.google.common.base.Optional +import org.cryptomator.data.util.NetworkConnectionCheck +import org.cryptomator.domain.di.PerView +import org.cryptomator.domain.usecases.DoUpdateCheckUseCase +import org.cryptomator.domain.usecases.DoUpdateUseCase +import org.cryptomator.domain.usecases.NoOpResultHandler +import org.cryptomator.domain.usecases.UpdateCheck +import org.cryptomator.presentation.R +import org.cryptomator.presentation.exception.ExceptionHandlers +import org.cryptomator.presentation.model.ProgressModel +import org.cryptomator.presentation.ui.activity.view.CryptomatorVariantsView +import org.cryptomator.presentation.util.FileUtil +import javax.inject.Inject + +@PerView +class CryptomatorVariantsPresenter @Inject constructor( + // + exceptionMappings: ExceptionHandlers, // + private val updateCheckUseCase: DoUpdateCheckUseCase, // + private val updateUseCase: DoUpdateUseCase, // + private val networkConnectionCheck: NetworkConnectionCheck, // + private val fileUtil: FileUtil, // +) : Presenter(exceptionMappings) { + + private val fDroidPackageName = "org.fdroid.fdroid" + + fun onInstallMainFDroidVariantClicked() { + context().packageManager.getLaunchIntentForPackage(fDroidPackageName)?.let { + it.data = Uri.parse("https://f-droid.org/packages/org.cryptomator.light") + context().startActivity(it) + } ?: Toast.makeText(context(), R.string.error_interact_with_fdroid_but_fdroid_missing, Toast.LENGTH_SHORT).show() + } + + fun onAddRepoClicked() { + context().packageManager.getLaunchIntentForPackage(fDroidPackageName)?.let { + it.data = Uri.parse("https://static.cryptomator.org/android/fdroid/repo?fingerprint=F7C3EC3B0D588D3CB52983E9EB1A7421C93D4339A286398E71D7B651E8D8ECDD") + context().startActivity(it) + } ?: Toast.makeText(context(), R.string.error_interact_with_fdroid_but_fdroid_missing, Toast.LENGTH_SHORT).show() + } + + fun onInstallFDroidVariantClicked() { + context().packageManager.getLaunchIntentForPackage(fDroidPackageName)?.let { + it.data = Uri.parse("https://f-droid.org/packages/org.cryptomator") + context().startActivity(it) + } ?: Toast.makeText(context(), R.string.error_interact_with_fdroid_but_fdroid_missing, Toast.LENGTH_SHORT).show() + } + + fun onInstallWebsiteVariantClicked() { + if (networkConnectionCheck.isPresent) { + view?.showProgress(ProgressModel.GENERIC) + + updateCheckUseCase // + .withVersion("0.0.0") + .run(object : NoOpResultHandler>() { + override fun onSuccess(result: Optional) { + installUpdate() + } + + override fun onError(e: Throwable) { + view?.showProgress(ProgressModel.COMPLETED) + showError(e) + } + }) + } else { + Toast.makeText(context(), R.string.error_update_no_internet, Toast.LENGTH_SHORT).show() + } + } + + private fun installUpdate() { + val uri = fileUtil.contentUriForNewTempFile("cryptomator.apk") + val file = fileUtil.tempFile("cryptomator.apk") + + updateUseCase // + .withFile(file) // + .run(object : NoOpResultHandler() { + override fun onError(e: Throwable) { + showError(e) + } + + override fun onSuccess(result: Void?) { + super.onSuccess(result) + val intent = Intent(Intent.ACTION_VIEW) + intent.setDataAndType(uri, "application/vnd.android.package-archive") + intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + context().startActivity(intent) + } + + override fun onFinished() { + view?.showProgress(ProgressModel.COMPLETED) + } + }) + } + + init { + unsubscribeOnDestroy(updateCheckUseCase, updateUseCase) + } +} diff --git a/presentation/src/main/java/org/cryptomator/presentation/ui/activity/BaseActivity.kt b/presentation/src/main/java/org/cryptomator/presentation/ui/activity/BaseActivity.kt index a4b73723..25f4c3ba 100644 --- a/presentation/src/main/java/org/cryptomator/presentation/ui/activity/BaseActivity.kt +++ b/presentation/src/main/java/org/cryptomator/presentation/ui/activity/BaseActivity.kt @@ -316,6 +316,7 @@ abstract class BaseActivity : AppCompatActivity(), View, ActivityCompat.OnReques internal open fun snackbarView(): android.view.View { return activity().findViewById(R.id.locationsRecyclerView) as android.view.View? + ?: activity().findViewById(R.id.rlChooseCloudService) as android.view.View? ?: return activity().findViewById(R.id.coordinatorLayout) } diff --git a/presentation/src/main/java/org/cryptomator/presentation/ui/activity/CryptomatorVariantsActivity.kt b/presentation/src/main/java/org/cryptomator/presentation/ui/activity/CryptomatorVariantsActivity.kt new file mode 100644 index 00000000..1a77652a --- /dev/null +++ b/presentation/src/main/java/org/cryptomator/presentation/ui/activity/CryptomatorVariantsActivity.kt @@ -0,0 +1,51 @@ +package org.cryptomator.presentation.ui.activity + +import org.cryptomator.generator.Activity +import org.cryptomator.presentation.R +import org.cryptomator.presentation.presenter.CryptomatorVariantsPresenter +import org.cryptomator.presentation.ui.activity.view.CryptomatorVariantsView +import javax.inject.Inject +import kotlinx.android.synthetic.main.activity_cryptomator_variants.btnAddRepo +import kotlinx.android.synthetic.main.activity_cryptomator_variants.btnInstallFDroidVariant +import kotlinx.android.synthetic.main.activity_cryptomator_variants.btnInstallMainFDroidVariant +import kotlinx.android.synthetic.main.activity_cryptomator_variants.btnInstallWebsiteVariant +import kotlinx.android.synthetic.main.activity_cryptomator_variants.tvFdroidCustomSupported +import kotlinx.android.synthetic.main.activity_cryptomator_variants.tvFdroidCustomUnsupported +import kotlinx.android.synthetic.main.activity_cryptomator_variants.tvFdroidMainSupported +import kotlinx.android.synthetic.main.activity_cryptomator_variants.tvFdroidMainUnsupported +import kotlinx.android.synthetic.main.activity_cryptomator_variants.tvWebsiteAllowed +import kotlinx.android.synthetic.main.toolbar_layout.toolbar + +@Activity(layout = R.layout.activity_cryptomator_variants) +class CryptomatorVariantsActivity : BaseActivity(), CryptomatorVariantsView { + + @Inject + lateinit var presenter: CryptomatorVariantsPresenter + + override fun setupView() { + toolbar.title = getString(R.string.screen_cryptomator_variants_title) + setSupportActionBar(toolbar) + + tvFdroidMainSupported.text = "WebDAV, S3, Local Storage" + tvFdroidMainUnsupported.text = "Dropbox, Google Drive, OneDrive, pCloud" + + tvFdroidCustomSupported.text = "Dropbox, OneDrive, pCloud, WebDAV, S3, Local Storage" + tvFdroidCustomUnsupported.text = "Google Drive" + + tvWebsiteAllowed.text = "Dropbox, Google Drive, OneDrive, pCloud, WebDAV, S3, Local Storage" + + btnInstallMainFDroidVariant.setOnClickListener { + presenter.onInstallMainFDroidVariantClicked() + } + btnAddRepo.setOnClickListener { + presenter.onAddRepoClicked() + } + btnInstallFDroidVariant.setOnClickListener { + presenter.onInstallFDroidVariantClicked() + } + btnInstallWebsiteVariant.setOnClickListener { + presenter.onInstallWebsiteVariantClicked() + } + } + +} diff --git a/presentation/src/main/java/org/cryptomator/presentation/ui/activity/view/CryptomatorVariantsView.kt b/presentation/src/main/java/org/cryptomator/presentation/ui/activity/view/CryptomatorVariantsView.kt new file mode 100644 index 00000000..9b8ee1be --- /dev/null +++ b/presentation/src/main/java/org/cryptomator/presentation/ui/activity/view/CryptomatorVariantsView.kt @@ -0,0 +1,3 @@ +package org.cryptomator.presentation.ui.activity.view + +interface CryptomatorVariantsView : View diff --git a/presentation/src/main/java/org/cryptomator/presentation/ui/fragment/ChooseCloudServiceFragment.kt b/presentation/src/main/java/org/cryptomator/presentation/ui/fragment/ChooseCloudServiceFragment.kt index 4902d353..efe4bb46 100644 --- a/presentation/src/main/java/org/cryptomator/presentation/ui/fragment/ChooseCloudServiceFragment.kt +++ b/presentation/src/main/java/org/cryptomator/presentation/ui/fragment/ChooseCloudServiceFragment.kt @@ -27,6 +27,7 @@ class ChooseCloudServiceFragment : BaseFragment() { override fun setupView() { setupRecyclerView() + chooseCloudServicePresenter.showCloudMissingSnackbarHintInFDroidMainVariant() } fun render(cloudModels: List?) { diff --git a/presentation/src/main/res/drawable-night/ic_clear_gray.xml b/presentation/src/main/res/drawable-night/ic_clear_gray.xml new file mode 100644 index 00000000..70dfd52f --- /dev/null +++ b/presentation/src/main/res/drawable-night/ic_clear_gray.xml @@ -0,0 +1,9 @@ + + + diff --git a/presentation/src/main/res/drawable-night/ic_done_gray.xml b/presentation/src/main/res/drawable-night/ic_done_gray.xml new file mode 100644 index 00000000..5f99d9aa --- /dev/null +++ b/presentation/src/main/res/drawable-night/ic_done_gray.xml @@ -0,0 +1,9 @@ + + + diff --git a/presentation/src/main/res/drawable/ic_clear_gray.xml b/presentation/src/main/res/drawable/ic_clear_gray.xml new file mode 100644 index 00000000..481c0aa6 --- /dev/null +++ b/presentation/src/main/res/drawable/ic_clear_gray.xml @@ -0,0 +1,9 @@ + + + diff --git a/presentation/src/main/res/drawable/ic_done_gray.xml b/presentation/src/main/res/drawable/ic_done_gray.xml new file mode 100644 index 00000000..2603270e --- /dev/null +++ b/presentation/src/main/res/drawable/ic_done_gray.xml @@ -0,0 +1,9 @@ + + + diff --git a/presentation/src/main/res/layout/activity_cryptomator_variants.xml b/presentation/src/main/res/layout/activity_cryptomator_variants.xml new file mode 100644 index 00000000..cc3dc305 --- /dev/null +++ b/presentation/src/main/res/layout/activity_cryptomator_variants.xml @@ -0,0 +1,314 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +