Merge branch 'develop' into feature/150-office-apps-writable
This commit is contained in:
commit
a04b5f9e77
12
.github/stale.yml
vendored
12
.github/stale.yml
vendored
@ -1,10 +1,16 @@
|
|||||||
# Number of days of inactivity before an issue becomes stale
|
# Number of days of inactivity before an issue becomes stale
|
||||||
daysUntilStale: 60
|
daysUntilStale: 365
|
||||||
# Number of days of inactivity before a stale issue is closed
|
# Number of days of inactivity before a stale issue is closed
|
||||||
daysUntilClose: 7
|
daysUntilClose: 90
|
||||||
# Issues with these labels will never be considered stale
|
# Issues with these labels will never be considered stale
|
||||||
exemptLabels:
|
exemptLabels:
|
||||||
- type:security-issue
|
- type:security-issue # never close automatically
|
||||||
|
- type:feature-request # never close automatically
|
||||||
|
- type:enhancement # never close automatically
|
||||||
|
- type:upstream-bug # never close automatically
|
||||||
|
- state:awaiting-response # handled by different bot
|
||||||
|
- state:blocked
|
||||||
|
- state:confirmed
|
||||||
# Set to true to ignore issues in a milestone (defaults to false)
|
# Set to true to ignore issues in a milestone (defaults to false)
|
||||||
exemptMilestones: true
|
exemptMilestones: true
|
||||||
# Label to use when marking an issue as stale
|
# Label to use when marking an issue as stale
|
||||||
|
46
Gemfile.lock
46
Gemfile.lock
@ -10,20 +10,20 @@ GEM
|
|||||||
artifactory (3.0.15)
|
artifactory (3.0.15)
|
||||||
atomos (0.1.3)
|
atomos (0.1.3)
|
||||||
aws-eventstream (1.2.0)
|
aws-eventstream (1.2.0)
|
||||||
aws-partitions (1.554.0)
|
aws-partitions (1.587.0)
|
||||||
aws-sdk-core (3.126.1)
|
aws-sdk-core (3.130.2)
|
||||||
aws-eventstream (~> 1, >= 1.0.2)
|
aws-eventstream (~> 1, >= 1.0.2)
|
||||||
aws-partitions (~> 1, >= 1.525.0)
|
aws-partitions (~> 1, >= 1.525.0)
|
||||||
aws-sigv4 (~> 1.1)
|
aws-sigv4 (~> 1.1)
|
||||||
jmespath (~> 1.0)
|
jmespath (~> 1.0)
|
||||||
aws-sdk-kms (1.54.0)
|
aws-sdk-kms (1.56.0)
|
||||||
aws-sdk-core (~> 3, >= 3.126.0)
|
aws-sdk-core (~> 3, >= 3.127.0)
|
||||||
aws-sigv4 (~> 1.1)
|
aws-sigv4 (~> 1.1)
|
||||||
aws-sdk-s3 (1.112.0)
|
aws-sdk-s3 (1.114.0)
|
||||||
aws-sdk-core (~> 3, >= 3.126.0)
|
aws-sdk-core (~> 3, >= 3.127.0)
|
||||||
aws-sdk-kms (~> 1)
|
aws-sdk-kms (~> 1)
|
||||||
aws-sigv4 (~> 1.4)
|
aws-sigv4 (~> 1.4)
|
||||||
aws-sigv4 (1.4.0)
|
aws-sigv4 (1.5.0)
|
||||||
aws-eventstream (~> 1, >= 1.0.2)
|
aws-eventstream (~> 1, >= 1.0.2)
|
||||||
babosa (1.0.4)
|
babosa (1.0.4)
|
||||||
bcrypt_pbkdf (1.1.0)
|
bcrypt_pbkdf (1.1.0)
|
||||||
@ -40,8 +40,8 @@ GEM
|
|||||||
dotenv (2.7.6)
|
dotenv (2.7.6)
|
||||||
ed25519 (1.3.0)
|
ed25519 (1.3.0)
|
||||||
emoji_regex (3.2.3)
|
emoji_regex (3.2.3)
|
||||||
excon (0.91.0)
|
excon (0.92.3)
|
||||||
faraday (1.9.3)
|
faraday (1.10.0)
|
||||||
faraday-em_http (~> 1.0)
|
faraday-em_http (~> 1.0)
|
||||||
faraday-em_synchrony (~> 1.0)
|
faraday-em_synchrony (~> 1.0)
|
||||||
faraday-excon (~> 1.1)
|
faraday-excon (~> 1.1)
|
||||||
@ -70,7 +70,7 @@ GEM
|
|||||||
faraday_middleware (1.2.0)
|
faraday_middleware (1.2.0)
|
||||||
faraday (~> 1.0)
|
faraday (~> 1.0)
|
||||||
fastimage (2.2.6)
|
fastimage (2.2.6)
|
||||||
fastlane (2.204.3)
|
fastlane (2.205.2)
|
||||||
CFPropertyList (>= 2.3, < 4.0.0)
|
CFPropertyList (>= 2.3, < 4.0.0)
|
||||||
addressable (>= 2.8, < 3.0.0)
|
addressable (>= 2.8, < 3.0.0)
|
||||||
artifactory (~> 3.0)
|
artifactory (~> 3.0)
|
||||||
@ -109,13 +109,13 @@ GEM
|
|||||||
xcodeproj (>= 1.13.0, < 2.0.0)
|
xcodeproj (>= 1.13.0, < 2.0.0)
|
||||||
xcpretty (~> 0.3.0)
|
xcpretty (~> 0.3.0)
|
||||||
xcpretty-travis-formatter (>= 0.0.3)
|
xcpretty-travis-formatter (>= 0.0.3)
|
||||||
fastlane-plugin-aws_s3 (2.0.3)
|
fastlane-plugin-aws_s3 (2.1.0)
|
||||||
apktools (~> 0.7)
|
apktools (~> 0.7)
|
||||||
aws-sdk-s3 (~> 1)
|
aws-sdk-s3 (~> 1)
|
||||||
mime-types (~> 3.3)
|
mime-types (~> 3.3)
|
||||||
fastlane-plugin-get_version_name (0.2.2)
|
fastlane-plugin-get_version_name (0.2.2)
|
||||||
gh_inspector (1.1.3)
|
gh_inspector (1.1.3)
|
||||||
google-apis-androidpublisher_v3 (0.16.0)
|
google-apis-androidpublisher_v3 (0.20.0)
|
||||||
google-apis-core (>= 0.4, < 2.a)
|
google-apis-core (>= 0.4, < 2.a)
|
||||||
google-apis-core (0.4.2)
|
google-apis-core (0.4.2)
|
||||||
addressable (~> 2.5, >= 2.5.1)
|
addressable (~> 2.5, >= 2.5.1)
|
||||||
@ -130,15 +130,15 @@ GEM
|
|||||||
google-apis-core (>= 0.4, < 2.a)
|
google-apis-core (>= 0.4, < 2.a)
|
||||||
google-apis-playcustomapp_v1 (0.7.0)
|
google-apis-playcustomapp_v1 (0.7.0)
|
||||||
google-apis-core (>= 0.4, < 2.a)
|
google-apis-core (>= 0.4, < 2.a)
|
||||||
google-apis-storage_v1 (0.11.0)
|
google-apis-storage_v1 (0.13.0)
|
||||||
google-apis-core (>= 0.4, < 2.a)
|
google-apis-core (>= 0.4, < 2.a)
|
||||||
google-cloud-core (1.6.0)
|
google-cloud-core (1.6.0)
|
||||||
google-cloud-env (~> 1.0)
|
google-cloud-env (~> 1.0)
|
||||||
google-cloud-errors (~> 1.0)
|
google-cloud-errors (~> 1.0)
|
||||||
google-cloud-env (1.5.0)
|
google-cloud-env (1.6.0)
|
||||||
faraday (>= 0.17.3, < 2.0)
|
faraday (>= 0.17.3, < 3.0)
|
||||||
google-cloud-errors (1.2.0)
|
google-cloud-errors (1.2.0)
|
||||||
google-cloud-storage (1.36.1)
|
google-cloud-storage (1.36.2)
|
||||||
addressable (~> 2.8)
|
addressable (~> 2.8)
|
||||||
digest-crc (~> 0.4)
|
digest-crc (~> 0.4)
|
||||||
google-apis-iamcredentials_v1 (~> 0.1)
|
google-apis-iamcredentials_v1 (~> 0.1)
|
||||||
@ -146,8 +146,8 @@ GEM
|
|||||||
google-cloud-core (~> 1.6)
|
google-cloud-core (~> 1.6)
|
||||||
googleauth (>= 0.16.2, < 2.a)
|
googleauth (>= 0.16.2, < 2.a)
|
||||||
mini_mime (~> 1.0)
|
mini_mime (~> 1.0)
|
||||||
googleauth (1.1.1)
|
googleauth (1.1.3)
|
||||||
faraday (>= 0.17.3, < 2.0)
|
faraday (>= 0.17.3, < 3.a)
|
||||||
jwt (>= 1.4, < 3.0)
|
jwt (>= 1.4, < 3.0)
|
||||||
memoist (~> 0.16)
|
memoist (~> 0.16)
|
||||||
multi_json (~> 1.11)
|
multi_json (~> 1.11)
|
||||||
@ -157,7 +157,7 @@ GEM
|
|||||||
http-cookie (1.0.4)
|
http-cookie (1.0.4)
|
||||||
domain_name (~> 0.5)
|
domain_name (~> 0.5)
|
||||||
httpclient (2.8.3)
|
httpclient (2.8.3)
|
||||||
jmespath (1.6.0)
|
jmespath (1.6.1)
|
||||||
json (2.6.1)
|
json (2.6.1)
|
||||||
jwt (2.3.0)
|
jwt (2.3.0)
|
||||||
memoist (0.16.2)
|
memoist (0.16.2)
|
||||||
@ -176,7 +176,7 @@ GEM
|
|||||||
optparse (0.1.1)
|
optparse (0.1.1)
|
||||||
os (1.1.4)
|
os (1.1.4)
|
||||||
plist (3.6.0)
|
plist (3.6.0)
|
||||||
public_suffix (4.0.6)
|
public_suffix (4.0.7)
|
||||||
rake (13.0.6)
|
rake (13.0.6)
|
||||||
representable (3.1.1)
|
representable (3.1.1)
|
||||||
declarative (< 0.1.0)
|
declarative (< 0.1.0)
|
||||||
@ -188,9 +188,9 @@ GEM
|
|||||||
ruby2_keywords (0.0.5)
|
ruby2_keywords (0.0.5)
|
||||||
rubyzip (2.3.2)
|
rubyzip (2.3.2)
|
||||||
security (0.1.3)
|
security (0.1.3)
|
||||||
signet (0.16.0)
|
signet (0.16.1)
|
||||||
addressable (~> 2.8)
|
addressable (~> 2.8)
|
||||||
faraday (>= 0.17.3, < 2.0)
|
faraday (>= 0.17.5, < 3.0)
|
||||||
jwt (>= 1.5, < 3.0)
|
jwt (>= 1.5, < 3.0)
|
||||||
multi_json (~> 1.10)
|
multi_json (~> 1.10)
|
||||||
simctl (1.6.8)
|
simctl (1.6.8)
|
||||||
@ -207,7 +207,7 @@ GEM
|
|||||||
uber (0.1.0)
|
uber (0.1.0)
|
||||||
unf (0.1.4)
|
unf (0.1.4)
|
||||||
unf_ext
|
unf_ext
|
||||||
unf_ext (0.0.8)
|
unf_ext (0.0.8.1)
|
||||||
unicode-display_width (1.8.0)
|
unicode-display_width (1.8.0)
|
||||||
webrick (1.7.0)
|
webrick (1.7.0)
|
||||||
word_wrap (1.0.0)
|
word_wrap (1.0.0)
|
||||||
|
@ -29,11 +29,13 @@ git submodule init && git submodule update // (not necessary if cloned using --r
|
|||||||
./gradlew assembleApkstoreDebug
|
./gradlew assembleApkstoreDebug
|
||||||
```
|
```
|
||||||
|
|
||||||
Before connecting to OneDrive or Dropbox you have to provide valid API keys using environment variables:
|
Before connecting to Dropbox, OneDrive or pCloud you have to provide valid API keys using environment variables:
|
||||||
For build type
|
For build type
|
||||||
|
|
||||||
* **release**: `DROPBOX_API_KEY` or `ONEDRIVE_API_KEY` and `ONEDRIVE_API_REDIRCT_URI`
|
* **release**: `DROPBOX_API_KEY`, `ONEDRIVE_API_KEY` and `ONEDRIVE_API_REDIRCT_URI` or `PCLOUD_CLIENT_ID`
|
||||||
* **debug**: `DROPBOX_API_KEY_DEBUG` or `ONEDRIVE_API_KEY_DEBUG` and `ONEDRIVE_API_REDIRCT_URI_DEBUG`
|
* **debug**: `DROPBOX_API_KEY_DEBUG`, `ONEDRIVE_API_KEY_DEBUG` and `ONEDRIVE_API_REDIRCT_URI_DEBUG` or `PCLOUD_CLIENT_ID_DEBUG`
|
||||||
|
|
||||||
|
Before connecting to Google Drive you have to create a new project in [Google Cloud Platform](https://console.cloud.google.com) with Google Drive API, credentials including Google Drive scopes (read, write, delete,..) and the fingerprint of the key you use to build the app.
|
||||||
|
|
||||||
## Contributing to Cryptomator for Android
|
## Contributing to Cryptomator for Android
|
||||||
|
|
||||||
|
@ -2,13 +2,13 @@ apply from: 'buildsystem/dependencies.gradle'
|
|||||||
apply plugin: "com.vanniktech.android.junit.jacoco"
|
apply plugin: "com.vanniktech.android.junit.jacoco"
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = '1.6.10'
|
ext.kotlin_version = '1.6.21'
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
google()
|
google()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:7.1.1'
|
classpath 'com.android.tools.build:gradle:7.2.1'
|
||||||
classpath 'org.greenrobot:greendao-gradle-plugin:3.3.0'
|
classpath 'org.greenrobot:greendao-gradle-plugin:3.3.0'
|
||||||
classpath 'com.vanniktech:gradle-android-junit-jacoco-plugin:0.16.0'
|
classpath 'com.vanniktech:gradle-android-junit-jacoco-plugin:0.16.0'
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
@ -39,7 +39,7 @@ allprojects {
|
|||||||
ext {
|
ext {
|
||||||
androidApplicationId = 'org.cryptomator'
|
androidApplicationId = 'org.cryptomator'
|
||||||
androidVersionCode = getVersionCode()
|
androidVersionCode = getVersionCode()
|
||||||
androidVersionName = '1.7.0-SNAPSHOT'
|
androidVersionName = '1.8.0-SNAPSHOT'
|
||||||
}
|
}
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
@ -21,8 +21,8 @@ allprojects {
|
|||||||
ext {
|
ext {
|
||||||
androidBuildToolsVersion = "30.0.3"
|
androidBuildToolsVersion = "30.0.3"
|
||||||
androidMinSdkVersion = 26
|
androidMinSdkVersion = 26
|
||||||
androidTargetSdkVersion = 30
|
androidTargetSdkVersion = 31
|
||||||
androidCompileSdkVersion = 30
|
androidCompileSdkVersion = 31
|
||||||
|
|
||||||
// android and java libs
|
// android and java libs
|
||||||
androidVersion = '4.1.1.4'
|
androidVersion = '4.1.1.4'
|
||||||
@ -30,9 +30,9 @@ ext {
|
|||||||
javaxAnnotationVersion = '1.0'
|
javaxAnnotationVersion = '1.0'
|
||||||
|
|
||||||
// support lib
|
// support lib
|
||||||
androidSupportAnnotationsVersion = '1.2.0'
|
androidSupportAnnotationsVersion = '1.3.0'
|
||||||
androidSupportAppcompatVersion = '1.3.1'
|
androidSupportAppcompatVersion = '1.4.1'
|
||||||
androidSupportDesignVersion = '1.4.0'
|
androidMaterialDesignVersion = '1.6.0'
|
||||||
|
|
||||||
coreDesugaringVersion = '1.1.5'
|
coreDesugaringVersion = '1.1.5'
|
||||||
|
|
||||||
@ -42,18 +42,18 @@ ext {
|
|||||||
rxAndroidVersion = '2.1.1'
|
rxAndroidVersion = '2.1.1'
|
||||||
rxBindingVersion = '2.2.0'
|
rxBindingVersion = '2.2.0'
|
||||||
|
|
||||||
daggerVersion = '2.40.5'
|
daggerVersion = '2.42'
|
||||||
|
|
||||||
gsonVersion = '2.9.0'
|
gsonVersion = '2.9.0'
|
||||||
|
|
||||||
okHttpVersion = '4.9.3'
|
okHttpVersion = '4.9.3'
|
||||||
okHttpDigestVersion = '2.6'
|
okHttpDigestVersion = '2.7'
|
||||||
|
|
||||||
velocityVersion = '2.3'
|
velocityVersion = '2.3'
|
||||||
|
|
||||||
timberVersion = '5.0.1'
|
timberVersion = '5.0.1'
|
||||||
|
|
||||||
zxcvbnVersion = '1.5.2'
|
zxcvbnVersion = '1.7.0'
|
||||||
|
|
||||||
scaleImageViewVersion = '3.10.0'
|
scaleImageViewVersion = '3.10.0'
|
||||||
|
|
||||||
@ -65,21 +65,21 @@ ext {
|
|||||||
// cloud provider libs
|
// cloud provider libs
|
||||||
cryptolibVersion = '2.0.2'
|
cryptolibVersion = '2.0.2'
|
||||||
|
|
||||||
dropboxVersion = '5.1.1'
|
dropboxVersion = '5.2.0'
|
||||||
|
|
||||||
googleApiServicesVersion = 'v3-rev20220110-1.32.1'
|
googleApiServicesVersion = 'v3-rev20220508-1.32.1'
|
||||||
googlePlayServicesVersion = '19.2.0'
|
googlePlayServicesVersion = '19.2.0'
|
||||||
googleClientVersion = '1.33.2' // keep in sync with https://github.com/SailReal/google-http-java-client
|
googleClientVersion = '1.35.0' // keep in sync with https://github.com/SailReal/google-http-java-client
|
||||||
/*
|
/*
|
||||||
update using https://github.com/SailReal/google-http-java-client with `mvn clean install`,
|
update using https://github.com/SailReal/google-http-java-client with `mvn clean install`,
|
||||||
copying `google-http-client-*.jar` and `google-http-client-android-*.jar` into the lib folder of this project
|
copying `google-http-client-*.jar` and `google-http-client-android-*.jar` into the lib folder of this project
|
||||||
*/
|
*/
|
||||||
trackingFreeGoogleCLientVersion = '1.41.4'
|
trackingFreeGoogleCLientVersion = '1.41.8'
|
||||||
|
|
||||||
msgraphVersion = '5.14.0'
|
msgraphVersion = '5.25.0'
|
||||||
msgraphAuthVersion = '2.2.3'
|
msgraphAuthVersion = '3.0.2'
|
||||||
|
|
||||||
minIoVersion = '8.3.6'
|
minIoVersion = '8.4.1'
|
||||||
staxVersion = '1.2.0' // needed for minIO
|
staxVersion = '1.2.0' // needed for minIO
|
||||||
|
|
||||||
commonsCodecVersion = '1.15'
|
commonsCodecVersion = '1.15'
|
||||||
@ -90,7 +90,7 @@ ext {
|
|||||||
|
|
||||||
jUnitVersion = '5.8.2'
|
jUnitVersion = '5.8.2'
|
||||||
assertJVersion = '1.7.1'
|
assertJVersion = '1.7.1'
|
||||||
mockitoVersion = '4.3.1'
|
mockitoVersion = '4.6.1'
|
||||||
mockitoKotlinVersion = '4.0.0'
|
mockitoKotlinVersion = '4.0.0'
|
||||||
hamcrestVersion = '1.3'
|
hamcrestVersion = '1.3'
|
||||||
dexmakerVersion = '1.0'
|
dexmakerVersion = '1.0'
|
||||||
@ -100,18 +100,20 @@ ext {
|
|||||||
rulesVersion = '1.4.0'
|
rulesVersion = '1.4.0'
|
||||||
contributionVersion = '3.4.0'
|
contributionVersion = '3.4.0'
|
||||||
uiautomatorVersion = '2.2.0'
|
uiautomatorVersion = '2.2.0'
|
||||||
|
androidxTestJunitKtlnVersion = '1.1.3'
|
||||||
|
|
||||||
androidxCoreVersion = '1.6.0'
|
androidxCoreVersion = '1.7.0'
|
||||||
androidxFragmentVersion = '1.3.6'
|
androidxFragmentVersion = '1.4.1'
|
||||||
androidxViewpagerVersion = '1.0.0'
|
androidxViewpagerVersion = '1.0.0'
|
||||||
androidxSwiperefreshVersion = '1.1.0'
|
androidxSwiperefreshVersion = '1.1.0'
|
||||||
androidxPreferenceVersion = '1.1.1'
|
androidxPreferenceVersion = '1.2.0'
|
||||||
androidxRecyclerViewVersion = '1.2.1'
|
androidxRecyclerViewVersion = '1.2.1'
|
||||||
androidxDocumentfileVersion = '1.0.1'
|
androidxDocumentfileVersion = '1.0.1'
|
||||||
androidxBiometricVersion = '1.1.0'
|
androidxBiometricVersion = '1.1.0'
|
||||||
androidxTestCoreVersion = '1.4.0'
|
androidxTestCoreVersion = '1.4.0'
|
||||||
|
androidxSplashscreenVersion = '1.0.0-rc01'
|
||||||
|
|
||||||
jsonWebTokenApiVersion = '0.11.2'
|
jsonWebTokenApiVersion = '0.11.5'
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
android : "com.google.android:android:${androidVersion}",
|
android : "com.google.android:android:${androidVersion}",
|
||||||
@ -125,12 +127,14 @@ ext {
|
|||||||
androidxPreference : "androidx.preference:preference:${androidxPreferenceVersion}",
|
androidxPreference : "androidx.preference:preference:${androidxPreferenceVersion}",
|
||||||
documentFile : "androidx.documentfile:documentfile:${androidxDocumentfileVersion}",
|
documentFile : "androidx.documentfile:documentfile:${androidxDocumentfileVersion}",
|
||||||
recyclerView : "androidx.recyclerview:recyclerview:${androidxRecyclerViewVersion}",
|
recyclerView : "androidx.recyclerview:recyclerview:${androidxRecyclerViewVersion}",
|
||||||
|
androidxSplashscreen : "androidx.core:core-splashscreen:${androidxSplashscreenVersion}",
|
||||||
androidxTestCore : "androidx.test:core:${androidxTestCoreVersion}",
|
androidxTestCore : "androidx.test:core:${androidxTestCoreVersion}",
|
||||||
|
androidxTestJunitKtln : "androidx.test.ext:junit-ktx:${androidxTestJunitKtlnVersion}",
|
||||||
commonsCodec : "commons-codec:commons-codec:${commonsCodecVersion}",
|
commonsCodec : "commons-codec:commons-codec:${commonsCodecVersion}",
|
||||||
cryptolib : "org.cryptomator:cryptolib:${cryptolibVersion}",
|
cryptolib : "org.cryptomator:cryptolib:${cryptolibVersion}",
|
||||||
dagger : "com.google.dagger:dagger:${daggerVersion}",
|
dagger : "com.google.dagger:dagger:${daggerVersion}",
|
||||||
daggerCompiler : "com.google.dagger:dagger-compiler:${daggerVersion}",
|
daggerCompiler : "com.google.dagger:dagger-compiler:${daggerVersion}",
|
||||||
design : "com.google.android.material:material:${androidSupportDesignVersion}",
|
design : "com.google.android.material:material:${androidMaterialDesignVersion}",
|
||||||
coreDesugaring : "com.android.tools:desugar_jdk_libs:${coreDesugaringVersion}",
|
coreDesugaring : "com.android.tools:desugar_jdk_libs:${coreDesugaringVersion}",
|
||||||
dropbox : "com.dropbox.core:dropbox-core-sdk:${dropboxVersion}",
|
dropbox : "com.dropbox.core:dropbox-core-sdk:${dropboxVersion}",
|
||||||
espresso : "androidx.test.espresso:espresso-core:${espressoVersion}",
|
espresso : "androidx.test.espresso:espresso-core:${espressoVersion}",
|
||||||
|
@ -53,19 +53,27 @@ android {
|
|||||||
fdroid {
|
fdroid {
|
||||||
dimension "version"
|
dimension "version"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lite {
|
||||||
|
dimension "version"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
playstore {
|
playstore {
|
||||||
java.srcDirs = ['src/main/java', 'src/main/java/', 'src/notFoss/java', 'src/notFoss/java/']
|
java.srcDirs = ['src/main/java/', 'src/apiKey/java/', 'src/apkStorePlaystore/java/']
|
||||||
}
|
}
|
||||||
|
|
||||||
apkstore {
|
apkstore {
|
||||||
java.srcDirs = ['src/main/java', 'src/main/java/', 'src/notFoss/java', 'src/notFoss/java/']
|
java.srcDirs = ['src/main/java/', 'src/apiKey/java/', 'src/apkStorePlaystore/java/']
|
||||||
}
|
}
|
||||||
|
|
||||||
fdroid {
|
fdroid {
|
||||||
java.srcDirs = ['src/main/java', 'src/main/java/', 'src/foss/java', 'src/foss/java/']
|
java.srcDirs = ['src/main/java/', 'src/apiKey/java/', 'src/fdroid/java/']
|
||||||
|
}
|
||||||
|
|
||||||
|
lite {
|
||||||
|
java.srcDirs = ['src/main/java/', 'src/lite/java/']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
packagingOptions {
|
packagingOptions {
|
||||||
@ -82,7 +90,7 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
greendao {
|
greendao {
|
||||||
schemaVersion 11
|
schemaVersion 12
|
||||||
}
|
}
|
||||||
|
|
||||||
configurations.all {
|
configurations.all {
|
||||||
@ -95,7 +103,9 @@ dependencies {
|
|||||||
|
|
||||||
implementation project(':domain')
|
implementation project(':domain')
|
||||||
implementation project(':util')
|
implementation project(':util')
|
||||||
implementation project(':pcloud-sdk-java')
|
playstoreImplementation project(':pcloud-sdk-java')
|
||||||
|
apkstoreImplementation project(':pcloud-sdk-java')
|
||||||
|
fdroidImplementation project(':pcloud-sdk-java')
|
||||||
|
|
||||||
coreLibraryDesugaring dependencies.coreDesugaring
|
coreLibraryDesugaring dependencies.coreDesugaring
|
||||||
|
|
||||||
@ -113,9 +123,16 @@ dependencies {
|
|||||||
implementation dependencies.jsonWebTokenJson
|
implementation dependencies.jsonWebTokenJson
|
||||||
|
|
||||||
// cloud
|
// cloud
|
||||||
implementation dependencies.dropbox
|
playstoreImplementation dependencies.dropbox
|
||||||
implementation dependencies.msgraphAuth
|
apkstoreImplementation dependencies.dropbox
|
||||||
implementation dependencies.msgraph
|
fdroidImplementation dependencies.dropbox
|
||||||
|
|
||||||
|
playstoreImplementation dependencies.msgraphAuth
|
||||||
|
apkstoreImplementation dependencies.msgraphAuth
|
||||||
|
fdroidImplementation dependencies.msgraphAuth
|
||||||
|
playstoreImplementation dependencies.msgraph
|
||||||
|
apkstoreImplementation dependencies.msgraph
|
||||||
|
fdroidImplementation dependencies.msgraph
|
||||||
|
|
||||||
implementation dependencies.stax
|
implementation dependencies.stax
|
||||||
api dependencies.minIo
|
api dependencies.minIo
|
||||||
@ -191,6 +208,7 @@ dependencies {
|
|||||||
testRuntimeOnly dependencies.junitEngine
|
testRuntimeOnly dependencies.junitEngine
|
||||||
testImplementation dependencies.junitParams
|
testImplementation dependencies.junitParams
|
||||||
testRuntimeOnly dependencies.junit4Engine
|
testRuntimeOnly dependencies.junit4Engine
|
||||||
|
implementation dependencies.androidxTestJunitKtln
|
||||||
|
|
||||||
testImplementation dependencies.mockito
|
testImplementation dependencies.mockito
|
||||||
testImplementation dependencies.mockitoKotlin
|
testImplementation dependencies.mockitoKotlin
|
||||||
|
@ -2,9 +2,10 @@ package org.cryptomator.data.db
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.database.sqlite.SQLiteDatabase
|
import android.database.sqlite.SQLiteDatabase
|
||||||
import androidx.test.InstrumentationRegistry
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import androidx.test.filters.SmallTest
|
import androidx.test.filters.SmallTest
|
||||||
import androidx.test.runner.AndroidJUnit4
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
|
import com.google.common.base.Optional
|
||||||
import org.cryptomator.data.db.entities.CloudEntityDao
|
import org.cryptomator.data.db.entities.CloudEntityDao
|
||||||
import org.cryptomator.data.db.entities.UpdateCheckEntityDao
|
import org.cryptomator.data.db.entities.UpdateCheckEntityDao
|
||||||
import org.cryptomator.data.db.entities.VaultEntityDao
|
import org.cryptomator.data.db.entities.VaultEntityDao
|
||||||
@ -25,7 +26,7 @@ import org.junit.runner.RunWith
|
|||||||
@SmallTest
|
@SmallTest
|
||||||
class UpgradeDatabaseTest {
|
class UpgradeDatabaseTest {
|
||||||
|
|
||||||
private val context = InstrumentationRegistry.getTargetContext()
|
private val context = InstrumentationRegistry.getInstrumentation().context
|
||||||
private val sharedPreferencesHandler = SharedPreferencesHandler(context)
|
private val sharedPreferencesHandler = SharedPreferencesHandler(context)
|
||||||
private lateinit var db: Database
|
private lateinit var db: Database
|
||||||
|
|
||||||
@ -580,4 +581,66 @@ class UpgradeDatabaseTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun upgrade11To12IfOldDefaultSet() {
|
||||||
|
Upgrade0To1().applyTo(db, 0)
|
||||||
|
Upgrade1To2().applyTo(db, 1)
|
||||||
|
Upgrade2To3(context).applyTo(db, 2)
|
||||||
|
Upgrade3To4().applyTo(db, 3)
|
||||||
|
Upgrade4To5().applyTo(db, 4)
|
||||||
|
Upgrade5To6().applyTo(db, 5)
|
||||||
|
Upgrade6To7().applyTo(db, 6)
|
||||||
|
Upgrade7To8().applyTo(db, 7)
|
||||||
|
Upgrade8To9(sharedPreferencesHandler).applyTo(db, 8)
|
||||||
|
Upgrade9To10(sharedPreferencesHandler).applyTo(db, 9)
|
||||||
|
Upgrade10To11().applyTo(db, 10)
|
||||||
|
|
||||||
|
sharedPreferencesHandler.setUpdateIntervalInDays(Optional.of(7))
|
||||||
|
|
||||||
|
Upgrade11To12(sharedPreferencesHandler).applyTo(db, 11)
|
||||||
|
|
||||||
|
Assert.assertThat(sharedPreferencesHandler.updateIntervalInDays(), CoreMatchers.`is`(Optional.of(1)))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun upgrade11To12MonthlySet() {
|
||||||
|
Upgrade0To1().applyTo(db, 0)
|
||||||
|
Upgrade1To2().applyTo(db, 1)
|
||||||
|
Upgrade2To3(context).applyTo(db, 2)
|
||||||
|
Upgrade3To4().applyTo(db, 3)
|
||||||
|
Upgrade4To5().applyTo(db, 4)
|
||||||
|
Upgrade5To6().applyTo(db, 5)
|
||||||
|
Upgrade6To7().applyTo(db, 6)
|
||||||
|
Upgrade7To8().applyTo(db, 7)
|
||||||
|
Upgrade8To9(sharedPreferencesHandler).applyTo(db, 8)
|
||||||
|
Upgrade9To10(sharedPreferencesHandler).applyTo(db, 9)
|
||||||
|
Upgrade10To11().applyTo(db, 10)
|
||||||
|
|
||||||
|
sharedPreferencesHandler.setUpdateIntervalInDays(Optional.of(30))
|
||||||
|
|
||||||
|
Upgrade11To12(sharedPreferencesHandler).applyTo(db, 11)
|
||||||
|
|
||||||
|
Assert.assertThat(sharedPreferencesHandler.updateIntervalInDays(), CoreMatchers.`is`(Optional.of(1)))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun upgrade11To12MonthlyNever() {
|
||||||
|
Upgrade0To1().applyTo(db, 0)
|
||||||
|
Upgrade1To2().applyTo(db, 1)
|
||||||
|
Upgrade2To3(context).applyTo(db, 2)
|
||||||
|
Upgrade3To4().applyTo(db, 3)
|
||||||
|
Upgrade4To5().applyTo(db, 4)
|
||||||
|
Upgrade5To6().applyTo(db, 5)
|
||||||
|
Upgrade6To7().applyTo(db, 6)
|
||||||
|
Upgrade7To8().applyTo(db, 7)
|
||||||
|
Upgrade8To9(sharedPreferencesHandler).applyTo(db, 8)
|
||||||
|
Upgrade9To10(sharedPreferencesHandler).applyTo(db, 9)
|
||||||
|
Upgrade10To11().applyTo(db, 10)
|
||||||
|
|
||||||
|
sharedPreferencesHandler.setUpdateIntervalInDays(Optional.absent())
|
||||||
|
|
||||||
|
Upgrade11To12(sharedPreferencesHandler).applyTo(db, 11)
|
||||||
|
|
||||||
|
Assert.assertThat(sharedPreferencesHandler.updateIntervalInDays(), CoreMatchers.`is`(Optional.absent()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ import com.microsoft.graph.httpcore.HttpClients
|
|||||||
import com.microsoft.graph.requests.GraphServiceClient
|
import com.microsoft.graph.requests.GraphServiceClient
|
||||||
import org.cryptomator.data.cloud.okhttplogging.HttpLoggingInterceptor
|
import org.cryptomator.data.cloud.okhttplogging.HttpLoggingInterceptor
|
||||||
import org.cryptomator.data.util.NetworkTimeout
|
import org.cryptomator.data.util.NetworkTimeout
|
||||||
import org.cryptomator.util.SharedPreferencesHandler
|
|
||||||
import org.cryptomator.util.crypto.CredentialCryptor
|
import org.cryptomator.util.crypto.CredentialCryptor
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.util.concurrent.CompletableFuture
|
import java.util.concurrent.CompletableFuture
|
||||||
@ -19,7 +18,7 @@ class OnedriveClientFactory private constructor() {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
fun createInstance(context: Context, encryptedToken: String, sharedPreferencesHandler: SharedPreferencesHandler): GraphServiceClient<Request> {
|
fun createInstance(context: Context, encryptedToken: String): GraphServiceClient<Request> {
|
||||||
val tokenAuthenticationProvider = object : BaseAuthenticationProvider() {
|
val tokenAuthenticationProvider = object : BaseAuthenticationProvider() {
|
||||||
val token = CompletableFuture.completedFuture(CredentialCryptor.getInstance(context).decrypt(encryptedToken))
|
val token = CompletableFuture.completedFuture(CredentialCryptor.getInstance(context).decrypt(encryptedToken))
|
||||||
override fun getAuthorizationTokenAsync(requestUrl: URL): CompletableFuture<String> {
|
override fun getAuthorizationTokenAsync(requestUrl: URL): CompletableFuture<String> {
|
@ -4,7 +4,7 @@ import android.content.Context
|
|||||||
import com.microsoft.graph.core.GraphErrorCodes
|
import com.microsoft.graph.core.GraphErrorCodes
|
||||||
import com.microsoft.graph.http.GraphServiceException
|
import com.microsoft.graph.http.GraphServiceException
|
||||||
import com.microsoft.graph.requests.GraphServiceClient
|
import com.microsoft.graph.requests.GraphServiceClient
|
||||||
import com.microsoft.identity.common.exception.ClientException
|
import com.microsoft.identity.common.java.exception.ClientException
|
||||||
import org.cryptomator.data.cloud.InterceptingCloudContentRepository
|
import org.cryptomator.data.cloud.InterceptingCloudContentRepository
|
||||||
import org.cryptomator.domain.OnedriveCloud
|
import org.cryptomator.domain.OnedriveCloud
|
||||||
import org.cryptomator.domain.exception.BackendException
|
import org.cryptomator.domain.exception.BackendException
|
@ -33,6 +33,6 @@ public class OnedriveCloudContentRepositoryFactory implements CloudContentReposi
|
|||||||
@Override
|
@Override
|
||||||
public CloudContentRepository<OnedriveCloud, OnedriveNode, OnedriveFolder, OnedriveFile> cloudContentRepositoryFor(Cloud cloud) {
|
public CloudContentRepository<OnedriveCloud, OnedriveNode, OnedriveFolder, OnedriveFile> cloudContentRepositoryFor(Cloud cloud) {
|
||||||
OnedriveCloud onedriveCloud = (OnedriveCloud) cloud;
|
OnedriveCloud onedriveCloud = (OnedriveCloud) cloud;
|
||||||
return new OnedriveCloudContentRepository(onedriveCloud, context, OnedriveClientFactory.Companion.createInstance(context, onedriveCloud.accessToken(), sharedPreferencesHandler));
|
return new OnedriveCloudContentRepository(onedriveCloud, context, OnedriveClientFactory.Companion.createInstance(context, onedriveCloud.accessToken()));
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,7 +1,6 @@
|
|||||||
package org.cryptomator.data.cloud.onedrive
|
package org.cryptomator.data.cloud.onedrive
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
|
||||||
import com.microsoft.graph.http.GraphServiceException
|
import com.microsoft.graph.http.GraphServiceException
|
||||||
import com.microsoft.graph.models.DriveItem
|
import com.microsoft.graph.models.DriveItem
|
||||||
import com.microsoft.graph.models.DriveItemCreateUploadSessionParameterSet
|
import com.microsoft.graph.models.DriveItemCreateUploadSessionParameterSet
|
||||||
@ -86,7 +85,7 @@ internal class OnedriveImpl(cloud: OnedriveCloud, context: Context, graphService
|
|||||||
|
|
||||||
private fun childByName(parentId: String, parentDriveId: String, name: String): DriveItem? {
|
private fun childByName(parentId: String, parentDriveId: String, name: String): DriveItem? {
|
||||||
return try {
|
return try {
|
||||||
drive(parentDriveId).items(parentId).itemWithPath(Uri.encode(name)).buildRequest().get()
|
drive(parentDriveId).items(parentId).itemWithPath(name).buildRequest().get()
|
||||||
} catch (e: GraphServiceException) {
|
} catch (e: GraphServiceException) {
|
||||||
if (isNotFoundError(e)) {
|
if (isNotFoundError(e)) {
|
||||||
null
|
null
|
@ -1,5 +1,7 @@
|
|||||||
package org.cryptomator.data.cloud;
|
package org.cryptomator.data.cloud;
|
||||||
|
|
||||||
|
import static java.util.Arrays.asList;
|
||||||
|
|
||||||
import org.cryptomator.data.cloud.crypto.CryptoCloudContentRepositoryFactory;
|
import org.cryptomator.data.cloud.crypto.CryptoCloudContentRepositoryFactory;
|
||||||
import org.cryptomator.data.cloud.dropbox.DropboxCloudContentRepositoryFactory;
|
import org.cryptomator.data.cloud.dropbox.DropboxCloudContentRepositoryFactory;
|
||||||
import org.cryptomator.data.cloud.googledrive.GoogleDriveCloudContentRepositoryFactory;
|
import org.cryptomator.data.cloud.googledrive.GoogleDriveCloudContentRepositoryFactory;
|
||||||
@ -16,8 +18,6 @@ import java.util.Iterator;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import static java.util.Arrays.asList;
|
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class CloudContentRepositoryFactories implements Iterable<CloudContentRepositoryFactory> {
|
public class CloudContentRepositoryFactories implements Iterable<CloudContentRepositoryFactory> {
|
||||||
|
|
@ -56,7 +56,7 @@ internal class GoogleDriveImpl(context: Context, googleDriveCloud: GoogleDriveCl
|
|||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
private fun findFile(parentDriveId: String?, name: String): File? {
|
private fun findFile(parentDriveId: String?, name: String): File? {
|
||||||
val fileListQuery = client().files().list().setFields("files(id,mimeType,name,size,shortcutDetails)")
|
val fileListQuery = client().files().list().setFields("files(id,mimeType,name,size,shortcutDetails)").setSupportsAllDrives(true).setIncludeItemsFromAllDrives(true)
|
||||||
fileListQuery.q = "name contains '$name' and '$parentDriveId' in parents and trashed = false"
|
fileListQuery.q = "name contains '$name' and '$parentDriveId' in parents and trashed = false"
|
||||||
return fileListQuery.execute().files.firstOrNull { it.name == name }
|
return fileListQuery.execute().files.firstOrNull { it.name == name }
|
||||||
}
|
}
|
||||||
@ -131,6 +131,8 @@ internal class GoogleDriveImpl(context: Context, googleDriveCloud: GoogleDriveCl
|
|||||||
.setFields("nextPageToken,files(id,mimeType,modifiedTime,name,size,shortcutDetails)") //
|
.setFields("nextPageToken,files(id,mimeType,modifiedTime,name,size,shortcutDetails)") //
|
||||||
.setPageSize(1000) //
|
.setPageSize(1000) //
|
||||||
.setPageToken(pageToken)
|
.setPageToken(pageToken)
|
||||||
|
.setSupportsAllDrives(true)
|
||||||
|
.setIncludeItemsFromAllDrives(true)
|
||||||
fileListQuery.q = "'" + folder.driveId + "' in parents and trashed = false"
|
fileListQuery.q = "'" + folder.driveId + "' in parents and trashed = false"
|
||||||
val fileList = fileListQuery.execute()
|
val fileList = fileListQuery.execute()
|
||||||
for (file in fileList.files) {
|
for (file in fileList.files) {
|
||||||
@ -160,6 +162,7 @@ internal class GoogleDriveImpl(context: Context, googleDriveCloud: GoogleDriveCl
|
|||||||
.files() //
|
.files() //
|
||||||
.create(metadata) //
|
.create(metadata) //
|
||||||
.setFields("id,name") //
|
.setFields("id,name") //
|
||||||
|
.setSupportsAllDrives(true) //
|
||||||
.execute()
|
.execute()
|
||||||
return idCache.cache(GoogleDriveCloudNodeFactory.folder(parentFolder, createdFolder))
|
return idCache.cache(GoogleDriveCloudNodeFactory.folder(parentFolder, createdFolder))
|
||||||
} ?: throw ParentFolderIsNullException(folder.name)
|
} ?: throw ParentFolderIsNullException(folder.name)
|
||||||
@ -181,6 +184,7 @@ internal class GoogleDriveImpl(context: Context, googleDriveCloud: GoogleDriveCl
|
|||||||
.setFields("id,mimeType,modifiedTime,name,size") //
|
.setFields("id,mimeType,modifiedTime,name,size") //
|
||||||
.setAddParents(targetsParent.driveId) //
|
.setAddParents(targetsParent.driveId) //
|
||||||
.setRemoveParents(sourcesParent.driveId) //
|
.setRemoveParents(sourcesParent.driveId) //
|
||||||
|
.setSupportsAllDrives(true) //
|
||||||
.execute()
|
.execute()
|
||||||
idCache.remove(source)
|
idCache.remove(source)
|
||||||
return idCache.cache(GoogleDriveCloudNodeFactory.from(targetsParent, movedFile))
|
return idCache.cache(GoogleDriveCloudNodeFactory.from(targetsParent, movedFile))
|
||||||
@ -224,6 +228,7 @@ internal class GoogleDriveImpl(context: Context, googleDriveCloud: GoogleDriveCl
|
|||||||
.files() //
|
.files() //
|
||||||
.update(file.driveId, metadata, it) //
|
.update(file.driveId, metadata, it) //
|
||||||
.setFields("id,modifiedTime,name,size") //
|
.setFields("id,modifiedTime,name,size") //
|
||||||
|
.setSupportsAllDrives(true) //
|
||||||
.execute()
|
.execute()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -246,6 +251,7 @@ internal class GoogleDriveImpl(context: Context, googleDriveCloud: GoogleDriveCl
|
|||||||
.files() //
|
.files() //
|
||||||
.create(metadata, it) //
|
.create(metadata, it) //
|
||||||
.setFields("id,modifiedTime,name,size") //
|
.setFields("id,modifiedTime,name,size") //
|
||||||
|
.setSupportsAllDrives(true) //
|
||||||
.execute()
|
.execute()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -316,6 +322,7 @@ internal class GoogleDriveImpl(context: Context, googleDriveCloud: GoogleDriveCl
|
|||||||
client() //
|
client() //
|
||||||
.files()[file.driveId] //
|
.files()[file.driveId] //
|
||||||
.setAlt("media") //
|
.setAlt("media") //
|
||||||
|
.setSupportsAllDrives(true) //
|
||||||
.executeMediaAndDownloadTo(it)
|
.executeMediaAndDownloadTo(it)
|
||||||
}
|
}
|
||||||
} catch (e: HttpResponseException) {
|
} catch (e: HttpResponseException) {
|
||||||
@ -373,7 +380,7 @@ internal class GoogleDriveImpl(context: Context, googleDriveCloud: GoogleDriveCl
|
|||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun delete(node: GoogleDriveNode) {
|
fun delete(node: GoogleDriveNode) {
|
||||||
client().files().delete(node.driveId).execute()
|
client().files().delete(node.driveId).setSupportsAllDrives(true).execute()
|
||||||
idCache.remove(node)
|
idCache.remove(node)
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
|||||||
|
package org.cryptomator.data.cloud;
|
||||||
|
|
||||||
|
import static java.util.Arrays.asList;
|
||||||
|
|
||||||
|
import org.cryptomator.data.cloud.crypto.CryptoCloudContentRepositoryFactory;
|
||||||
|
import org.cryptomator.data.cloud.local.LocalStorageContentRepositoryFactory;
|
||||||
|
import org.cryptomator.data.cloud.s3.S3CloudContentRepositoryFactory;
|
||||||
|
import org.cryptomator.data.cloud.webdav.WebDavCloudContentRepositoryFactory;
|
||||||
|
import org.cryptomator.data.repository.CloudContentRepositoryFactory;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
public class CloudContentRepositoryFactories implements Iterable<CloudContentRepositoryFactory> {
|
||||||
|
|
||||||
|
private final Iterable<CloudContentRepositoryFactory> factories;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public CloudContentRepositoryFactories(
|
||||||
|
S3CloudContentRepositoryFactory s3Factory, //
|
||||||
|
CryptoCloudContentRepositoryFactory cryptoFactory, //
|
||||||
|
LocalStorageContentRepositoryFactory localStorageFactory, //
|
||||||
|
WebDavCloudContentRepositoryFactory webDavFactory) {
|
||||||
|
|
||||||
|
factories = asList(s3Factory, //
|
||||||
|
cryptoFactory, //
|
||||||
|
localStorageFactory, //
|
||||||
|
webDavFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Iterator<CloudContentRepositoryFactory> iterator() {
|
||||||
|
return factories.iterator();
|
||||||
|
}
|
||||||
|
}
|
@ -85,10 +85,13 @@ abstract class CryptoImplDecorator(
|
|||||||
abstract fun write(cryptoFile: CryptoFile, data: DataSource, progressAware: ProgressAware<UploadState>, replace: Boolean, length: Long): CryptoFile
|
abstract fun write(cryptoFile: CryptoFile, data: DataSource, progressAware: ProgressAware<UploadState>, replace: Boolean, length: Long): CryptoFile
|
||||||
|
|
||||||
@Throws(BackendException::class, EmptyDirFileException::class)
|
@Throws(BackendException::class, EmptyDirFileException::class)
|
||||||
abstract fun loadDirId(folder: CryptoFolder): String
|
abstract fun loadDirId(folder: CryptoFolder): String?
|
||||||
|
|
||||||
@Throws(BackendException::class)
|
@Throws(BackendException::class)
|
||||||
abstract fun createDirIdInfo(folder: CryptoFolder): DirIdInfo
|
abstract fun getOrCreateDirIdInfo(folder: CryptoFolder): DirIdInfo
|
||||||
|
|
||||||
|
@Throws(BackendException::class)
|
||||||
|
abstract fun getDirIdInfo(folder: CryptoFolder): DirIdInfo?
|
||||||
|
|
||||||
private fun dirHash(directoryId: String): String {
|
private fun dirHash(directoryId: String): String {
|
||||||
return cryptor().fileNameCryptor().hashDirectoryId(directoryId)
|
return cryptor().fileNameCryptor().hashDirectoryId(directoryId)
|
||||||
@ -162,7 +165,7 @@ abstract class CryptoImplDecorator(
|
|||||||
@Throws(BackendException::class)
|
@Throws(BackendException::class)
|
||||||
private fun file(cryptoParent: CryptoFolder, cleartextName: String, ciphertextName: String, cleartextSize: Long?): CryptoFile {
|
private fun file(cryptoParent: CryptoFolder, cleartextName: String, ciphertextName: String, cleartextSize: Long?): CryptoFile {
|
||||||
val ciphertextSize = cleartextSize?.let { cryptor().fileContentCryptor().ciphertextSize(it) + cryptor().fileHeaderCryptor().headerSize() }
|
val ciphertextSize = cleartextSize?.let { cryptor().fileContentCryptor().ciphertextSize(it) + cryptor().fileHeaderCryptor().headerSize() }
|
||||||
val cloudFile = cloudContentRepository.file(dirIdInfo(cryptoParent).cloudFolder, ciphertextName, ciphertextSize)
|
val cloudFile = cloudContentRepository.file(getOrCreateCachingAwareDirIdInfo(cryptoParent).cloudFolder, ciphertextName, ciphertextSize)
|
||||||
return file(cryptoParent, cleartextName, cloudFile, cleartextSize)
|
return file(cryptoParent, cleartextName, cloudFile, cleartextSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,7 +215,9 @@ abstract class CryptoImplDecorator(
|
|||||||
@Throws(BackendException::class)
|
@Throws(BackendException::class)
|
||||||
private fun exists(folder: CryptoFolder): Boolean {
|
private fun exists(folder: CryptoFolder): Boolean {
|
||||||
requireNotNull(folder.dirFile)
|
requireNotNull(folder.dirFile)
|
||||||
return cloudContentRepository.exists(folder.dirFile) && cloudContentRepository.exists(dirIdInfo(folder).cloudFolder)
|
return cloudContentRepository.exists(folder.dirFile) && getCachingAwareDirIdInfo(folder)?.let {
|
||||||
|
cloudContentRepository.exists(it.cloudFolder)
|
||||||
|
} ?: false
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(BackendException::class)
|
@Throws(BackendException::class)
|
||||||
@ -352,8 +357,13 @@ abstract class CryptoImplDecorator(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Throws(BackendException::class)
|
@Throws(BackendException::class)
|
||||||
fun dirIdInfo(folder: CryptoFolder): DirIdInfo {
|
fun getOrCreateCachingAwareDirIdInfo(folder: CryptoFolder): DirIdInfo {
|
||||||
return dirIdCache[folder] ?: return createDirIdInfo(folder)
|
return dirIdCache[folder] ?: return getOrCreateDirIdInfo(folder)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(BackendException::class)
|
||||||
|
fun getCachingAwareDirIdInfo(folder: CryptoFolder): DirIdInfo? {
|
||||||
|
return dirIdCache[folder] ?: return getDirIdInfo(folder)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(BackendException::class)
|
@Throws(BackendException::class)
|
||||||
|
@ -64,7 +64,7 @@ open class CryptoImplVaultFormat7 : CryptoImplDecorator {
|
|||||||
@Throws(BackendException::class)
|
@Throws(BackendException::class)
|
||||||
override fun folder(cryptoParent: CryptoFolder, cleartextName: String): CryptoFolder {
|
override fun folder(cryptoParent: CryptoFolder, cleartextName: String): CryptoFolder {
|
||||||
val dirFileName = encryptFolderName(cryptoParent, cleartextName)
|
val dirFileName = encryptFolderName(cryptoParent, cleartextName)
|
||||||
val dirFolder = cloudContentRepository.folder(dirIdInfo(cryptoParent).cloudFolder, dirFileName)
|
val dirFolder = cloudContentRepository.folder(getOrCreateCachingAwareDirIdInfo(cryptoParent).cloudFolder, dirFileName)
|
||||||
val dirFile = cloudContentRepository.file(dirFolder, CLOUD_FOLDER_DIR_FILE_PRE + CLOUD_NODE_EXT)
|
val dirFile = cloudContentRepository.file(dirFolder, CLOUD_FOLDER_DIR_FILE_PRE + CLOUD_NODE_EXT)
|
||||||
return folder(cryptoParent, cleartextName, dirFile)
|
return folder(cryptoParent, cleartextName, dirFile)
|
||||||
}
|
}
|
||||||
@ -73,7 +73,7 @@ open class CryptoImplVaultFormat7 : CryptoImplDecorator {
|
|||||||
override fun encryptName(cryptoParent: CryptoFolder, name: String): String {
|
override fun encryptName(cryptoParent: CryptoFolder, name: String): String {
|
||||||
var ciphertextName: String = cryptor() //
|
var ciphertextName: String = cryptor() //
|
||||||
.fileNameCryptor() //
|
.fileNameCryptor() //
|
||||||
.encryptFilename(BaseEncoding.base64Url(), name, dirIdInfo(cryptoParent).id.toByteArray(StandardCharsets.UTF_8)) + CLOUD_NODE_EXT
|
.encryptFilename(BaseEncoding.base64Url(), name, getOrCreateCachingAwareDirIdInfo(cryptoParent).id.toByteArray(StandardCharsets.UTF_8)) + CLOUD_NODE_EXT
|
||||||
if (ciphertextName.length > shorteningThreshold) {
|
if (ciphertextName.length > shorteningThreshold) {
|
||||||
ciphertextName = deflate(cryptoParent, ciphertextName)
|
ciphertextName = deflate(cryptoParent, ciphertextName)
|
||||||
}
|
}
|
||||||
@ -85,7 +85,7 @@ open class CryptoImplVaultFormat7 : CryptoImplDecorator {
|
|||||||
val longFilenameBytes = longFileName.toByteArray(StandardCharsets.UTF_8)
|
val longFilenameBytes = longFileName.toByteArray(StandardCharsets.UTF_8)
|
||||||
val hash = MessageDigestSupplier.SHA1.get().digest(longFilenameBytes)
|
val hash = MessageDigestSupplier.SHA1.get().digest(longFilenameBytes)
|
||||||
val shortFileName = BaseEncoding.base64Url().encode(hash) + LONG_NODE_FILE_EXT
|
val shortFileName = BaseEncoding.base64Url().encode(hash) + LONG_NODE_FILE_EXT
|
||||||
var dirFolder = cloudContentRepository.folder(dirIdInfo(cryptoParent).cloudFolder, shortFileName)
|
var dirFolder = cloudContentRepository.folder(getOrCreateCachingAwareDirIdInfo(cryptoParent).cloudFolder, shortFileName)
|
||||||
|
|
||||||
// if folder already exists in case of renaming
|
// if folder already exists in case of renaming
|
||||||
if (!cloudContentRepository.exists(dirFolder)) {
|
if (!cloudContentRepository.exists(dirFolder)) {
|
||||||
@ -98,6 +98,14 @@ open class CryptoImplVaultFormat7 : CryptoImplDecorator {
|
|||||||
return shortFileName
|
return shortFileName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Throws(BackendException::class)
|
||||||
|
private fun inflate(cloudNode: CloudNode): String {
|
||||||
|
val metadataFile = metadataFile(cloudNode)
|
||||||
|
val out = ByteArrayOutputStream()
|
||||||
|
cloudContentRepository.read(metadataFile, null, out, ProgressAware.NO_OP_PROGRESS_AWARE_DOWNLOAD)
|
||||||
|
return String(out.toByteArray(), StandardCharsets.UTF_8)
|
||||||
|
}
|
||||||
|
|
||||||
@Throws(BackendException::class)
|
@Throws(BackendException::class)
|
||||||
private fun metadataFile(cloudNode: CloudNode): CloudFile {
|
private fun metadataFile(cloudNode: CloudNode): CloudFile {
|
||||||
val cloudFolder = when (cloudNode) {
|
val cloudFolder = when (cloudNode) {
|
||||||
@ -114,14 +122,6 @@ open class CryptoImplVaultFormat7 : CryptoImplDecorator {
|
|||||||
return cloudContentRepository.file(cloudFolder, LONG_NODE_FILE_CONTENT_NAME + LONG_NODE_FILE_EXT)
|
return cloudContentRepository.file(cloudFolder, LONG_NODE_FILE_CONTENT_NAME + LONG_NODE_FILE_EXT)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(BackendException::class)
|
|
||||||
private fun inflate(cloudNode: CloudNode): String {
|
|
||||||
val metadataFile = metadataFile(cloudNode)
|
|
||||||
val out = ByteArrayOutputStream()
|
|
||||||
cloudContentRepository.read(metadataFile, null, out, ProgressAware.NO_OP_PROGRESS_AWARE_DOWNLOAD)
|
|
||||||
return String(out.toByteArray(), StandardCharsets.UTF_8)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun decryptName(dirId: String, encryptedName: String): String? {
|
override fun decryptName(dirId: String, encryptedName: String): String? {
|
||||||
return extractEncryptedName(encryptedName)?.let {
|
return extractEncryptedName(encryptedName)?.let {
|
||||||
return cryptor().fileNameCryptor().decryptFilename(BaseEncoding.base64Url(), it, dirId.toByteArray(StandardCharsets.UTF_8))
|
return cryptor().fileNameCryptor().decryptFilename(BaseEncoding.base64Url(), it, dirId.toByteArray(StandardCharsets.UTF_8))
|
||||||
@ -132,34 +132,41 @@ open class CryptoImplVaultFormat7 : CryptoImplDecorator {
|
|||||||
override fun list(cryptoFolder: CryptoFolder): List<CryptoNode> {
|
override fun list(cryptoFolder: CryptoFolder): List<CryptoNode> {
|
||||||
dirIdCache.evictSubFoldersOf(cryptoFolder)
|
dirIdCache.evictSubFoldersOf(cryptoFolder)
|
||||||
|
|
||||||
val dirIdInfo = dirIdInfo(cryptoFolder)
|
val dirIdInfo = getCachingAwareDirIdInfo(cryptoFolder)
|
||||||
val dirId = dirIdInfo(cryptoFolder).id
|
?: when (cryptoFolder.dirFile) {
|
||||||
|
null -> {
|
||||||
|
Timber.tag("CryptoFs").e(String.format("Dir-file of folder is null %s", cryptoFolder.path))
|
||||||
|
throw FatalBackendException(String.format("Dir-file of folder is null %s", cryptoFolder.path))
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
Timber.tag("CryptoFs").e("No dir file exists in %s", cryptoFolder.dirFile.path)
|
||||||
|
throw NoDirFileException(cryptoFolder.name, cryptoFolder.dirFile.path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val dirId = dirIdInfo.id
|
||||||
val lvl2Dir = dirIdInfo.cloudFolder
|
val lvl2Dir = dirIdInfo.cloudFolder
|
||||||
|
|
||||||
val ciphertextNodes: List<CloudNode> = try {
|
return try {
|
||||||
cloudContentRepository.list(lvl2Dir)
|
cloudContentRepository.list(lvl2Dir)
|
||||||
} catch (e: NoSuchCloudFileException) {
|
} catch (e: NoSuchCloudFileException) {
|
||||||
if (cryptoFolder is RootCryptoFolder) {
|
when {
|
||||||
Timber.tag("CryptoFs").e("No lvl2Dir exists for root folder in %s", lvl2Dir.path)
|
cryptoFolder is RootCryptoFolder -> {
|
||||||
throw FatalBackendException(String.format("No lvl2Dir exists for root folder in %s", lvl2Dir.path), e)
|
Timber.tag("CryptoFs").e("No lvl2Dir exists for root folder in %s", lvl2Dir.path)
|
||||||
} else if (cryptoFolder.dirFile == null) {
|
throw FatalBackendException(String.format("No lvl2Dir exists for root folder in %s", lvl2Dir.path), e)
|
||||||
Timber.tag("CryptoFs").e(String.format("Dir-file of folder is null %s", lvl2Dir.path))
|
}
|
||||||
throw FatalBackendException(String.format("Dir-file of folder is null %s", lvl2Dir.path))
|
cryptoFolder.dirFile == null -> {
|
||||||
} else if (cloudContentRepository.exists(cloudContentRepository.file(cryptoFolder.dirFile.parent, CLOUD_NODE_SYMLINK_PRE + CLOUD_NODE_EXT))) {
|
Timber.tag("CryptoFs").e(String.format("Dir-file of folder is null %s", lvl2Dir.path))
|
||||||
throw SymLinkException()
|
throw FatalBackendException(String.format("Dir-file of folder is null %s", lvl2Dir.path))
|
||||||
} else if (!cloudContentRepository.exists(cryptoFolder.dirFile)) {
|
}
|
||||||
Timber.tag("CryptoFs").e("No dir file exists in %s", cryptoFolder.dirFile.path)
|
cloudContentRepository.exists(cloudContentRepository.file(cryptoFolder.dirFile.parent, CLOUD_NODE_SYMLINK_PRE + CLOUD_NODE_EXT)) -> {
|
||||||
throw NoDirFileException(cryptoFolder.name, cryptoFolder.dirFile.path)
|
throw SymLinkException()
|
||||||
|
}
|
||||||
|
else -> return emptyList()
|
||||||
}
|
}
|
||||||
return emptyList()
|
}.map { node ->
|
||||||
}
|
ciphertextToCleartextNode(cryptoFolder, dirId, node)
|
||||||
|
}.toList().filterNotNull()
|
||||||
return ciphertextNodes
|
|
||||||
.map { node ->
|
|
||||||
ciphertextToCleartextNode(cryptoFolder, dirId, node)
|
|
||||||
}
|
|
||||||
.toList()
|
|
||||||
.filterNotNull()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(BackendException::class)
|
@Throws(BackendException::class)
|
||||||
@ -247,11 +254,17 @@ open class CryptoImplVaultFormat7 : CryptoImplDecorator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Throws(BackendException::class)
|
@Throws(BackendException::class)
|
||||||
override fun createDirIdInfo(folder: CryptoFolder): DirIdInfo {
|
override fun getOrCreateDirIdInfo(folder: CryptoFolder): DirIdInfo {
|
||||||
val dirId = loadDirId(folder)
|
val dirId = loadDirId(folder) ?: newDirId()
|
||||||
return dirIdCache.put(folder, createDirIdInfoFor(dirId))
|
return dirIdCache.put(folder, createDirIdInfoFor(dirId))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getDirIdInfo(folder: CryptoFolder): DirIdInfo? {
|
||||||
|
return loadDirId(folder)?.let {
|
||||||
|
dirIdCache.put(folder, createDirIdInfoFor(it))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Throws(BackendException::class)
|
@Throws(BackendException::class)
|
||||||
override fun encryptFolderName(cryptoFolder: CryptoFolder, name: String): String {
|
override fun encryptFolderName(cryptoFolder: CryptoFolder, name: String): String {
|
||||||
return encryptName(cryptoFolder, name)
|
return encryptName(cryptoFolder, name)
|
||||||
@ -263,17 +276,13 @@ open class CryptoImplVaultFormat7 : CryptoImplDecorator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Throws(BackendException::class, EmptyDirFileException::class)
|
@Throws(BackendException::class, EmptyDirFileException::class)
|
||||||
override fun loadDirId(folder: CryptoFolder): String {
|
override fun loadDirId(folder: CryptoFolder): String? {
|
||||||
var dirFile: CloudFile? = null
|
|
||||||
if (folder.dirFile != null) {
|
|
||||||
dirFile = folder.dirFile
|
|
||||||
}
|
|
||||||
return if (RootCryptoFolder.isRoot(folder)) {
|
return if (RootCryptoFolder.isRoot(folder)) {
|
||||||
CryptoConstants.ROOT_DIR_ID
|
CryptoConstants.ROOT_DIR_ID
|
||||||
} else if (dirFile != null && cloudContentRepository.exists(dirFile)) {
|
} else if (folder.dirFile != null && cloudContentRepository.exists(folder.dirFile)) {
|
||||||
String(loadContentsOfDirFile(dirFile), StandardCharsets.UTF_8)
|
String(loadContentsOfDirFile(folder.dirFile), StandardCharsets.UTF_8)
|
||||||
} else {
|
} else {
|
||||||
newDirId()
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -302,7 +311,7 @@ open class CryptoImplVaultFormat7 : CryptoImplDecorator {
|
|||||||
assertCryptoFolderAlreadyExists(folder)
|
assertCryptoFolderAlreadyExists(folder)
|
||||||
shortName = true
|
shortName = true
|
||||||
}
|
}
|
||||||
val dirIdInfo = dirIdInfo(folder)
|
val dirIdInfo = getOrCreateCachingAwareDirIdInfo(folder)
|
||||||
val createdCloudFolder = cloudContentRepository.create(dirIdInfo.cloudFolder)
|
val createdCloudFolder = cloudContentRepository.create(dirIdInfo.cloudFolder)
|
||||||
var dirFolder = folder.dirFile.parent
|
var dirFolder = folder.dirFile.parent
|
||||||
var dirFile = folder.dirFile
|
var dirFile = folder.dirFile
|
||||||
@ -407,17 +416,13 @@ open class CryptoImplVaultFormat7 : CryptoImplDecorator {
|
|||||||
requireNotNull(node.dirFile)
|
requireNotNull(node.dirFile)
|
||||||
val cryptoSubfolders = deepCollectSubfolders(node)
|
val cryptoSubfolders = deepCollectSubfolders(node)
|
||||||
for (cryptoSubfolder in cryptoSubfolders) {
|
for (cryptoSubfolder in cryptoSubfolders) {
|
||||||
try {
|
getCachingAwareDirIdInfo(cryptoSubfolder)?.let {
|
||||||
cloudContentRepository.delete(dirIdInfo(cryptoSubfolder).cloudFolder)
|
cloudContentRepository.delete(it.cloudFolder)
|
||||||
} catch (e: NoSuchCloudFileException) {
|
} ?: Timber.tag("CryptoFs").w("Dir file doesn't exists of a sub folder while deleting the parent, continue anyway")
|
||||||
// Ignoring because nothing can be done if the dir-file doesn't exists in the cloud
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
cloudContentRepository.delete(dirIdInfo(node).cloudFolder)
|
|
||||||
} catch (e: NoSuchCloudFileException) {
|
|
||||||
// Ignoring because nothing can be done if the dir-file doesn't exists in the cloud
|
|
||||||
}
|
}
|
||||||
|
getCachingAwareDirIdInfo(node)?.let {
|
||||||
|
cloudContentRepository.delete(it.cloudFolder)
|
||||||
|
} ?: Timber.tag("CryptoFs").w("Dir file doesn't exists while deleting the folder, continue anyway")
|
||||||
cloudContentRepository.delete(node.dirFile.parent)
|
cloudContentRepository.delete(node.dirFile.parent)
|
||||||
evictFromCache(node)
|
evictFromCache(node)
|
||||||
} else if (node is CryptoFile) {
|
} else if (node is CryptoFile) {
|
||||||
|
@ -16,6 +16,7 @@ import org.cryptomator.domain.CloudNode
|
|||||||
import org.cryptomator.domain.exception.AlreadyExistException
|
import org.cryptomator.domain.exception.AlreadyExistException
|
||||||
import org.cryptomator.domain.exception.BackendException
|
import org.cryptomator.domain.exception.BackendException
|
||||||
import org.cryptomator.domain.exception.EmptyDirFileException
|
import org.cryptomator.domain.exception.EmptyDirFileException
|
||||||
|
import org.cryptomator.domain.exception.NoDirFileException
|
||||||
import org.cryptomator.domain.exception.NoSuchCloudFileException
|
import org.cryptomator.domain.exception.NoSuchCloudFileException
|
||||||
import org.cryptomator.domain.exception.ParentFolderIsNullException
|
import org.cryptomator.domain.exception.ParentFolderIsNullException
|
||||||
import org.cryptomator.domain.repository.CloudContentRepository
|
import org.cryptomator.domain.repository.CloudContentRepository
|
||||||
@ -27,7 +28,6 @@ import java.io.ByteArrayOutputStream
|
|||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import java.util.function.Supplier
|
import java.util.function.Supplier
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
import kotlin.streams.toList
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
internal class CryptoImplVaultFormatPre7(
|
internal class CryptoImplVaultFormatPre7(
|
||||||
@ -44,7 +44,7 @@ internal class CryptoImplVaultFormatPre7(
|
|||||||
@Throws(BackendException::class)
|
@Throws(BackendException::class)
|
||||||
override fun folder(cryptoParent: CryptoFolder, cleartextName: String): CryptoFolder {
|
override fun folder(cryptoParent: CryptoFolder, cleartextName: String): CryptoFolder {
|
||||||
val dirFileName = encryptFolderName(cryptoParent, cleartextName)
|
val dirFileName = encryptFolderName(cryptoParent, cleartextName)
|
||||||
val dirFile = cloudContentRepository.file(dirIdInfo(cryptoParent).cloudFolder, dirFileName)
|
val dirFile = cloudContentRepository.file(getOrCreateCachingAwareDirIdInfo(cryptoParent).cloudFolder, dirFileName)
|
||||||
return folder(cryptoParent, cleartextName, dirFile)
|
return folder(cryptoParent, cleartextName, dirFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ internal class CryptoImplVaultFormatPre7(
|
|||||||
override fun create(folder: CryptoFolder): CryptoFolder {
|
override fun create(folder: CryptoFolder): CryptoFolder {
|
||||||
requireNotNull(folder.dirFile)
|
requireNotNull(folder.dirFile)
|
||||||
assertCryptoFolderAlreadyExists(folder)
|
assertCryptoFolderAlreadyExists(folder)
|
||||||
val dirIdInfo = dirIdInfo(folder)
|
val dirIdInfo = getOrCreateCachingAwareDirIdInfo(folder)
|
||||||
val createdCloudFolder = cloudContentRepository.create(dirIdInfo.cloudFolder)
|
val createdCloudFolder = cloudContentRepository.create(dirIdInfo.cloudFolder)
|
||||||
val dirId = dirIdInfo.id.toByteArray(StandardCharsets.UTF_8)
|
val dirId = dirIdInfo.id.toByteArray(StandardCharsets.UTF_8)
|
||||||
val createdDirFile = cloudContentRepository.write(folder.dirFile, from(dirId), ProgressAware.NO_OP_PROGRESS_AWARE_UPLOAD, false, dirId.size.toLong())
|
val createdDirFile = cloudContentRepository.write(folder.dirFile, from(dirId), ProgressAware.NO_OP_PROGRESS_AWARE_UPLOAD, false, dirId.size.toLong())
|
||||||
@ -68,7 +68,7 @@ internal class CryptoImplVaultFormatPre7(
|
|||||||
|
|
||||||
@Throws(BackendException::class)
|
@Throws(BackendException::class)
|
||||||
private fun encryptName(cryptoParent: CryptoFolder, name: String, prefix: String): String {
|
private fun encryptName(cryptoParent: CryptoFolder, name: String, prefix: String): String {
|
||||||
var ciphertextName = prefix + cryptor().fileNameCryptor().encryptFilename(BaseEncoding.base32(), name, dirIdInfo(cryptoParent).id.toByteArray(StandardCharsets.UTF_8))
|
var ciphertextName = prefix + cryptor().fileNameCryptor().encryptFilename(BaseEncoding.base32(), name, getOrCreateCachingAwareDirIdInfo(cryptoParent).id.toByteArray(StandardCharsets.UTF_8))
|
||||||
if (ciphertextName.length > shorteningThreshold) {
|
if (ciphertextName.length > shorteningThreshold) {
|
||||||
ciphertextName = deflate(ciphertextName)
|
ciphertextName = deflate(ciphertextName)
|
||||||
}
|
}
|
||||||
@ -120,8 +120,8 @@ internal class CryptoImplVaultFormatPre7(
|
|||||||
|
|
||||||
@Throws(BackendException::class)
|
@Throws(BackendException::class)
|
||||||
override fun list(cryptoFolder: CryptoFolder): List<CryptoNode> {
|
override fun list(cryptoFolder: CryptoFolder): List<CryptoNode> {
|
||||||
val dirIdInfo = dirIdInfo(cryptoFolder)
|
val dirIdInfo = getDirIdInfo(cryptoFolder) ?: throw NoDirFileException(cryptoFolder.name, cryptoFolder.dirFile?.path)
|
||||||
val dirId = dirIdInfo(cryptoFolder).id
|
val dirId = dirIdInfo.id
|
||||||
val lvl2Dir = dirIdInfo.cloudFolder
|
val lvl2Dir = dirIdInfo.cloudFolder
|
||||||
return cloudContentRepository
|
return cloudContentRepository
|
||||||
.list(lvl2Dir)
|
.list(lvl2Dir)
|
||||||
@ -198,7 +198,7 @@ internal class CryptoImplVaultFormatPre7(
|
|||||||
@Throws(BackendException::class)
|
@Throws(BackendException::class)
|
||||||
override fun symlink(cryptoParent: CryptoFolder, cleartextName: String, target: String): CryptoSymlink {
|
override fun symlink(cryptoParent: CryptoFolder, cleartextName: String, target: String): CryptoSymlink {
|
||||||
val ciphertextName = encryptSymlinkName(cryptoParent, cleartextName)
|
val ciphertextName = encryptSymlinkName(cryptoParent, cleartextName)
|
||||||
val cloudFile = cloudContentRepository.file(dirIdInfo(cryptoParent).cloudFolder, ciphertextName)
|
val cloudFile = cloudContentRepository.file(getOrCreateCachingAwareDirIdInfo(cryptoParent).cloudFolder, ciphertextName)
|
||||||
return CryptoSymlink(cryptoParent, cleartextName, path(cryptoParent, cleartextName), target, cloudFile)
|
return CryptoSymlink(cryptoParent, cleartextName, path(cryptoParent, cleartextName), target, cloudFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,9 +237,13 @@ internal class CryptoImplVaultFormatPre7(
|
|||||||
requireNotNull(node.dirFile)
|
requireNotNull(node.dirFile)
|
||||||
val cryptoSubfolders = deepCollectSubfolders(node)
|
val cryptoSubfolders = deepCollectSubfolders(node)
|
||||||
for (cryptoSubfolder in cryptoSubfolders) {
|
for (cryptoSubfolder in cryptoSubfolders) {
|
||||||
cloudContentRepository.delete(dirIdInfo(cryptoSubfolder).cloudFolder)
|
getCachingAwareDirIdInfo(cryptoSubfolder)?.let {
|
||||||
|
cloudContentRepository.delete(it.cloudFolder)
|
||||||
|
} ?: Timber.tag("CryptoFs").w("Dir file doesn't exists of a sub folder while deleting the parent, continue anyway")
|
||||||
}
|
}
|
||||||
cloudContentRepository.delete(dirIdInfo(node).cloudFolder)
|
getCachingAwareDirIdInfo(node)?.let {
|
||||||
|
cloudContentRepository.delete(it.cloudFolder)
|
||||||
|
} ?: Timber.tag("CryptoFs").w("Dir file doesn't exists while deleting the folder, continue anyway")
|
||||||
cloudContentRepository.delete(node.dirFile)
|
cloudContentRepository.delete(node.dirFile)
|
||||||
evictFromCache(node)
|
evictFromCache(node)
|
||||||
} else if (node is CryptoFile) {
|
} else if (node is CryptoFile) {
|
||||||
@ -248,22 +252,28 @@ internal class CryptoImplVaultFormatPre7(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Throws(BackendException::class, EmptyDirFileException::class)
|
@Throws(BackendException::class, EmptyDirFileException::class)
|
||||||
override fun loadDirId(folder: CryptoFolder): String {
|
override fun loadDirId(folder: CryptoFolder): String? {
|
||||||
return if (isRoot(folder)) {
|
return if (isRoot(folder)) {
|
||||||
CryptoConstants.ROOT_DIR_ID
|
CryptoConstants.ROOT_DIR_ID
|
||||||
} else if (folder.dirFile != null && cloudContentRepository.exists(folder.dirFile)) {
|
} else if (folder.dirFile != null && cloudContentRepository.exists(folder.dirFile)) {
|
||||||
String(loadContentsOfDirFile(folder), StandardCharsets.UTF_8)
|
String(loadContentsOfDirFile(folder), StandardCharsets.UTF_8)
|
||||||
} else {
|
} else {
|
||||||
newDirId()
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(BackendException::class)
|
@Throws(BackendException::class)
|
||||||
override fun createDirIdInfo(folder: CryptoFolder): DirIdInfo {
|
override fun getOrCreateDirIdInfo(folder: CryptoFolder): DirIdInfo {
|
||||||
val dirId = loadDirId(folder)
|
val dirId = loadDirId(folder) ?: newDirId()
|
||||||
return dirIdCache.put(folder, createDirIdInfoFor(dirId))
|
return dirIdCache.put(folder, createDirIdInfoFor(dirId))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getDirIdInfo(folder: CryptoFolder): DirIdInfo? {
|
||||||
|
return loadDirId(folder)?.let {
|
||||||
|
dirIdCache.put(folder, createDirIdInfoFor(it))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Throws(BackendException::class)
|
@Throws(BackendException::class)
|
||||||
override fun write(cryptoFile: CryptoFile, data: DataSource, progressAware: ProgressAware<UploadState>, replace: Boolean, length: Long): CryptoFile {
|
override fun write(cryptoFile: CryptoFile, data: DataSource, progressAware: ProgressAware<UploadState>, replace: Boolean, length: Long): CryptoFile {
|
||||||
return writeShortNameFile(cryptoFile, data, progressAware, replace, length)
|
return writeShortNameFile(cryptoFile, data, progressAware, replace, length)
|
||||||
|
@ -19,6 +19,7 @@ import org.cryptomator.domain.Vault
|
|||||||
import org.cryptomator.domain.exception.BackendException
|
import org.cryptomator.domain.exception.BackendException
|
||||||
import org.cryptomator.domain.exception.CancellationException
|
import org.cryptomator.domain.exception.CancellationException
|
||||||
import org.cryptomator.domain.exception.FatalBackendException
|
import org.cryptomator.domain.exception.FatalBackendException
|
||||||
|
import org.cryptomator.domain.exception.vaultconfig.MissingVaultConfigFileException
|
||||||
import org.cryptomator.domain.exception.vaultconfig.UnsupportedMasterkeyLocationException
|
import org.cryptomator.domain.exception.vaultconfig.UnsupportedMasterkeyLocationException
|
||||||
import org.cryptomator.domain.repository.CloudContentRepository
|
import org.cryptomator.domain.repository.CloudContentRepository
|
||||||
import org.cryptomator.domain.usecases.ProgressAware
|
import org.cryptomator.domain.usecases.ProgressAware
|
||||||
@ -204,10 +205,16 @@ class MasterkeyCryptoCloudProvider(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun assertLegacyVaultVersionIsSupported(version: Int) {
|
private fun assertLegacyVaultVersionIsSupported(version: Int) {
|
||||||
if (version < CryptoConstants.MIN_VAULT_VERSION) {
|
when {
|
||||||
throw UnsupportedVaultFormatException(version, CryptoConstants.MIN_VAULT_VERSION)
|
version < CryptoConstants.MIN_VAULT_VERSION -> {
|
||||||
} else if (version > CryptoConstants.MAX_VAULT_VERSION_WITHOUT_VAULT_CONFIG) {
|
throw UnsupportedVaultFormatException(version, CryptoConstants.MIN_VAULT_VERSION)
|
||||||
throw UnsupportedVaultFormatException(version, CryptoConstants.MAX_VAULT_VERSION_WITHOUT_VAULT_CONFIG)
|
}
|
||||||
|
version == CryptoConstants.DEFAULT_MASTERKEY_FILE_VERSION -> {
|
||||||
|
throw MissingVaultConfigFileException()
|
||||||
|
}
|
||||||
|
version > CryptoConstants.MAX_VAULT_VERSION_WITHOUT_VAULT_CONFIG -> {
|
||||||
|
throw UnsupportedVaultFormatException(version, CryptoConstants.MAX_VAULT_VERSION_WITHOUT_VAULT_CONFIG)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ internal object LocalStorageAccessFrameworkNodeFactory {
|
|||||||
getNodePath(parent, documentFile.name), //
|
getNodePath(parent, documentFile.name), //
|
||||||
documentFile.length(), //
|
documentFile.length(), //
|
||||||
Date(documentFile.lastModified()), //
|
Date(documentFile.lastModified()), //
|
||||||
DocumentsContract.getTreeDocumentId(documentFile.uri), //
|
DocumentsContract.getDocumentId(documentFile.uri), //
|
||||||
documentFile.uri.toString()
|
documentFile.uri.toString()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -109,7 +109,7 @@ internal class WebDavCompatibleHttpClient(cloud: WebDavCloud, context: Context)
|
|||||||
private fun httpAuthenticator(context: Context, webDavCloud: WebDavCloud, authCache: Map<String, CachingAuthenticator>): Authenticator {
|
private fun httpAuthenticator(context: Context, webDavCloud: WebDavCloud, authCache: Map<String, CachingAuthenticator>): Authenticator {
|
||||||
val credentials = Credentials(webDavCloud.username(), decryptPassword(context, webDavCloud.password()))
|
val credentials = Credentials(webDavCloud.username(), decryptPassword(context, webDavCloud.password()))
|
||||||
val basicAuthenticator = BasicAuthenticator(credentials, StandardCharsets.UTF_8)
|
val basicAuthenticator = BasicAuthenticator(credentials, StandardCharsets.UTF_8)
|
||||||
val digestAuthenticator = DigestAuthenticator(credentials)
|
val digestAuthenticator = DigestAuthenticator(credentials, StandardCharsets.UTF_8)
|
||||||
val authenticator = DispatchingAuthenticator.Builder() //
|
val authenticator = DispatchingAuthenticator.Builder() //
|
||||||
.with("digest", digestAuthenticator) //
|
.with("digest", digestAuthenticator) //
|
||||||
.with("basic", basicAuthenticator) //
|
.with("basic", basicAuthenticator) //
|
||||||
|
@ -29,7 +29,8 @@ class DatabaseUpgrades {
|
|||||||
Upgrade7To8 upgrade7To8, //
|
Upgrade7To8 upgrade7To8, //
|
||||||
Upgrade8To9 upgrade8To9, //
|
Upgrade8To9 upgrade8To9, //
|
||||||
Upgrade9To10 upgrade9To10, //
|
Upgrade9To10 upgrade9To10, //
|
||||||
Upgrade10To11 upgrade10To11
|
Upgrade10To11 upgrade10To11, //
|
||||||
|
Upgrade11To12 upgrade11To12
|
||||||
) {
|
) {
|
||||||
|
|
||||||
availableUpgrades = defineUpgrades( //
|
availableUpgrades = defineUpgrades( //
|
||||||
@ -43,7 +44,8 @@ class DatabaseUpgrades {
|
|||||||
upgrade7To8, //
|
upgrade7To8, //
|
||||||
upgrade8To9, //
|
upgrade8To9, //
|
||||||
upgrade9To10, //
|
upgrade9To10, //
|
||||||
upgrade10To11);
|
upgrade10To11, //
|
||||||
|
upgrade11To12);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<Integer, List<DatabaseUpgrade>> defineUpgrades(DatabaseUpgrade... upgrades) {
|
private Map<Integer, List<DatabaseUpgrade>> defineUpgrades(DatabaseUpgrade... upgrades) {
|
||||||
|
17
data/src/main/java/org/cryptomator/data/db/Upgrade11To12.kt
Normal file
17
data/src/main/java/org/cryptomator/data/db/Upgrade11To12.kt
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package org.cryptomator.data.db
|
||||||
|
|
||||||
|
import com.google.common.base.Optional
|
||||||
|
import org.cryptomator.util.SharedPreferencesHandler
|
||||||
|
import org.greenrobot.greendao.database.Database
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
internal class Upgrade11To12 @Inject constructor(private val sharedPreferencesHandler: SharedPreferencesHandler) : DatabaseUpgrade(11, 12) {
|
||||||
|
|
||||||
|
override fun internalApplyTo(db: Database, origin: Int) {
|
||||||
|
when (sharedPreferencesHandler.updateIntervalInDays()) {
|
||||||
|
Optional.of(7), Optional.of(30) -> sharedPreferencesHandler.setUpdateIntervalInDays(Optional.of(1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -67,9 +67,9 @@ class CloudRepositoryImpl implements CloudRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Cloud storedCloud = mapper.fromEntity(database.store(mapper.toEntity(cloud)));
|
Cloud storedCloud = mapper.fromEntity(database.store(mapper.toEntity(cloud)));
|
||||||
|
database.clearCache();
|
||||||
|
|
||||||
dispatchingCloudContentRepository.updateCloudContentRepositoryFor(storedCloud);
|
dispatchingCloudContentRepository.updateCloudContentRepositoryFor(storedCloud);
|
||||||
database.clearCache();
|
|
||||||
|
|
||||||
return storedCloud;
|
return storedCloud;
|
||||||
}
|
}
|
||||||
@ -80,6 +80,7 @@ class CloudRepositoryImpl implements CloudRepository {
|
|||||||
throw new IllegalArgumentException("Can not delete non persistent cloud");
|
throw new IllegalArgumentException("Can not delete non persistent cloud");
|
||||||
}
|
}
|
||||||
database.delete(mapper.toEntity(cloud));
|
database.delete(mapper.toEntity(cloud));
|
||||||
|
dispatchingCloudContentRepository.removeCloudContentRepositoryFor(cloud);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -42,7 +42,6 @@ import java.io.InputStreamReader
|
|||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import java.util.ArrayList
|
|
||||||
import kotlin.io.path.createTempDirectory
|
import kotlin.io.path.createTempDirectory
|
||||||
import kotlin.io.path.deleteExisting
|
import kotlin.io.path.deleteExisting
|
||||||
|
|
||||||
@ -730,6 +729,7 @@ class CryptoImplVaultFormat7Test {
|
|||||||
whenever(cloudContentRepository.folder(aaFolder, shortenedFileName)).thenReturn(testDir3)
|
whenever(cloudContentRepository.folder(aaFolder, shortenedFileName)).thenReturn(testDir3)
|
||||||
whenever(cloudContentRepository.exists(testDir3)).thenReturn(false)
|
whenever(cloudContentRepository.exists(testDir3)).thenReturn(false)
|
||||||
whenever(dirIdCache.put(eq(cryptoFolder3), any())).thenReturn(DirIdInfo("dir3-id", ddFolder))
|
whenever(dirIdCache.put(eq(cryptoFolder3), any())).thenReturn(DirIdInfo("dir3-id", ddFolder))
|
||||||
|
whenever(dirIdCache[cryptoFolder3]).thenReturn(DirIdInfo("dir3-id", ddFolder))
|
||||||
whenever(cloudContentRepository.file(testDir3, "dir.c9r")).thenReturn(testDir3DirFile)
|
whenever(cloudContentRepository.file(testDir3, "dir.c9r")).thenReturn(testDir3DirFile)
|
||||||
whenever(cloudContentRepository.file(testDir3, "name.c9s", 257L)).thenReturn(testDir3NameFile)
|
whenever(cloudContentRepository.file(testDir3, "name.c9s", 257L)).thenReturn(testDir3NameFile)
|
||||||
whenever<List<*>>(cloudContentRepository.list(ddFolder)).thenReturn(ArrayList<CloudNode>())
|
whenever<List<*>>(cloudContentRepository.list(ddFolder)).thenReturn(ArrayList<CloudNode>())
|
||||||
|
@ -33,6 +33,7 @@ import org.mockito.AdditionalMatchers
|
|||||||
import org.mockito.Mockito
|
import org.mockito.Mockito
|
||||||
import org.mockito.invocation.InvocationOnMock
|
import org.mockito.invocation.InvocationOnMock
|
||||||
import org.mockito.kotlin.any
|
import org.mockito.kotlin.any
|
||||||
|
import org.mockito.kotlin.anyOrNull
|
||||||
import org.mockito.kotlin.eq
|
import org.mockito.kotlin.eq
|
||||||
import org.mockito.kotlin.mock
|
import org.mockito.kotlin.mock
|
||||||
import org.mockito.kotlin.whenever
|
import org.mockito.kotlin.whenever
|
||||||
@ -43,7 +44,6 @@ import java.io.InputStreamReader
|
|||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import java.util.ArrayList
|
|
||||||
import java.util.function.Supplier
|
import java.util.function.Supplier
|
||||||
import kotlin.io.path.createTempDirectory
|
import kotlin.io.path.createTempDirectory
|
||||||
import kotlin.io.path.deleteExisting
|
import kotlin.io.path.deleteExisting
|
||||||
@ -138,11 +138,11 @@ internal class CryptoImplVaultFormatPre7Test {
|
|||||||
whenever(cloudContentRepository.folder(lvl2Dir, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")).thenReturn(aaFolder)
|
whenever(cloudContentRepository.folder(lvl2Dir, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")).thenReturn(aaFolder)
|
||||||
whenever(cloudContentRepository.file(aaFolder, "0dir1")).thenReturn(testDir1)
|
whenever(cloudContentRepository.file(aaFolder, "0dir1")).thenReturn(testDir1)
|
||||||
whenever(cloudContentRepository.exists(testDir1)).thenReturn(true)
|
whenever(cloudContentRepository.exists(testDir1)).thenReturn(true)
|
||||||
Mockito.doAnswer { invocation: InvocationOnMock ->
|
whenever(cloudContentRepository.read(eq(cryptoFolder1.dirFile!!), any(), any(), any())).thenAnswer { invocationOnMock: InvocationOnMock ->
|
||||||
val out = invocation.getArgument<OutputStream>(2)
|
val out = invocationOnMock.getArgument<OutputStream>(2)
|
||||||
copyStreamToStream(ByteArrayInputStream(dirId1.toByteArray()), out)
|
copyStreamToStream(ByteArrayInputStream(dirId1.toByteArray()), out)
|
||||||
null
|
null
|
||||||
}.`when`(cloudContentRepository).read(eq(cryptoFolder1.dirFile!!), any(), any(), any())
|
}
|
||||||
whenever<List<*>>(cloudContentRepository.list(aaFolder)).thenReturn(rootItems)
|
whenever<List<*>>(cloudContentRepository.list(aaFolder)).thenReturn(rootItems)
|
||||||
whenever(dirIdCache.put(eq(root), any())).thenReturn(DirIdInfo("", aaFolder))
|
whenever(dirIdCache.put(eq(root), any())).thenReturn(DirIdInfo("", aaFolder))
|
||||||
}
|
}
|
||||||
@ -193,11 +193,11 @@ internal class CryptoImplVaultFormatPre7Test {
|
|||||||
|
|
||||||
val cryptoFolder3 = CryptoFolder(cryptoFolder1, dir3Name, "/Directory 1/$dir3Name", testDir3DirFile)
|
val cryptoFolder3 = CryptoFolder(cryptoFolder1, dir3Name, "/Directory 1/$dir3Name", testDir3DirFile)
|
||||||
|
|
||||||
Mockito.doAnswer { invocation: InvocationOnMock ->
|
whenever(cloudContentRepository.read(eq(cryptoFolder3.dirFile!!), anyOrNull(), any(), any())).thenAnswer { invocationOnMock: InvocationOnMock ->
|
||||||
val out = invocation.getArgument<OutputStream>(2)
|
val out = invocationOnMock.getArgument<OutputStream>(2)
|
||||||
copyStreamToStream(ByteArrayInputStream("dir3-id".toByteArray()), out)
|
copyStreamToStream(ByteArrayInputStream("dir3-id".toByteArray()), out)
|
||||||
null
|
null
|
||||||
}.`when`(cloudContentRepository).read(eq(cryptoFolder3.dirFile!!), any(), any(), any())
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* │ ├─ Directory 3x250
|
* │ ├─ Directory 3x250
|
||||||
@ -217,11 +217,11 @@ internal class CryptoImplVaultFormatPre7Test {
|
|||||||
whenever(cloudContentRepository.file(directory4x250, "name.c9s")).thenReturn(testDir4NameFile)
|
whenever(cloudContentRepository.file(directory4x250, "name.c9s")).thenReturn(testDir4NameFile)
|
||||||
whenever(fileNameCryptor.encryptFilename(BaseEncoding.base32(), dir4Name, "dir3-id".toByteArray())).thenReturn(dir4Cipher)
|
whenever(fileNameCryptor.encryptFilename(BaseEncoding.base32(), dir4Name, "dir3-id".toByteArray())).thenReturn(dir4Cipher)
|
||||||
whenever(fileNameCryptor.decryptFilename(BaseEncoding.base32(), dir4Cipher, "dir3-id".toByteArray())).thenReturn(dir4Name)
|
whenever(fileNameCryptor.decryptFilename(BaseEncoding.base32(), dir4Cipher, "dir3-id".toByteArray())).thenReturn(dir4Name)
|
||||||
Mockito.doAnswer { invocation: InvocationOnMock ->
|
whenever(cloudContentRepository.read(eq(testDir4NameFile), any(), any(), any())).thenAnswer { invocationOnMock: InvocationOnMock ->
|
||||||
val out = invocation.getArgument<OutputStream>(2)
|
val out = invocationOnMock.getArgument<OutputStream>(2)
|
||||||
copyStreamToStream(ByteArrayInputStream(dir4Cipher.toByteArray(charset("UTF-8"))), out)
|
copyStreamToStream(ByteArrayInputStream(dir4Cipher.toByteArray(charset("UTF-8"))), out)
|
||||||
null
|
null
|
||||||
}.`when`(cloudContentRepository).read(eq(testDir4NameFile), any(), any(), any())
|
}
|
||||||
|
|
||||||
val dir4Files: ArrayList<CloudNode> = object : ArrayList<CloudNode>() {
|
val dir4Files: ArrayList<CloudNode> = object : ArrayList<CloudNode>() {
|
||||||
init {
|
init {
|
||||||
@ -242,11 +242,12 @@ internal class CryptoImplVaultFormatPre7Test {
|
|||||||
whenever(cloudContentRepository.file(directory5x250, "name.c9s")).thenReturn(testFile5NameFile)
|
whenever(cloudContentRepository.file(directory5x250, "name.c9s")).thenReturn(testFile5NameFile)
|
||||||
whenever(fileNameCryptor.encryptFilename(BaseEncoding.base32(), file5Name, "dir3-id".toByteArray())).thenReturn(file5Cipher)
|
whenever(fileNameCryptor.encryptFilename(BaseEncoding.base32(), file5Name, "dir3-id".toByteArray())).thenReturn(file5Cipher)
|
||||||
whenever(fileNameCryptor.decryptFilename(BaseEncoding.base32(), file5Cipher, "dir3-id".toByteArray())).thenReturn(file5Name)
|
whenever(fileNameCryptor.decryptFilename(BaseEncoding.base32(), file5Cipher, "dir3-id".toByteArray())).thenReturn(file5Name)
|
||||||
Mockito.doAnswer { invocation: InvocationOnMock ->
|
whenever(cloudContentRepository.read(eq(testFile5NameFile), any(), any(), any())).thenAnswer { invocationOnMock: InvocationOnMock ->
|
||||||
val out = invocation.getArgument<OutputStream>(2)
|
val out = invocationOnMock.getArgument<OutputStream>(2)
|
||||||
copyStreamToStream(ByteArrayInputStream(file5Cipher.toByteArray(charset("UTF-8"))), out)
|
copyStreamToStream(ByteArrayInputStream(file5Cipher.toByteArray(charset("UTF-8"))), out)
|
||||||
null
|
null
|
||||||
}.`when`(cloudContentRepository).read(eq(testFile5NameFile), any(), any(), any())
|
}
|
||||||
|
|
||||||
val dir5Files: ArrayList<CloudNode> = object : ArrayList<CloudNode>() {
|
val dir5Files: ArrayList<CloudNode> = object : ArrayList<CloudNode>() {
|
||||||
init {
|
init {
|
||||||
add(testFile5ContentFile)
|
add(testFile5ContentFile)
|
||||||
@ -286,11 +287,11 @@ internal class CryptoImplVaultFormatPre7Test {
|
|||||||
whenever(fileHeaderCryptor.decryptHeader(StandardCharsets.UTF_8.encode("hhhhh"))).thenReturn(header)
|
whenever(fileHeaderCryptor.decryptHeader(StandardCharsets.UTF_8.encode("hhhhh"))).thenReturn(header)
|
||||||
whenever(fileContentCryptor.decryptChunk(eq(StandardCharsets.UTF_8.encode("TOPSECRET!")), any(), eq(header), any()))
|
whenever(fileContentCryptor.decryptChunk(eq(StandardCharsets.UTF_8.encode("TOPSECRET!")), any(), eq(header), any()))
|
||||||
.then { invocation: InvocationOnMock? -> StandardCharsets.UTF_8.encode("geheim!!") }
|
.then { invocation: InvocationOnMock? -> StandardCharsets.UTF_8.encode("geheim!!") }
|
||||||
Mockito.doAnswer { invocation: InvocationOnMock ->
|
whenever(cloudContentRepository.read(eq(cryptoFile1.cloudFile), any(), any(), any())).thenAnswer { invocationOnMock: InvocationOnMock ->
|
||||||
val out = invocation.getArgument<OutputStream>(2)
|
val out = invocationOnMock.getArgument<OutputStream>(2)
|
||||||
copyStreamToStream(ByteArrayInputStream(file1Content), out)
|
copyStreamToStream(ByteArrayInputStream(file1Content), out)
|
||||||
null
|
null
|
||||||
}.`when`(cloudContentRepository).read(eq(cryptoFile1.cloudFile), any(), any(), any())
|
}
|
||||||
|
|
||||||
val outputStream = ByteArrayOutputStream(1000)
|
val outputStream = ByteArrayOutputStream(1000)
|
||||||
inTest.read(cryptoFile1, outputStream, ProgressAware.NO_OP_PROGRESS_AWARE_DOWNLOAD)
|
inTest.read(cryptoFile1, outputStream, ProgressAware.NO_OP_PROGRESS_AWARE_DOWNLOAD)
|
||||||
@ -318,11 +319,11 @@ internal class CryptoImplVaultFormatPre7Test {
|
|||||||
whenever(fileContentCryptor.decryptChunk(eq(StandardCharsets.UTF_8.encode("TOPSECRET!")), any(), eq(header), any()))
|
whenever(fileContentCryptor.decryptChunk(eq(StandardCharsets.UTF_8.encode("TOPSECRET!")), any(), eq(header), any()))
|
||||||
.then { invocation: InvocationOnMock? -> StandardCharsets.UTF_8.encode("geheim!!") }
|
.then { invocation: InvocationOnMock? -> StandardCharsets.UTF_8.encode("geheim!!") }
|
||||||
val cryptoFile15 = CryptoFile(root, file3Name, "/$file3Name", null, testFile3ContentFile)
|
val cryptoFile15 = CryptoFile(root, file3Name, "/$file3Name", null, testFile3ContentFile)
|
||||||
Mockito.doAnswer { invocation: InvocationOnMock ->
|
whenever(cloudContentRepository.read(eq(cryptoFile15.cloudFile), any(), any(), any())).thenAnswer { invocationOnMock: InvocationOnMock ->
|
||||||
val out = invocation.getArgument<OutputStream>(2)
|
val out = invocationOnMock.getArgument<OutputStream>(2)
|
||||||
copyStreamToStream(ByteArrayInputStream(file1Content), out)
|
copyStreamToStream(ByteArrayInputStream(file1Content), out)
|
||||||
null
|
null
|
||||||
}.`when`(cloudContentRepository).read(eq(cryptoFile15.cloudFile), any(), any(), any())
|
}
|
||||||
|
|
||||||
val outputStream = ByteArrayOutputStream(1000)
|
val outputStream = ByteArrayOutputStream(1000)
|
||||||
inTest.read(cryptoFile15, outputStream, ProgressAware.NO_OP_PROGRESS_AWARE_DOWNLOAD)
|
inTest.read(cryptoFile15, outputStream, ProgressAware.NO_OP_PROGRESS_AWARE_DOWNLOAD)
|
||||||
@ -593,11 +594,11 @@ internal class CryptoImplVaultFormatPre7Test {
|
|||||||
val testDir2DirFile = TestFile(bbFolder, "0dir2", "/d/11/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBB/0dir2", null, null)
|
val testDir2DirFile = TestFile(bbFolder, "0dir2", "/d/11/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBB/0dir2", null, null)
|
||||||
val cryptoFolder2 = CryptoFolder(cryptoFolder1, "Directory 2", "/Directory 1/Directory 2", testDir2DirFile)
|
val cryptoFolder2 = CryptoFolder(cryptoFolder1, "Directory 2", "/Directory 1/Directory 2", testDir2DirFile)
|
||||||
|
|
||||||
Mockito.doAnswer { invocation: InvocationOnMock ->
|
whenever(cloudContentRepository.read(eq(cryptoFolder2.dirFile!!), anyOrNull(), any(), any())).thenAnswer { invocationOnMock: InvocationOnMock ->
|
||||||
val out = invocation.getArgument<OutputStream>(2)
|
val out = invocationOnMock.getArgument<OutputStream>(2)
|
||||||
copyStreamToStream(ByteArrayInputStream(dirId2.toByteArray()), out)
|
copyStreamToStream(ByteArrayInputStream(dirId2.toByteArray()), out)
|
||||||
null
|
null
|
||||||
}.`when`(cloudContentRepository).read(eq(cryptoFolder2.dirFile!!), any(), any(), any())
|
}
|
||||||
|
|
||||||
val dir1Items: ArrayList<CloudNode> = object : ArrayList<CloudNode>() {
|
val dir1Items: ArrayList<CloudNode> = object : ArrayList<CloudNode>() {
|
||||||
init {
|
init {
|
||||||
@ -657,6 +658,7 @@ internal class CryptoImplVaultFormatPre7Test {
|
|||||||
whenever(cloudContentRepository.folder(d, "33")).thenReturn(ddLvl2Dir)
|
whenever(cloudContentRepository.folder(d, "33")).thenReturn(ddLvl2Dir)
|
||||||
whenever(cloudContentRepository.folder(lvl2Dir, "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDD")).thenReturn(ddFolder)
|
whenever(cloudContentRepository.folder(lvl2Dir, "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDD")).thenReturn(ddFolder)
|
||||||
whenever(dirIdCache.put(eq(cryptoFolder3), any())).thenReturn(DirIdInfo("dir3-id", ddFolder))
|
whenever(dirIdCache.put(eq(cryptoFolder3), any())).thenReturn(DirIdInfo("dir3-id", ddFolder))
|
||||||
|
whenever(dirIdCache[cryptoFolder3]).thenReturn(DirIdInfo("dir3-id", ddFolder))
|
||||||
whenever(cloudContentRepository.file(aaFolder, shortenedFileName)).thenReturn(testDir3DirFile)
|
whenever(cloudContentRepository.file(aaFolder, shortenedFileName)).thenReturn(testDir3DirFile)
|
||||||
whenever(cloudContentRepository.file(testDir3NameFile.parent!!, shortenedFileName, 257L)).thenReturn(testDir3NameFile)
|
whenever(cloudContentRepository.file(testDir3NameFile.parent!!, shortenedFileName, 257L)).thenReturn(testDir3NameFile)
|
||||||
whenever<List<*>>(cloudContentRepository.list(ddFolder)).thenReturn(ArrayList<CloudNode>())
|
whenever<List<*>>(cloudContentRepository.list(ddFolder)).thenReturn(ArrayList<CloudNode>())
|
||||||
@ -767,7 +769,7 @@ internal class CryptoImplVaultFormatPre7Test {
|
|||||||
val cryptoMovedFile4 = CryptoFile(cryptoFolder1, file4Name, "/Directory 1/$file4Name", null, testFile4ContentFile)
|
val cryptoMovedFile4 = CryptoFile(cryptoFolder1, file4Name, "/Directory 1/$file4Name", null, testFile4ContentFile)
|
||||||
|
|
||||||
whenever(cloudContentRepository.move(testFile4ContentFileOld, testFile4ContentFile)).thenReturn(testFile4ContentFile)
|
whenever(cloudContentRepository.move(testFile4ContentFileOld, testFile4ContentFile)).thenReturn(testFile4ContentFile)
|
||||||
whenever(cloudContentRepository.create(testFile4NameFile.parent!!)).thenReturn(testFile4NameFile.parent)
|
whenever(cloudContentRepository.create(testFile4NameFile.parent)).thenReturn(testFile4NameFile.parent)
|
||||||
whenever(cloudContentRepository.write(eq(testFile4NameFile), any(), any(), eq(true), any())).thenAnswer { invocationOnMock: InvocationOnMock ->
|
whenever(cloudContentRepository.write(eq(testFile4NameFile), any(), any(), eq(true), any())).thenAnswer { invocationOnMock: InvocationOnMock ->
|
||||||
val inputStream = invocationOnMock.getArgument<DataSource>(1)
|
val inputStream = invocationOnMock.getArgument<DataSource>(1)
|
||||||
val dirContent = BufferedReader(InputStreamReader(inputStream.open(context)!!, StandardCharsets.UTF_8)).readLine()
|
val dirContent = BufferedReader(InputStreamReader(inputStream.open(context)!!, StandardCharsets.UTF_8)).readLine()
|
||||||
|
@ -6,7 +6,6 @@ import org.cryptomator.cryptolib.api.Cryptor
|
|||||||
import org.cryptomator.cryptolib.api.CryptorProvider
|
import org.cryptomator.cryptolib.api.CryptorProvider
|
||||||
import org.cryptomator.cryptolib.api.FileNameCryptor
|
import org.cryptomator.cryptolib.api.FileNameCryptor
|
||||||
import org.cryptomator.cryptolib.api.Masterkey
|
import org.cryptomator.cryptolib.api.Masterkey
|
||||||
import org.cryptomator.cryptolib.api.UnsupportedVaultFormatException
|
|
||||||
import org.cryptomator.data.cloud.crypto.BackupFileIdSuffixGenerator.generate
|
import org.cryptomator.data.cloud.crypto.BackupFileIdSuffixGenerator.generate
|
||||||
import org.cryptomator.data.cloud.crypto.MasterkeyCryptoCloudProvider.UnlockTokenImpl
|
import org.cryptomator.data.cloud.crypto.MasterkeyCryptoCloudProvider.UnlockTokenImpl
|
||||||
import org.cryptomator.data.cloud.crypto.VaultConfig.VaultConfigBuilder
|
import org.cryptomator.data.cloud.crypto.VaultConfig.VaultConfigBuilder
|
||||||
@ -19,6 +18,7 @@ import org.cryptomator.domain.CloudType
|
|||||||
import org.cryptomator.domain.UnverifiedVaultConfig
|
import org.cryptomator.domain.UnverifiedVaultConfig
|
||||||
import org.cryptomator.domain.Vault
|
import org.cryptomator.domain.Vault
|
||||||
import org.cryptomator.domain.exception.BackendException
|
import org.cryptomator.domain.exception.BackendException
|
||||||
|
import org.cryptomator.domain.exception.vaultconfig.MissingVaultConfigFileException
|
||||||
import org.cryptomator.domain.repository.CloudContentRepository
|
import org.cryptomator.domain.repository.CloudContentRepository
|
||||||
import org.cryptomator.domain.usecases.ProgressAware
|
import org.cryptomator.domain.usecases.ProgressAware
|
||||||
import org.cryptomator.domain.usecases.cloud.DataSource
|
import org.cryptomator.domain.usecases.cloud.DataSource
|
||||||
@ -207,7 +207,7 @@ internal class MasterkeyCryptoCloudProviderTest {
|
|||||||
@DisplayName("unlockLegacyUsingNewVault(\"foo\")")
|
@DisplayName("unlockLegacyUsingNewVault(\"foo\")")
|
||||||
fun testUnlockLegacyVaultUsingVaultFormat8() {
|
fun testUnlockLegacyVaultUsingVaultFormat8() {
|
||||||
val unlockToken: UnlockToken = UnlockTokenImpl(vault, masterkeyV8.toByteArray(StandardCharsets.UTF_8))
|
val unlockToken: UnlockToken = UnlockTokenImpl(vault, masterkeyV8.toByteArray(StandardCharsets.UTF_8))
|
||||||
Assertions.assertThrows(UnsupportedVaultFormatException::class.java) { inTest.unlock(unlockToken, Optional.absent(), "foo", { false }) }
|
Assertions.assertThrows(MissingVaultConfigFileException::class.java) { inTest.unlock(unlockToken, Optional.absent(), "foo", { false }) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@DisplayName("changePassword(\"foo\")")
|
@DisplayName("changePassword(\"foo\")")
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
package org.cryptomator.domain.exception.vaultconfig;
|
||||||
|
|
||||||
|
import org.cryptomator.domain.exception.BackendException;
|
||||||
|
|
||||||
|
public class MissingVaultConfigFileException extends BackendException {
|
||||||
|
}
|
@ -29,6 +29,7 @@ platform :android do |options|
|
|||||||
deployToPlaystore(alpha:options[:alpha], beta:options[:beta])
|
deployToPlaystore(alpha:options[:alpha], beta:options[:beta])
|
||||||
deployToServer(alpha:options[:alpha], beta:options[:beta])
|
deployToServer(alpha:options[:alpha], beta:options[:beta])
|
||||||
deployToFDroid(alpha:options[:alpha], beta:options[:beta])
|
deployToFDroid(alpha:options[:alpha], beta:options[:beta])
|
||||||
|
testLite(alpha:options[:alpha], beta:options[:beta])
|
||||||
createGitHubDraftRelease(alpha:options[:alpha], beta:options[:beta])
|
createGitHubDraftRelease(alpha:options[:alpha], beta:options[:beta])
|
||||||
|
|
||||||
slack(
|
slack(
|
||||||
@ -220,6 +221,29 @@ platform :android do |options|
|
|||||||
FileUtils.cp(lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH], "release/Cryptomator-#{version}_fdroid_signed.apk")
|
FileUtils.cp(lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH], "release/Cryptomator-#{version}_fdroid_signed.apk")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
desc "Deploy new lite version"
|
||||||
|
lane :deployLite do |options|
|
||||||
|
gradle(task: "clean")
|
||||||
|
|
||||||
|
gradle(
|
||||||
|
task: "assemble",
|
||||||
|
build_type: "Release",
|
||||||
|
flavor: "lite",
|
||||||
|
print_command: false,
|
||||||
|
properties: {
|
||||||
|
"android.injected.signing.store.file" => ENV["SIGNING_KEYSTORE_PATH"],
|
||||||
|
"android.injected.signing.store.password" => ENV["SIGNING_KEYSTORE_PASSWORD"],
|
||||||
|
"android.injected.signing.key.alias" => ENV["SIGNING_KEY_ALIAS"],
|
||||||
|
"android.injected.signing.key.password" => ENV["SIGNING_KEY_PASSWORD"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
checkTrackingAddedInDependencyUsingIzzyScript(alpha:options[:alpha], beta:options[:beta], flavor: 'lite')
|
||||||
|
checkTrackingAddedInDependencyUsingExodus(alpha:options[:alpha], beta:options[:beta], flavor: 'lite')
|
||||||
|
|
||||||
|
FileUtils.cp(lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH], "release/Cryptomator-#{version}_lite_signed.apk")
|
||||||
|
end
|
||||||
|
|
||||||
desc "Check if tracking added in some dependency using Izzy's script"
|
desc "Check if tracking added in some dependency using Izzy's script"
|
||||||
lane :checkTrackingAddedInDependencyUsingIzzyScript do |options|
|
lane :checkTrackingAddedInDependencyUsingIzzyScript do |options|
|
||||||
flavor = options[:flavor]
|
flavor = options[:flavor]
|
||||||
@ -289,8 +313,9 @@ platform :android do |options|
|
|||||||
|
|
||||||
website_apk_sha256 = Digest::SHA256.hexdigest File.read "release/Cryptomator-#{version}_signed.apk"
|
website_apk_sha256 = Digest::SHA256.hexdigest File.read "release/Cryptomator-#{version}_signed.apk"
|
||||||
fdroid_apk_sha256 = Digest::SHA256.hexdigest File.read "release/Cryptomator-#{version}_fdroid_signed.apk"
|
fdroid_apk_sha256 = Digest::SHA256.hexdigest File.read "release/Cryptomator-#{version}_fdroid_signed.apk"
|
||||||
|
lite_sha256 = Digest::SHA256.hexdigest File.read "release/Cryptomator-#{version}_lite_signed.apk"
|
||||||
|
|
||||||
release_note = "## What's New\n\n" + File.read(release_note_path_en) + "\n\n---\n\nSHA256 Signature: `#{website_apk_sha256}`\nSHA256 Signature fdroid: `#{fdroid_apk_sha256}`\n"
|
release_note = "## What's New\n\n" + File.read(release_note_path_en) + "\n\n---\n\nSHA256 Signature: `#{website_apk_sha256}`\nSHA256 Signature fdroid: `#{fdroid_apk_sha256}`\nSHA256 Signature lite: `#{lite_sha256}`\n"
|
||||||
|
|
||||||
puts release_note
|
puts release_note
|
||||||
|
|
||||||
@ -303,7 +328,82 @@ platform :android do |options|
|
|||||||
commitish: target_branch,
|
commitish: target_branch,
|
||||||
is_draft: true,
|
is_draft: true,
|
||||||
is_prerelease: prerelease,
|
is_prerelease: prerelease,
|
||||||
upload_assets: ["fastlane/release/Cryptomator-#{version}_fdroid_signed.apk", "fastlane/release/Cryptomator-#{version}_signed.apk"]
|
upload_assets: ["fastlane/release/Cryptomator-#{version}_fdroid_signed.apk", "fastlane/release/Cryptomator-#{version}_lite_signed.apk", "fastlane/release/Cryptomator-#{version}_signed.apk"]
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
desc "Dry run - check tracking added for all flavors"
|
||||||
|
lane :dryRun do |options|
|
||||||
|
gradle(task: "clean")
|
||||||
|
|
||||||
|
gradle(
|
||||||
|
task: "assemble",
|
||||||
|
build_type: "Release",
|
||||||
|
flavor: "playstore",
|
||||||
|
print_command: false,
|
||||||
|
properties: {
|
||||||
|
"android.injected.signing.store.file" => ENV["SIGNING_KEYSTORE_PATH"],
|
||||||
|
"android.injected.signing.store.password" => ENV["SIGNING_KEYSTORE_PASSWORD"],
|
||||||
|
"android.injected.signing.key.alias" => ENV["SIGNING_KEY_ALIAS"],
|
||||||
|
"android.injected.signing.key.password" => ENV["SIGNING_KEY_PASSWORD"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
checkTrackingAddedInDependencyUsingIzzyScript(alpha:options[:alpha], beta:options[:beta], flavor: 'playstore')
|
||||||
|
checkTrackingAddedInDependencyUsingExodus(alpha:options[:alpha], beta:options[:beta], flavor: 'playstore')
|
||||||
|
|
||||||
|
gradle(task: "clean")
|
||||||
|
|
||||||
|
gradle(
|
||||||
|
task: "assemble",
|
||||||
|
build_type: "Release",
|
||||||
|
flavor: "apkstore",
|
||||||
|
print_command: false,
|
||||||
|
properties: {
|
||||||
|
"android.injected.signing.store.file" => ENV["SIGNING_KEYSTORE_PATH"],
|
||||||
|
"android.injected.signing.store.password" => ENV["SIGNING_KEYSTORE_PASSWORD"],
|
||||||
|
"android.injected.signing.key.alias" => ENV["SIGNING_KEY_ALIAS"],
|
||||||
|
"android.injected.signing.key.password" => ENV["SIGNING_KEY_PASSWORD"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
checkTrackingAddedInDependencyUsingIzzyScript(alpha:options[:alpha], beta:options[:beta], flavor: 'apkstore')
|
||||||
|
checkTrackingAddedInDependencyUsingExodus(alpha:options[:alpha], beta:options[:beta], flavor: 'apkstore')
|
||||||
|
|
||||||
|
gradle(task: "clean")
|
||||||
|
|
||||||
|
gradle(
|
||||||
|
task: "assemble",
|
||||||
|
build_type: "Release",
|
||||||
|
flavor: "fdroid",
|
||||||
|
print_command: false,
|
||||||
|
properties: {
|
||||||
|
"android.injected.signing.store.file" => ENV["SIGNING_KEYSTORE_PATH"],
|
||||||
|
"android.injected.signing.store.password" => ENV["SIGNING_KEYSTORE_PASSWORD"],
|
||||||
|
"android.injected.signing.key.alias" => ENV["SIGNING_KEY_ALIAS"],
|
||||||
|
"android.injected.signing.key.password" => ENV["SIGNING_KEY_PASSWORD"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
checkTrackingAddedInDependencyUsingIzzyScript(alpha:options[:alpha], beta:options[:beta], flavor: 'fdroid')
|
||||||
|
checkTrackingAddedInDependencyUsingExodus(alpha:options[:alpha], beta:options[:beta], flavor: 'fdroid')
|
||||||
|
|
||||||
|
gradle(task: "clean")
|
||||||
|
|
||||||
|
gradle(
|
||||||
|
task: "assemble",
|
||||||
|
build_type: "Release",
|
||||||
|
flavor: "lite",
|
||||||
|
print_command: false,
|
||||||
|
properties: {
|
||||||
|
"android.injected.signing.store.file" => ENV["SIGNING_KEYSTORE_PATH"],
|
||||||
|
"android.injected.signing.store.password" => ENV["SIGNING_KEYSTORE_PASSWORD"],
|
||||||
|
"android.injected.signing.key.alias" => ENV["SIGNING_KEY_ALIAS"],
|
||||||
|
"android.injected.signing.key.password" => ENV["SIGNING_KEY_PASSWORD"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
checkTrackingAddedInDependencyUsingIzzyScript(alpha:options[:alpha], beta:options[:beta], flavor: 'lite')
|
||||||
|
checkTrackingAddedInDependencyUsingExodus(alpha:options[:alpha], beta:options[:beta], flavor: 'lite')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,64 +1,104 @@
|
|||||||
fastlane documentation
|
fastlane documentation
|
||||||
================
|
----
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
|
|
||||||
Make sure you have the latest version of the Xcode command line tools installed:
|
Make sure you have the latest version of the Xcode command line tools installed:
|
||||||
|
|
||||||
```
|
```sh
|
||||||
xcode-select --install
|
xcode-select --install
|
||||||
```
|
```
|
||||||
|
|
||||||
Install _fastlane_ using
|
For _fastlane_ installation instructions, see [Installing _fastlane_](https://docs.fastlane.tools/#installing-fastlane)
|
||||||
```
|
|
||||||
[sudo] gem install fastlane -NV
|
|
||||||
```
|
|
||||||
or alternatively using `brew install fastlane`
|
|
||||||
|
|
||||||
# Available Actions
|
# Available Actions
|
||||||
|
|
||||||
## Android
|
## Android
|
||||||
|
|
||||||
### android test
|
### android test
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[bundle exec] fastlane android test
|
||||||
```
|
```
|
||||||
fastlane android test
|
|
||||||
```
|
|
||||||
Run all the tests
|
Run all the tests
|
||||||
|
|
||||||
### android deploy
|
### android deploy
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[bundle exec] fastlane android deploy
|
||||||
```
|
```
|
||||||
fastlane android deploy
|
|
||||||
```
|
|
||||||
Deploy new version to Google Play and APK Store options: beta:false (default)
|
Deploy new version to Google Play and APK Store options: beta:false (default)
|
||||||
|
|
||||||
### android deployToPlaystore
|
### android deployToPlaystore
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[bundle exec] fastlane android deployToPlaystore
|
||||||
```
|
```
|
||||||
fastlane android deployToPlaystore
|
|
||||||
```
|
|
||||||
Deploy new version to Play Store
|
Deploy new version to Play Store
|
||||||
|
|
||||||
### android deployToServer
|
### android deployToServer
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[bundle exec] fastlane android deployToServer
|
||||||
```
|
```
|
||||||
fastlane android deployToServer
|
|
||||||
```
|
|
||||||
Deploy new version to server
|
Deploy new version to server
|
||||||
|
|
||||||
### android deployToFDroid
|
### android deployToFDroid
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[bundle exec] fastlane android deployToFDroid
|
||||||
```
|
```
|
||||||
fastlane android deployToFDroid
|
|
||||||
```
|
|
||||||
Deploy new version to F-Droid
|
Deploy new version to F-Droid
|
||||||
|
|
||||||
|
### android deployLite
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[bundle exec] fastlane android deployLite
|
||||||
|
```
|
||||||
|
|
||||||
|
Deploy new lite version
|
||||||
|
|
||||||
### android checkTrackingAddedInDependencyUsingIzzyScript
|
### android checkTrackingAddedInDependencyUsingIzzyScript
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[bundle exec] fastlane android checkTrackingAddedInDependencyUsingIzzyScript
|
||||||
```
|
```
|
||||||
fastlane android checkTrackingAddedInDependencyUsingIzzyScript
|
|
||||||
```
|
|
||||||
Check if tracking added in some dependency using Izzy's script
|
Check if tracking added in some dependency using Izzy's script
|
||||||
|
|
||||||
### android checkTrackingAddedInDependencyUsingExodus
|
### android checkTrackingAddedInDependencyUsingExodus
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[bundle exec] fastlane android checkTrackingAddedInDependencyUsingExodus
|
||||||
```
|
```
|
||||||
fastlane android checkTrackingAddedInDependencyUsingExodus
|
|
||||||
```
|
|
||||||
Check if tracking added in some dependency using exodus
|
Check if tracking added in some dependency using exodus
|
||||||
|
|
||||||
### android createGitHubDraftRelease
|
### android createGitHubDraftRelease
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[bundle exec] fastlane android createGitHubDraftRelease
|
||||||
```
|
```
|
||||||
fastlane android createGitHubDraftRelease
|
|
||||||
```
|
|
||||||
Create GitHub draft release
|
Create GitHub draft release
|
||||||
|
|
||||||
|
### android dryRun
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[bundle exec] fastlane android dryRun
|
||||||
|
```
|
||||||
|
|
||||||
|
Dry run - check tracking added for all flavors
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run.
|
This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run.
|
||||||
More information about fastlane can be found on [fastlane.tools](https://fastlane.tools).
|
|
||||||
The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools).
|
More information about _fastlane_ can be found on [fastlane.tools](https://fastlane.tools).
|
||||||
|
|
||||||
|
The documentation of _fastlane_ can be found on [docs.fastlane.tools](https://docs.fastlane.tools).
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
fastlane/izzyscript/result_lite.json
Normal file
1
fastlane/izzyscript/result_lite.json
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,2 +1,3 @@
|
|||||||
- Zeige Dialog und Benachrichtigung an, wenn die Berechtigung "Dateien" widerrufen wird, erforderlich für den automatischen Upload
|
- Update auf die neueste Android-Zielplatform-Version
|
||||||
- Das Abmelden von einer Cloud löscht nun auch die Anmeldeinformationen einer aktiven Verbindung zu ihr
|
- Löschen von Ordnern ohne Verzeichnisdatei im lokalen Cloud-Speicher behoben
|
||||||
|
- Dateiexport wenn keine App den ausgewählten Dateityp verarbeiten kann behoben
|
@ -1,2 +1,3 @@
|
|||||||
- Show information when "Storage" permission revoked, required for auto upload
|
- Updated to the latest Android target version
|
||||||
- Logging out of a cloud now also clears the credentials of an active connection to it
|
- Fixed deletion of folders without a dir-file in local storage cloud
|
||||||
|
- Fixed file export when no app can process the selected file type
|
@ -1,4 +1,5 @@
|
|||||||
<ul>
|
<ul>
|
||||||
<li>Show information when "Storage" permission revoked, required for auto upload</li>
|
<li>Updated to the latest Android target version</li>
|
||||||
<li>Logging out of a cloud now also clears the credentials of an active connection to it</li>
|
<li>Fixed deletion of folders without a dir-file in local storage cloud</li>
|
||||||
|
<li>Fixed file export when no app can process the selected file type</li>
|
||||||
</ul>
|
</ul>
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,5 +1,5 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
BIN
lib/google-http-client-1.41.8-sources.jar
Normal file
BIN
lib/google-http-client-1.41.8-sources.jar
Normal file
Binary file not shown.
Binary file not shown.
BIN
lib/google-http-client-android-1.41.8-sources.jar
Normal file
BIN
lib/google-http-client-android-1.41.8-sources.jar
Normal file
Binary file not shown.
Binary file not shown.
@ -1 +1 @@
|
|||||||
Subproject commit dc4d0897f7917f026376d35f9a6eaf6edbc7115d
|
Subproject commit 4834dde955127b1760be9bb527e9d45613b1f036
|
@ -50,8 +50,6 @@ android {
|
|||||||
buildConfigField "String", "PCLOUD_CLIENT_ID", "\"" + getApiKey('PCLOUD_CLIENT_ID') + "\""
|
buildConfigField "String", "PCLOUD_CLIENT_ID", "\"" + getApiKey('PCLOUD_CLIENT_ID') + "\""
|
||||||
|
|
||||||
manifestPlaceholders = [DROPBOX_API_KEY: getApiKey('DROPBOX_API_KEY'), ONEDRIVE_API_KEY_DECODED: getOnedriveApiKey()]
|
manifestPlaceholders = [DROPBOX_API_KEY: getApiKey('DROPBOX_API_KEY'), ONEDRIVE_API_KEY_DECODED: getOnedriveApiKey()]
|
||||||
|
|
||||||
resValue "string", "app_id", androidApplicationId
|
|
||||||
}
|
}
|
||||||
|
|
||||||
debug {
|
debug {
|
||||||
@ -69,8 +67,6 @@ android {
|
|||||||
|
|
||||||
applicationIdSuffix ".debug"
|
applicationIdSuffix ".debug"
|
||||||
versionNameSuffix '-DEBUG'
|
versionNameSuffix '-DEBUG'
|
||||||
|
|
||||||
resValue "string", "app_id", androidApplicationId + applicationIdSuffix
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,19 +84,30 @@ android {
|
|||||||
fdroid {
|
fdroid {
|
||||||
dimension "version"
|
dimension "version"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lite {
|
||||||
|
dimension "version"
|
||||||
|
|
||||||
|
applicationIdSuffix ".lite"
|
||||||
|
resValue "string", "app_id", androidApplicationId + applicationIdSuffix
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
playstore {
|
playstore {
|
||||||
java.srcDirs = ['src/main/java', 'src/main/java/', 'src/notFoss/java', 'src/notFoss/java/']
|
java.srcDirs = ['src/main/java', 'src/apiKey/java/', 'src/apkStorePlaystore/java/']
|
||||||
}
|
}
|
||||||
|
|
||||||
apkstore {
|
apkstore {
|
||||||
java.srcDirs = ['src/main/java', 'src/main/java/', 'src/notFoss/java', 'src/notFoss/java/']
|
java.srcDirs = ['src/main/java/', 'src/apiKey/java/', 'src/apkStorePlaystore/java/']
|
||||||
}
|
}
|
||||||
|
|
||||||
fdroid {
|
fdroid {
|
||||||
java.srcDirs = ['src/main/java', 'src/main/java/', 'src/foss/java', 'src/foss/java/']
|
java.srcDirs = ['src/main/java/', 'src/apiKey/java/', 'src/fdroid/java/', 'src/fdroidAndLite/java/']
|
||||||
|
}
|
||||||
|
|
||||||
|
lite {
|
||||||
|
java.srcDirs = ['src/main/java/', 'src/lite/java/', 'src/fdroidAndLite/java/']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
packagingOptions {
|
packagingOptions {
|
||||||
@ -142,14 +149,22 @@ dependencies {
|
|||||||
implementation dependencies.androidxCore
|
implementation dependencies.androidxCore
|
||||||
implementation dependencies.androidxFragment
|
implementation dependencies.androidxFragment
|
||||||
implementation dependencies.androidxViewpager
|
implementation dependencies.androidxViewpager
|
||||||
|
implementation dependencies.androidxSplashscreen
|
||||||
implementation dependencies.androidxSwiperefresh
|
implementation dependencies.androidxSwiperefresh
|
||||||
implementation dependencies.androidxPreference
|
implementation dependencies.androidxPreference
|
||||||
implementation dependencies.androidxBiometric
|
implementation dependencies.androidxBiometric
|
||||||
|
|
||||||
// cloud
|
// cloud
|
||||||
implementation dependencies.dropbox
|
playstoreImplementation dependencies.dropbox
|
||||||
implementation dependencies.msgraph
|
apkstoreImplementation dependencies.dropbox
|
||||||
implementation dependencies.msgraphAuth
|
fdroidImplementation dependencies.dropbox
|
||||||
|
|
||||||
|
playstoreImplementation dependencies.msgraphAuth
|
||||||
|
apkstoreImplementation dependencies.msgraphAuth
|
||||||
|
fdroidImplementation dependencies.msgraphAuth
|
||||||
|
playstoreImplementation dependencies.msgraph
|
||||||
|
apkstoreImplementation dependencies.msgraph
|
||||||
|
fdroidImplementation dependencies.msgraph
|
||||||
|
|
||||||
playstoreImplementation(dependencies.googleApiServicesDrive) {
|
playstoreImplementation(dependencies.googleApiServicesDrive) {
|
||||||
exclude module: 'guava-jdk5'
|
exclude module: 'guava-jdk5'
|
||||||
|
@ -1,422 +0,0 @@
|
|||||||
package org.cryptomator.presentation;
|
|
||||||
|
|
||||||
import androidx.test.rule.ActivityTestRule;
|
|
||||||
|
|
||||||
import org.cryptomator.data.cloud.local.file.RootLocalFolder;
|
|
||||||
import org.cryptomator.domain.Cloud;
|
|
||||||
import org.cryptomator.domain.CloudFile;
|
|
||||||
import org.cryptomator.domain.CloudFolder;
|
|
||||||
import org.cryptomator.domain.CloudNode;
|
|
||||||
import org.cryptomator.domain.CloudType;
|
|
||||||
import org.cryptomator.domain.LocalStorageCloud;
|
|
||||||
import org.cryptomator.domain.exception.BackendException;
|
|
||||||
import org.cryptomator.domain.exception.CloudNodeAlreadyExistsException;
|
|
||||||
import org.cryptomator.domain.repository.CloudContentRepository;
|
|
||||||
import org.cryptomator.domain.usecases.ProgressAware;
|
|
||||||
import org.cryptomator.domain.usecases.cloud.ByteArrayDataSource;
|
|
||||||
import org.cryptomator.presentation.di.component.ApplicationComponent;
|
|
||||||
import org.cryptomator.presentation.testCloud.CryptoTestCloud;
|
|
||||||
import org.cryptomator.presentation.testCloud.DropboxTestCloud;
|
|
||||||
import org.cryptomator.presentation.testCloud.GoogledriveTestCloud;
|
|
||||||
import org.cryptomator.presentation.testCloud.LocalStorageTestCloud;
|
|
||||||
import org.cryptomator.presentation.testCloud.LocalTestCloud;
|
|
||||||
import org.cryptomator.presentation.testCloud.OnedriveTestCloud;
|
|
||||||
import org.cryptomator.presentation.testCloud.TestCloud;
|
|
||||||
import org.cryptomator.presentation.testCloud.WebdavTestCloud;
|
|
||||||
import org.cryptomator.presentation.ui.activity.SplashActivity;
|
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Rule;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.rules.ExpectedException;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.Parameterized;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import static androidx.test.InstrumentationRegistry.getTargetContext;
|
|
||||||
import static org.cryptomator.presentation.CloudNodeMatchers.aFile;
|
|
||||||
import static org.cryptomator.presentation.CloudNodeMatchers.folder;
|
|
||||||
import static org.hamcrest.CoreMatchers.is;
|
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
|
||||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
|
||||||
import static org.hamcrest.collection.IsEmptyCollection.emptyCollectionOf;
|
|
||||||
|
|
||||||
@RunWith(Parameterized.class)
|
|
||||||
public class CloudContentRepositoryBlackboxTest {
|
|
||||||
|
|
||||||
private static final byte[] DIGITS_ONE_TO_TEN_AS_BYTES = new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
|
|
||||||
private static final byte[] DIGITS_SEVEN_TO_ONE_AS_BYTES = new byte[] {7, 6, 5, 4, 3, 2, 1};
|
|
||||||
|
|
||||||
private static Cloud cloud;
|
|
||||||
private static TestCloud inTestCloud;
|
|
||||||
private static boolean setupCloudCompleted = false;
|
|
||||||
@Rule
|
|
||||||
public final ActivityTestRule<SplashActivity> activityTestRule = new ActivityTestRule<>(SplashActivity.class);
|
|
||||||
@Rule
|
|
||||||
public ExpectedException thrown = ExpectedException.none();
|
|
||||||
private CloudContentRepository inTest;
|
|
||||||
private CloudFolder root;
|
|
||||||
|
|
||||||
public CloudContentRepositoryBlackboxTest(TestCloud testCloud) {
|
|
||||||
if (inTestCloud != null && inTestCloud != testCloud) {
|
|
||||||
setupCloudCompleted = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
inTestCloud = testCloud;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Parameterized.Parameters(name = "{0}")
|
|
||||||
public static TestCloud[] data() {
|
|
||||||
return new TestCloud[] { //
|
|
||||||
new LocalStorageTestCloud(), //
|
|
||||||
new LocalTestCloud(), //
|
|
||||||
new WebdavTestCloud(getTargetContext()), //
|
|
||||||
new DropboxTestCloud(getTargetContext()), //
|
|
||||||
new GoogledriveTestCloud(), //
|
|
||||||
new OnedriveTestCloud(getTargetContext()), //
|
|
||||||
new CryptoTestCloud()};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setup() throws BackendException {
|
|
||||||
|
|
||||||
ApplicationComponent appComponent = ((CryptomatorApp) activityTestRule //
|
|
||||||
.getActivity() //
|
|
||||||
.getApplication()) //
|
|
||||||
.getComponent();
|
|
||||||
|
|
||||||
if (!setupCloudCompleted) {
|
|
||||||
if (inTestCloud instanceof CryptoTestCloud) {
|
|
||||||
// FIXME 343 @julian just for testcase local cloud
|
|
||||||
Cloud testCloud = appComponent.cloudRepository().clouds(CloudType.LOCAL).get(0);
|
|
||||||
CloudFolder rootFolder = new RootLocalFolder((LocalStorageCloud) testCloud);
|
|
||||||
cloud = ((CryptoTestCloud) inTestCloud).getInstance(appComponent, testCloud, rootFolder);
|
|
||||||
} else {
|
|
||||||
cloud = inTestCloud.getInstance(appComponent);
|
|
||||||
}
|
|
||||||
|
|
||||||
setupCloudCompleted = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
inTest = appComponent.cloudContentRepository();
|
|
||||||
root = inTest.create(inTest.resolve(cloud, UUID.randomUUID().toString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testListEmptyDirectory() throws BackendException {
|
|
||||||
assertThat(listingOf(root), is(emptyCollectionOf(CloudNode.class)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testListDirectory() throws BackendException {
|
|
||||||
createParentsAndWrite("a", DIGITS_SEVEN_TO_ONE_AS_BYTES);
|
|
||||||
createParentsAndWrite("b.dat", DIGITS_ONE_TO_TEN_AS_BYTES);
|
|
||||||
createParentsAndWrite("empty.txt", new byte[0]);
|
|
||||||
inTest.create(inTest.folder(root, "b"));
|
|
||||||
inTest.create(inTest.folder(root, "c"));
|
|
||||||
|
|
||||||
assertThat(listingOf(root), containsInAnyOrder( //
|
|
||||||
aFile().withName("a").withSize(DIGITS_SEVEN_TO_ONE_AS_BYTES.length), //
|
|
||||||
aFile().withName("b.dat").withSize(DIGITS_ONE_TO_TEN_AS_BYTES.length), //
|
|
||||||
aFile().withName("empty.txt").withSize(0L), //
|
|
||||||
folder("b"), //
|
|
||||||
folder("c")));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreateDirectory() throws BackendException {
|
|
||||||
CloudFolder created = inTest.folder(root, "created");
|
|
||||||
created = inTest.create(created);
|
|
||||||
|
|
||||||
assertThat(listingOf(created), is(emptyCollectionOf(CloudNode.class)));
|
|
||||||
assertThat(listingOf(root), containsInAnyOrder(folder("created")));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDeleteDirectory() throws BackendException {
|
|
||||||
inTest.create(inTest.folder(root, "created"));
|
|
||||||
inTest.delete(inTest.folder(root, "created"));
|
|
||||||
|
|
||||||
assertThat(inTest.exists(inTest.folder(root, "created")), is(false));
|
|
||||||
assertThat(listingOf(root), is(emptyCollectionOf(CloudNode.class)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
public void testUploadFile() throws BackendException {
|
|
||||||
Date start = new Date();
|
|
||||||
CloudFile file = createParentsAndWrite("file", DIGITS_ONE_TO_TEN_AS_BYTES);
|
|
||||||
|
|
||||||
assertThat(file, is(aFile() //
|
|
||||||
.withName("file") //
|
|
||||||
.withSize(DIGITS_ONE_TO_TEN_AS_BYTES.length) //
|
|
||||||
.withModifiedIn(start, new Date())));
|
|
||||||
assertThat(listingOf(root), //
|
|
||||||
containsInAnyOrder(aFile() //
|
|
||||||
.withName("file") //
|
|
||||||
.withSize(DIGITS_ONE_TO_TEN_AS_BYTES.length)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
public void testReplaceFile() throws BackendException {
|
|
||||||
createParentsAndWrite("file", DIGITS_ONE_TO_TEN_AS_BYTES);
|
|
||||||
Date start = new Date();
|
|
||||||
CloudFile file = createParentsAndWriteOrReplace("file", DIGITS_SEVEN_TO_ONE_AS_BYTES);
|
|
||||||
|
|
||||||
assertThat(file, is(aFile() //
|
|
||||||
.withName("file") //
|
|
||||||
.withSize(DIGITS_SEVEN_TO_ONE_AS_BYTES.length) //
|
|
||||||
.withModifiedIn(start, new Date())));
|
|
||||||
assertThat(listingOf(root), //
|
|
||||||
containsInAnyOrder(aFile() //
|
|
||||||
.withName("file") //
|
|
||||||
.withSize(DIGITS_SEVEN_TO_ONE_AS_BYTES.length)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testUploadExistingFileWithoutReplaceFlag() throws BackendException {
|
|
||||||
createParentsAndWrite("file", DIGITS_ONE_TO_TEN_AS_BYTES);
|
|
||||||
|
|
||||||
thrown.expect(CloudNodeAlreadyExistsException.class);
|
|
||||||
thrown.expectMessage("CloudNode already exists and replace is false");
|
|
||||||
|
|
||||||
createParentsAndWrite("file", DIGITS_ONE_TO_TEN_AS_BYTES);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDownloadFile() throws BackendException {
|
|
||||||
createParentsAndWrite("file", DIGITS_ONE_TO_TEN_AS_BYTES);
|
|
||||||
|
|
||||||
assertThat(read(inTest.file(root, "file")), is(DIGITS_ONE_TO_TEN_AS_BYTES));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDeleteFile() throws BackendException {
|
|
||||||
createParentsAndWrite("file", DIGITS_ONE_TO_TEN_AS_BYTES);
|
|
||||||
inTest.delete(inTest.file(root, "file"));
|
|
||||||
|
|
||||||
assertThat(inTest.exists(inTest.file(root, "file")), is(false));
|
|
||||||
assertThat(listingOf(root), is(emptyCollectionOf(CloudNode.class)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
public void testRenameDirectory() throws BackendException {
|
|
||||||
CloudFile file = createParentsAndWrite("directory/file", DIGITS_ONE_TO_TEN_AS_BYTES);
|
|
||||||
CloudFolder directory = file.getParent();
|
|
||||||
CloudFolder target = inTest.folder(root, "newName");
|
|
||||||
|
|
||||||
target = inTest.move(directory, target);
|
|
||||||
|
|
||||||
assertThat(listingOf(target), containsInAnyOrder(aFile() //
|
|
||||||
.withName("file") //
|
|
||||||
.withSize(DIGITS_ONE_TO_TEN_AS_BYTES.length)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testRenameDirectoryToExistingDirectory() throws BackendException {
|
|
||||||
CloudFolder directory = inTest.folder(root, "directory");
|
|
||||||
directory = inTest.create(directory);
|
|
||||||
CloudFolder target = inTest.folder(root, "newName");
|
|
||||||
target = inTest.create(target);
|
|
||||||
|
|
||||||
thrown.expect(CloudNodeAlreadyExistsException.class);
|
|
||||||
thrown.expectMessage("newName");
|
|
||||||
|
|
||||||
inTest.move(directory, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
public void testRenameDirectoryToExistingFile() throws BackendException {
|
|
||||||
CloudFolder directory = inTest.folder(root, "directory");
|
|
||||||
directory = inTest.create(directory);
|
|
||||||
CloudFolder target = inTest.folder(root, "newName");
|
|
||||||
createParentsAndWrite("newName", new byte[0]);
|
|
||||||
|
|
||||||
thrown.expect(CloudNodeAlreadyExistsException.class);
|
|
||||||
thrown.expectMessage("newName");
|
|
||||||
|
|
||||||
inTest.move(directory, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
public void testMoveDirectory() throws BackendException {
|
|
||||||
CloudFile file = createParentsAndWrite("directory/file", DIGITS_ONE_TO_TEN_AS_BYTES);
|
|
||||||
CloudFolder directory = file.getParent();
|
|
||||||
CloudFolder newParent = inTest.create(inTest.folder(root, "newParent"));
|
|
||||||
CloudFolder target = inTest.folder(newParent, "directory");
|
|
||||||
|
|
||||||
target = inTest.move(directory, target);
|
|
||||||
|
|
||||||
assertThat(listingOf(target), containsInAnyOrder(aFile() //
|
|
||||||
.withName("file") //
|
|
||||||
.withSize(DIGITS_ONE_TO_TEN_AS_BYTES.length)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
public void testMoveDirectoryToExistingDirectory() throws BackendException {
|
|
||||||
CloudFile file = createParentsAndWrite("directory/file", DIGITS_ONE_TO_TEN_AS_BYTES);
|
|
||||||
CloudFolder directory = file.getParent();
|
|
||||||
CloudFolder newParent = inTest.create(inTest.folder(root, "newParent"));
|
|
||||||
CloudFolder target = inTest.folder(newParent, "directory");
|
|
||||||
target = inTest.create(target);
|
|
||||||
|
|
||||||
thrown.expect(CloudNodeAlreadyExistsException.class);
|
|
||||||
thrown.expectMessage("directory");
|
|
||||||
|
|
||||||
inTest.move(directory, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
public void testMoveDirectoryToExistingFile() throws BackendException {
|
|
||||||
CloudFile file = createParentsAndWrite("directory/file", DIGITS_ONE_TO_TEN_AS_BYTES);
|
|
||||||
CloudFolder directory = file.getParent();
|
|
||||||
CloudFolder newParent = inTest.create(inTest.folder(root, "newParent"));
|
|
||||||
CloudFolder target = inTest.folder(newParent, "directory");
|
|
||||||
createParentsAndWrite("newParent/directory", new byte[0]);
|
|
||||||
|
|
||||||
thrown.expect(CloudNodeAlreadyExistsException.class);
|
|
||||||
thrown.expectMessage("directory");
|
|
||||||
|
|
||||||
inTest.move(directory, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
public void testMoveAndRenameDirectory() throws BackendException {
|
|
||||||
CloudFile file = createParentsAndWrite("directory/file", DIGITS_ONE_TO_TEN_AS_BYTES);
|
|
||||||
CloudFolder directory = file.getParent();
|
|
||||||
CloudFolder newParent = inTest.create(inTest.folder(root, "newParent"));
|
|
||||||
CloudFolder target = inTest.folder(newParent, "newName");
|
|
||||||
|
|
||||||
target = inTest.move(directory, target);
|
|
||||||
|
|
||||||
assertThat(listingOf(target), containsInAnyOrder(aFile() //
|
|
||||||
.withName("file") //
|
|
||||||
.withSize(DIGITS_ONE_TO_TEN_AS_BYTES.length)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
public void testMoveAndRenameDirectoryToExistingDirectory() throws BackendException {
|
|
||||||
CloudFile file = createParentsAndWrite("directory/file", DIGITS_ONE_TO_TEN_AS_BYTES);
|
|
||||||
CloudFolder directory = file.getParent();
|
|
||||||
CloudFolder newParent = inTest.create(inTest.folder(root, "newParent"));
|
|
||||||
CloudFolder target = inTest.folder(newParent, "newName");
|
|
||||||
target = inTest.create(target);
|
|
||||||
|
|
||||||
thrown.expect(CloudNodeAlreadyExistsException.class);
|
|
||||||
thrown.expectMessage("newName");
|
|
||||||
|
|
||||||
inTest.move(directory, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
public void testMoveAndRenameDirectoryToExistingFile() throws BackendException {
|
|
||||||
CloudFile file = createParentsAndWrite("directory/file", DIGITS_ONE_TO_TEN_AS_BYTES);
|
|
||||||
CloudFolder directory = file.getParent();
|
|
||||||
CloudFolder newParent = inTest.create(inTest.folder(root, "newParent"));
|
|
||||||
CloudFolder target = inTest.folder(newParent, "newName");
|
|
||||||
createParentsAndWrite("newParent/newName", new byte[0]);
|
|
||||||
|
|
||||||
thrown.expect(CloudNodeAlreadyExistsException.class);
|
|
||||||
thrown.expectMessage("newName");
|
|
||||||
|
|
||||||
inTest.move(directory, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
public void testRenameFile() throws BackendException {
|
|
||||||
CloudFile file = createParentsAndWrite("directory/file", DIGITS_ONE_TO_TEN_AS_BYTES);
|
|
||||||
CloudFile target = inTest.file(file.getParent(), "newName");
|
|
||||||
|
|
||||||
target = inTest.move(file, target);
|
|
||||||
|
|
||||||
assertThat(listingOf(file.getParent()), containsInAnyOrder(aFile() //
|
|
||||||
.withName("newName") //
|
|
||||||
.withSize(DIGITS_ONE_TO_TEN_AS_BYTES.length)));
|
|
||||||
assertThat(read(target), is(DIGITS_ONE_TO_TEN_AS_BYTES));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
public void testMoveFile() throws BackendException {
|
|
||||||
CloudFile file = createParentsAndWrite("directory/file", DIGITS_ONE_TO_TEN_AS_BYTES);
|
|
||||||
CloudFolder oldParent = file.getParent();
|
|
||||||
CloudFolder newParent = inTest.create(inTest.folder(root, "newParent"));
|
|
||||||
CloudFile target = inTest.file(newParent, "file");
|
|
||||||
|
|
||||||
target = inTest.move(file, target);
|
|
||||||
|
|
||||||
assertThat(listingOf(oldParent), is(emptyCollectionOf(CloudNode.class)));
|
|
||||||
assertThat(listingOf(newParent), containsInAnyOrder(aFile() //
|
|
||||||
.withName("file") //
|
|
||||||
.withSize(DIGITS_ONE_TO_TEN_AS_BYTES.length)));
|
|
||||||
assertThat(read(target), is(DIGITS_ONE_TO_TEN_AS_BYTES));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
public void testMoveAndRenameFile() throws BackendException {
|
|
||||||
CloudFile file = createParentsAndWrite("directory/file", DIGITS_ONE_TO_TEN_AS_BYTES);
|
|
||||||
CloudFolder oldParent = file.getParent();
|
|
||||||
CloudFolder newParent = inTest.create(inTest.folder(root, "newParent"));
|
|
||||||
CloudFile target = inTest.file(newParent, "newName");
|
|
||||||
|
|
||||||
target = inTest.move(file, target);
|
|
||||||
|
|
||||||
assertThat(listingOf(oldParent), is(emptyCollectionOf(CloudNode.class)));
|
|
||||||
assertThat(listingOf(newParent), containsInAnyOrder(aFile() //
|
|
||||||
.withName("newName") //
|
|
||||||
.withSize(DIGITS_ONE_TO_TEN_AS_BYTES.length)));
|
|
||||||
assertThat(read(target), is(DIGITS_ONE_TO_TEN_AS_BYTES));
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
public void teardown() throws BackendException {
|
|
||||||
if (inTest != null && root != null) {
|
|
||||||
inTest.delete(root);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<CloudNode> listingOf(CloudFolder testRoot) throws BackendException {
|
|
||||||
return inTest.list(testRoot);
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] read(CloudFile file) throws BackendException {
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
inTest.read(file, null, out, ProgressAware.NO_OP_PROGRESS_AWARE_DOWNLOAD);
|
|
||||||
return out.toByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
private CloudFile createParentsAndWrite(String path, byte[] data) throws BackendException {
|
|
||||||
return createParentsAndWriteOrReplaceImpl(path, data, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private CloudFile createParentsAndWriteOrReplace(String path, byte[] data) throws BackendException {
|
|
||||||
return createParentsAndWriteOrReplaceImpl(path, data, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private CloudFile createParentsAndWriteOrReplaceImpl(String path, byte[] data, boolean repalce) throws BackendException {
|
|
||||||
path = root.getName() + "/" + path;
|
|
||||||
String pathToParent = path.substring(0, path.lastIndexOf('/') + 1);
|
|
||||||
String name = path.substring(path.lastIndexOf('/') + 1);
|
|
||||||
CloudFolder parent = inTest.resolve(cloud, pathToParent);
|
|
||||||
if (!inTest.exists(parent)) {
|
|
||||||
parent = inTest.create(parent);
|
|
||||||
}
|
|
||||||
CloudFile file = inTest.file(parent, name, new Long(data.length));
|
|
||||||
return inTest.write(file, ByteArrayDataSource.from(data), ProgressAware.NO_OP_PROGRESS_AWARE_UPLOAD, repalce, data.length);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,126 +0,0 @@
|
|||||||
package org.cryptomator.presentation;
|
|
||||||
|
|
||||||
import org.cryptomator.domain.CloudFile;
|
|
||||||
import org.cryptomator.domain.CloudFolder;
|
|
||||||
import org.cryptomator.domain.CloudNode;
|
|
||||||
import com.google.common.base.Optional;
|
|
||||||
import org.hamcrest.Description;
|
|
||||||
import org.hamcrest.Matcher;
|
|
||||||
import org.hamcrest.TypeSafeDiagnosingMatcher;
|
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
class CloudNodeMatchers {
|
|
||||||
|
|
||||||
public static Matcher<CloudNode> aFile(final String name) {
|
|
||||||
return (Matcher) new TypeSafeDiagnosingMatcher<CloudFile>() {
|
|
||||||
@Override
|
|
||||||
protected boolean matchesSafely(CloudFile file, Description description) {
|
|
||||||
if (name.equals(file.getName())) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
description.appendText("aFile with name '").appendText(file.getName()).appendText("'");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void describeTo(Description description) {
|
|
||||||
description.appendText("aFile with name '").appendText(name).appendText("'");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static FileMatcher aFile() {
|
|
||||||
return new FileMatcher();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Matcher<CloudNode> folder(final String name) {
|
|
||||||
return (Matcher) new TypeSafeDiagnosingMatcher<CloudFolder>() {
|
|
||||||
@Override
|
|
||||||
protected boolean matchesSafely(CloudFolder file, org.hamcrest.Description description) {
|
|
||||||
if (name.equals(file.getName())) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
description.appendText("folder with name '").appendText(file.getName()).appendText("'");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void describeTo(org.hamcrest.Description description) {
|
|
||||||
description.appendText("folder with name '").appendText(name).appendText("'");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class FileMatcher extends TypeSafeDiagnosingMatcher<CloudNode> {
|
|
||||||
|
|
||||||
private String nameToCheck;
|
|
||||||
private Optional<Long> sizeToCheck;
|
|
||||||
|
|
||||||
private Date minModifiedToCheck;
|
|
||||||
private Date maxModifiedToCheck;
|
|
||||||
|
|
||||||
private FileMatcher() {
|
|
||||||
super(CloudFile.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public FileMatcher withName(String name) {
|
|
||||||
this.nameToCheck = name;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FileMatcher withSize(int size) {
|
|
||||||
return withSize(Long.valueOf(size));
|
|
||||||
}
|
|
||||||
|
|
||||||
public FileMatcher withSize(Long size) {
|
|
||||||
this.sizeToCheck = Optional.ofNullable(size);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FileMatcher withModifiedIn(Date minModified, Date maxModified) {
|
|
||||||
this.minModifiedToCheck = minModified;
|
|
||||||
this.maxModifiedToCheck = maxModified;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void describeTo(Description description) {
|
|
||||||
description.appendText("a file");
|
|
||||||
if (nameToCheck != null) {
|
|
||||||
description.appendText(" with name ").appendText(nameToCheck);
|
|
||||||
}
|
|
||||||
if (sizeToCheck != null) {
|
|
||||||
description.appendText(" with size ").appendValue(sizeToCheck);
|
|
||||||
}
|
|
||||||
if (minModifiedToCheck != null) {
|
|
||||||
description.appendText(" with modified in [").appendValue(minModifiedToCheck).appendText(",").appendValue(maxModifiedToCheck).appendText("]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean matchesSafely(CloudNode cloudNode, Description description) {
|
|
||||||
CloudFile cloudFile = (CloudFile) cloudNode;
|
|
||||||
boolean match = true;
|
|
||||||
description.appendText("a file");
|
|
||||||
if (nameToCheck != null && !nameToCheck.equals(cloudFile.getName())) {
|
|
||||||
description.appendText(" with name ").appendText(cloudFile.getName());
|
|
||||||
match = false;
|
|
||||||
}
|
|
||||||
if (sizeToCheck != null && !sizeToCheck.equals(cloudFile.getSize())) {
|
|
||||||
description.appendText(" with size ").appendValue(cloudFile.getSize());
|
|
||||||
match = false;
|
|
||||||
}
|
|
||||||
if (minModifiedToCheck != null && dateInRange(minModifiedToCheck, maxModifiedToCheck, cloudFile.getModified())) {
|
|
||||||
description.appendText(" with modified ").appendValue(cloudFile.getModified());
|
|
||||||
}
|
|
||||||
return match;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean dateInRange(Date min, Date max, Optional<Date> modified) {
|
|
||||||
return modified.isPresent() && !modified.get().before(min) && !modified.get().after(max);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,71 +0,0 @@
|
|||||||
package org.cryptomator.presentation.testCloud;
|
|
||||||
|
|
||||||
import org.cryptomator.domain.Cloud;
|
|
||||||
import org.cryptomator.domain.CloudFolder;
|
|
||||||
import org.cryptomator.domain.Vault;
|
|
||||||
import org.cryptomator.domain.exception.BackendException;
|
|
||||||
import org.cryptomator.domain.exception.FatalBackendException;
|
|
||||||
import org.cryptomator.presentation.di.component.ApplicationComponent;
|
|
||||||
|
|
||||||
import static org.cryptomator.domain.Vault.aVault;
|
|
||||||
|
|
||||||
public class CryptoTestCloud extends TestCloud {
|
|
||||||
|
|
||||||
private final static String VAULT_PASSWORD = "password";
|
|
||||||
private final static String VAULT_NAME = "testVault";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Cloud getInstance(ApplicationComponent appComponent) {
|
|
||||||
throw new IllegalStateException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Cloud getInstance(ApplicationComponent appComponent, Cloud testCloud, CloudFolder rootFolder) {
|
|
||||||
try {
|
|
||||||
CloudFolder vaultFolder = appComponent //
|
|
||||||
.cloudContentRepository() //
|
|
||||||
.folder(rootFolder, VAULT_NAME);
|
|
||||||
|
|
||||||
Vault vault = aVault() //
|
|
||||||
.thatIsNew() //
|
|
||||||
.withCloud(testCloud) //
|
|
||||||
.withNamePathAndCloudFrom(vaultFolder) //
|
|
||||||
.build();
|
|
||||||
|
|
||||||
cleanup(appComponent, vault, vaultFolder);
|
|
||||||
|
|
||||||
vaultFolder = appComponent.cloudContentRepository().create(vaultFolder);
|
|
||||||
appComponent.cloudRepository().create(vaultFolder, VAULT_PASSWORD);
|
|
||||||
vault = appComponent.vaultRepository().store(vault);
|
|
||||||
|
|
||||||
return appComponent.cloudRepository().unlock(vault, VAULT_PASSWORD, () -> false);
|
|
||||||
} catch (BackendException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void cleanup(ApplicationComponent appComponent, Vault vault, CloudFolder vaultFolder) {
|
|
||||||
try {
|
|
||||||
appComponent.cloudContentRepository().delete(vaultFolder);
|
|
||||||
} catch (BackendException | FatalBackendException e) {
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
appComponent.vaultRepository().vaults().forEach(vaultInRepo -> {
|
|
||||||
if (vaultInRepo.getName().equals(vault.getName()) //
|
|
||||||
&& vaultInRepo.getPath().equals(vault.getPath())) {
|
|
||||||
try {
|
|
||||||
appComponent.vaultRepository().delete(vaultInRepo);
|
|
||||||
} catch (BackendException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (FatalBackendException | BackendException e) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "CryptoTestCloud";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
package org.cryptomator.presentation.testCloud;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import org.cryptomator.domain.Cloud;
|
|
||||||
import org.cryptomator.domain.WebDavCloud;
|
|
||||||
import org.cryptomator.presentation.di.component.ApplicationComponent;
|
|
||||||
import org.cryptomator.util.crypto.CredentialCryptor;
|
|
||||||
|
|
||||||
public class DropboxTestCloud extends TestCloud {
|
|
||||||
|
|
||||||
private final Context context;
|
|
||||||
|
|
||||||
public DropboxTestCloud(Context context) {
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Cloud getInstance(ApplicationComponent appComponent) {
|
|
||||||
return WebDavCloud.aWebDavCloudCloud() //
|
|
||||||
.withUrl("https://webdav.mc.gmx.net") //
|
|
||||||
.withUsername("jraufelder@gmx.de") //
|
|
||||||
.withPassword(CredentialCryptor.getInstance(context).encrypt("mG7!3B3Mx")) //
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "DropboxTestCloud";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,86 +0,0 @@
|
|||||||
package org.cryptomator.presentation.testCloud;
|
|
||||||
|
|
||||||
import androidx.test.InstrumentationRegistry;
|
|
||||||
import androidx.test.uiautomator.UiDevice;
|
|
||||||
import androidx.test.uiautomator.UiObjectNotFoundException;
|
|
||||||
import androidx.test.uiautomator.UiSelector;
|
|
||||||
|
|
||||||
import junit.framework.AssertionFailedError;
|
|
||||||
|
|
||||||
import org.cryptomator.domain.Cloud;
|
|
||||||
import org.cryptomator.domain.GoogleDriveCloud;
|
|
||||||
import org.cryptomator.presentation.R;
|
|
||||||
import org.cryptomator.presentation.di.component.ApplicationComponent;
|
|
||||||
|
|
||||||
import static androidx.test.espresso.Espresso.onView;
|
|
||||||
import static androidx.test.espresso.action.ViewActions.click;
|
|
||||||
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
|
||||||
import static androidx.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition;
|
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
|
||||||
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
|
||||||
import static org.cryptomator.presentation.ui.TestUtil.GOOGLE_DRIVE;
|
|
||||||
import static org.cryptomator.presentation.ui.activity.BasicNodeOperationsUtil.withRecyclerView;
|
|
||||||
import static org.cryptomator.presentation.ui.activity.CloudsOperationsTest.checkLoginResult;
|
|
||||||
import static org.cryptomator.presentation.ui.activity.CloudsOperationsTest.openCloudServices;
|
|
||||||
|
|
||||||
public class GoogledriveTestCloud extends TestCloud {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Cloud getInstance(ApplicationComponent appComponent) {
|
|
||||||
login();
|
|
||||||
return GoogleDriveCloud.aGoogleDriveCloud() //
|
|
||||||
.withUsername("geselthyn@googlemail.com") //
|
|
||||||
.withAccessToken("geselthyn@googlemail.com") //
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void login() {
|
|
||||||
UiDevice device = UiDevice.getInstance(getInstrumentation());
|
|
||||||
|
|
||||||
openCloudServices(device);
|
|
||||||
|
|
||||||
if (alreadyLoggedIn()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
onView(withId(R.id.recyclerView)) //
|
|
||||||
.perform(actionOnItemAtPosition(GOOGLE_DRIVE, click()));
|
|
||||||
|
|
||||||
try {
|
|
||||||
device //
|
|
||||||
.findObject(new UiSelector().resourceId("android:id/text1")) //
|
|
||||||
.click();
|
|
||||||
|
|
||||||
device //
|
|
||||||
.findObject(new UiSelector().resourceId("android:id/button1")) //
|
|
||||||
.click();
|
|
||||||
} catch (UiObjectNotFoundException e) {
|
|
||||||
throw new AssertionError("GoogleDrive login failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
device.waitForIdle();
|
|
||||||
|
|
||||||
checkLoginResult("Google Drive", GOOGLE_DRIVE);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean alreadyLoggedIn() {
|
|
||||||
try {
|
|
||||||
onView(withRecyclerView(R.id.recyclerView) //
|
|
||||||
.atPositionOnView(GOOGLE_DRIVE, R.id.tv_cloud_name)) //
|
|
||||||
.check(matches(withText(InstrumentationRegistry //
|
|
||||||
.getTargetContext() //
|
|
||||||
.getString(R.string.screen_cloud_settings_sign_out_from_cloud) + " Google Drive")));
|
|
||||||
} catch (AssertionFailedError e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "GoogledriveTestCloud";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,63 +0,0 @@
|
|||||||
package org.cryptomator.presentation.testCloud;
|
|
||||||
|
|
||||||
import androidx.test.uiautomator.UiDevice;
|
|
||||||
|
|
||||||
import org.cryptomator.domain.Cloud;
|
|
||||||
import org.cryptomator.domain.CloudType;
|
|
||||||
import org.cryptomator.domain.LocalStorageCloud;
|
|
||||||
import org.cryptomator.domain.exception.BackendException;
|
|
||||||
import org.cryptomator.presentation.R;
|
|
||||||
import org.cryptomator.presentation.di.component.ApplicationComponent;
|
|
||||||
|
|
||||||
import static androidx.test.espresso.Espresso.onView;
|
|
||||||
import static androidx.test.espresso.action.ViewActions.click;
|
|
||||||
import static androidx.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition;
|
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
|
||||||
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
|
||||||
import static org.cryptomator.domain.executor.BackgroundTasks.awaitCompleted;
|
|
||||||
import static org.cryptomator.presentation.ui.TestUtil.LOCAL;
|
|
||||||
import static org.cryptomator.presentation.ui.TestUtil.chooseSdCard;
|
|
||||||
import static org.cryptomator.presentation.ui.activity.CloudsOperationsTest.openCloudServices;
|
|
||||||
import static org.cryptomator.presentation.ui.activity.LoginLocalClouds.chooseFolder;
|
|
||||||
|
|
||||||
public class LocalStorageTestCloud extends TestCloud {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Cloud getInstance(ApplicationComponent appComponent) {
|
|
||||||
login();
|
|
||||||
try {
|
|
||||||
return appComponent.cloudRepository() //
|
|
||||||
.clouds(CloudType.LOCAL).stream() //
|
|
||||||
.map(LocalStorageCloud.class::cast) //
|
|
||||||
.filter(cloud -> cloud.rootUri() != null) //
|
|
||||||
.findFirst() //
|
|
||||||
.get();
|
|
||||||
} catch (BackendException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void login() {
|
|
||||||
UiDevice device = UiDevice.getInstance(getInstrumentation());
|
|
||||||
|
|
||||||
openCloudServices(device);
|
|
||||||
|
|
||||||
onView(withId(R.id.recyclerView)) //
|
|
||||||
.perform(actionOnItemAtPosition(LOCAL, click()));
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(withId(R.id.floating_action_button)) //
|
|
||||||
.perform(click());
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
chooseSdCard(device);
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
chooseFolder(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "LocalStorageTestCloud";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
package org.cryptomator.presentation.testCloud;
|
|
||||||
|
|
||||||
import org.cryptomator.domain.Cloud;
|
|
||||||
import org.cryptomator.domain.LocalStorageCloud;
|
|
||||||
import org.cryptomator.presentation.di.component.ApplicationComponent;
|
|
||||||
|
|
||||||
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
|
||||||
|
|
||||||
public class LocalTestCloud extends TestCloud {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Cloud getInstance(ApplicationComponent appComponent) {
|
|
||||||
getInstrumentation() //
|
|
||||||
.getUiAutomation() //
|
|
||||||
.executeShellCommand("pm grant " + "org.cryptomator" + " android.permission.READ_EXTERNAL_STORAGE");
|
|
||||||
|
|
||||||
getInstrumentation() //
|
|
||||||
.getUiAutomation() //
|
|
||||||
.executeShellCommand("pm grant " + "org.cryptomator" + " android.permission.WRITE_EXTERNAL_STORAGE");
|
|
||||||
|
|
||||||
return LocalStorageCloud.aLocalStorage().build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "LocalTestCloud";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
package org.cryptomator.presentation.testCloud;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
|
|
||||||
import org.cryptomator.domain.Cloud;
|
|
||||||
import org.cryptomator.domain.OnedriveCloud;
|
|
||||||
import org.cryptomator.presentation.di.component.ApplicationComponent;
|
|
||||||
|
|
||||||
public class OnedriveTestCloud extends TestCloud {
|
|
||||||
|
|
||||||
private final Context context;
|
|
||||||
|
|
||||||
public OnedriveTestCloud(Context context) {
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Cloud getInstance(ApplicationComponent appComponent) {
|
|
||||||
SharedPreferences sharedPreferences = context.getSharedPreferences("com.microsoft.live", 0);
|
|
||||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
|
||||||
editor.putString("refresh_token", "OAQABAAAAAABHh4kmS_aKT5XrjzxRAtHzMkxzFfIzutF8Q04IwuU77Vmp5-ErO6muS0-watCiLjvPG" + //
|
|
||||||
"-QICK6PwzSsJZApKZPIyTgDxJuElkhM9j9Caa-NGXKx3JPZx4Tk5X3zhmSWebZdQu2TM1N" + //
|
|
||||||
"RrKWLl2k2m3Zj7xr1zEsjcr4tUjjrZl_W6EIglAIoi-DPOwznIEDWAzWCJeUY76CIpywax" + //
|
|
||||||
"RTsbcZXD3u2321kIGaL-bnGLa_z5IzUbal0PcYfPNYPXXTuv8eMyl4L9Tls1tSEseTHgCu" + //
|
|
||||||
"X3OZU2owiGQk6ycDAeyrbRaPoUOA78GYuaJGUXRmhAeH1WBccUzABdrZmAY1dAt4yu2eV7" + //
|
|
||||||
"70RzrDQhbLCPV3u3x-7xEtvsM8w0W7096VBuu2-MXvaWuDccnCHo_PK8ketpow19_llBI9" + //
|
|
||||||
"fx7yBnIU-HCkKuvOCKcvVq3Bv9r312bwAoWHdOrxsKrNK8aLoR317O9Cxjpr7q-YI3NSJJ" + //
|
|
||||||
"veTK_vn2uE0e6gppGOYSmpJIvoW82ZVngpptW0jsp6rOsnkSg2yfKKpIPN-n0U1njVlYf8" + //
|
|
||||||
"cwsS99tx8NDdCPS6MTkVmcdKRJ4dhMuuWdEm4E_hZRnnj2Pya3APxjL2eqyAR1-54TDLF-" + //
|
|
||||||
"-L-jIJUS4bVpC_RBQn4fxcrX4s-ddo6I0ejfdC07mU_Np9p66VJ_3_Yokt64fFw-zzaGpn" + //
|
|
||||||
"QEzRMxtJp5G40MAelYwxLhDIc-syw91JEoSSqfGJYHETKExPlnQOw-rqLGPFbIF6OKFNqO" + //
|
|
||||||
"XtVLWKZFxIEf-EFQbhq5igZz8DZ2n-cRxC3HWi_x18tVSAA");
|
|
||||||
editor.commit();
|
|
||||||
|
|
||||||
return OnedriveCloud.aOnedriveCloud() //
|
|
||||||
.withUsername("info@cryptomator.org") //
|
|
||||||
.withAccessToken("authenticated") //
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "OnedriveTestCloud";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
package org.cryptomator.presentation.testCloud;
|
|
||||||
|
|
||||||
import org.cryptomator.domain.Cloud;
|
|
||||||
import org.cryptomator.presentation.di.component.ApplicationComponent;
|
|
||||||
|
|
||||||
public abstract class TestCloud {
|
|
||||||
|
|
||||||
public abstract Cloud getInstance(ApplicationComponent appComponent);
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
package org.cryptomator.presentation.testCloud;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import org.cryptomator.domain.Cloud;
|
|
||||||
import org.cryptomator.domain.WebDavCloud;
|
|
||||||
import org.cryptomator.presentation.di.component.ApplicationComponent;
|
|
||||||
import org.cryptomator.util.crypto.CredentialCryptor;
|
|
||||||
|
|
||||||
public class WebdavTestCloud extends TestCloud {
|
|
||||||
|
|
||||||
private final Context context;
|
|
||||||
|
|
||||||
public WebdavTestCloud(Context context) {
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Cloud getInstance(ApplicationComponent appComponent) {
|
|
||||||
return WebDavCloud.aWebDavCloudCloud() //
|
|
||||||
.withUrl("https://webdav.mc.gmx.net") //
|
|
||||||
.withUsername("jraufelder@gmx.de") //
|
|
||||||
.withPassword(CredentialCryptor.getInstance(context).encrypt("mG7!3B3Mx")) //
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "WebdavTestCloud";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,65 +0,0 @@
|
|||||||
package org.cryptomator.presentation.ui;
|
|
||||||
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
|
||||||
|
|
||||||
import org.hamcrest.Description;
|
|
||||||
import org.hamcrest.Matcher;
|
|
||||||
import org.hamcrest.TypeSafeMatcher;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by dannyroa on 5/10/15.
|
|
||||||
*/
|
|
||||||
public class RecyclerViewMatcher {
|
|
||||||
|
|
||||||
private final int recyclerViewId;
|
|
||||||
|
|
||||||
public RecyclerViewMatcher(int recyclerViewId) {
|
|
||||||
this.recyclerViewId = recyclerViewId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Matcher<View> atPositionOnView(final int position, final int targetViewId) {
|
|
||||||
|
|
||||||
return new TypeSafeMatcher<View>() {
|
|
||||||
Resources resources = null;
|
|
||||||
View childView;
|
|
||||||
|
|
||||||
public void describeTo(Description description) {
|
|
||||||
String idDescription = Integer.toString(recyclerViewId);
|
|
||||||
if (this.resources != null) {
|
|
||||||
try {
|
|
||||||
idDescription = this.resources.getResourceName(recyclerViewId);
|
|
||||||
} catch (Resources.NotFoundException var4) {
|
|
||||||
idDescription = String.format("%s (resource name not found)", recyclerViewId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
description.appendText("with id: " + idDescription);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean matchesSafely(View view) {
|
|
||||||
|
|
||||||
this.resources = view.getResources();
|
|
||||||
|
|
||||||
if (childView == null) {
|
|
||||||
RecyclerView recyclerView = view.getRootView().findViewById(recyclerViewId);
|
|
||||||
if (recyclerView != null && recyclerView.getId() == recyclerViewId) {
|
|
||||||
childView = recyclerView.findViewHolderForAdapterPosition(position).itemView;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targetViewId == -1) {
|
|
||||||
return view == childView;
|
|
||||||
} else {
|
|
||||||
View targetView = childView.findViewById(targetViewId);
|
|
||||||
return view == targetView;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,179 +0,0 @@
|
|||||||
package org.cryptomator.presentation.ui;
|
|
||||||
|
|
||||||
import androidx.test.espresso.ViewInteraction;
|
|
||||||
import androidx.test.rule.ActivityTestRule;
|
|
||||||
import androidx.test.uiautomator.UiDevice;
|
|
||||||
import androidx.test.uiautomator.UiObjectNotFoundException;
|
|
||||||
import androidx.test.uiautomator.UiSelector;
|
|
||||||
|
|
||||||
import org.cryptomator.domain.Cloud;
|
|
||||||
import org.cryptomator.domain.CloudFolder;
|
|
||||||
import org.cryptomator.domain.CloudNode;
|
|
||||||
import org.cryptomator.domain.CloudType;
|
|
||||||
import org.cryptomator.domain.Vault;
|
|
||||||
import org.cryptomator.domain.exception.BackendException;
|
|
||||||
import org.cryptomator.presentation.R;
|
|
||||||
import org.cryptomator.presentation.di.component.ApplicationComponent;
|
|
||||||
import org.hamcrest.Matchers;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static androidx.test.espresso.Espresso.onView;
|
|
||||||
import static androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu;
|
|
||||||
import static androidx.test.espresso.action.ViewActions.click;
|
|
||||||
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
|
||||||
import static androidx.test.espresso.matcher.RootMatchers.withDecorView;
|
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withParent;
|
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
|
||||||
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
|
||||||
import static org.cryptomator.domain.executor.BackgroundTasks.awaitCompleted;
|
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
|
||||||
import static org.hamcrest.Matchers.allOf;
|
|
||||||
import static org.hamcrest.Matchers.not;
|
|
||||||
import static org.hamcrest.Matchers.notNullValue;
|
|
||||||
import static org.hamcrest.core.Is.is;
|
|
||||||
|
|
||||||
public class TestUtil {
|
|
||||||
|
|
||||||
public static final int DROPBOX = 0;
|
|
||||||
public static final int GOOGLE_DRIVE = 1;
|
|
||||||
public static final int ONEDRIVE = 2;
|
|
||||||
public static final int WEBDAV = 3;
|
|
||||||
public static final int LOCAL = 4;
|
|
||||||
|
|
||||||
private static final String SD_CARD_REGEX = "(?i)SD[- ]*(CARD|KARTE)";
|
|
||||||
|
|
||||||
public static void isToastDisplayed(String message, ActivityTestRule activityTestRule) {
|
|
||||||
onView(withText(message)) //
|
|
||||||
.inRoot(withDecorView(not(is(activityTestRule.getActivity().getWindow().getDecorView())))) //
|
|
||||||
.check(matches(isDisplayed()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void openMenu(UiDevice device) {
|
|
||||||
try {
|
|
||||||
final UiSelector toolbar = new UiSelector() //
|
|
||||||
.resourceId("com.android.documentsui:id/toolbar");
|
|
||||||
|
|
||||||
device //
|
|
||||||
.findObject(toolbar.childSelector(new UiSelector().index(0))) //
|
|
||||||
.click();
|
|
||||||
} catch (UiObjectNotFoundException e) {
|
|
||||||
throw new AssertionError("Menu not found");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void chooseSdCard(UiDevice device) {
|
|
||||||
try {
|
|
||||||
if (!sdCardAlreadySelected()) {
|
|
||||||
openMenu(device);
|
|
||||||
|
|
||||||
device //
|
|
||||||
.findObject(new UiSelector().textMatches(SD_CARD_REGEX)) //
|
|
||||||
.click();
|
|
||||||
}
|
|
||||||
} catch (UiObjectNotFoundException e) {
|
|
||||||
throw new AssertionError("Menu not found");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean sdCardAlreadySelected() {
|
|
||||||
ViewInteraction textView = onView(allOf(withText("SDCARD"), withParent(withId(R.id.toolbar)), isDisplayed()));
|
|
||||||
return textView.check(matches(withText("SDCARD"))) != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void openSettings(UiDevice device) {
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
waitForIdle(device);
|
|
||||||
|
|
||||||
openActionBarOverflowOrOptionsMenu(getInstrumentation().getTargetContext());
|
|
||||||
|
|
||||||
waitForIdle(device);
|
|
||||||
|
|
||||||
onView(allOf( //
|
|
||||||
withId(R.id.title), //
|
|
||||||
withText(R.string.snack_bar_action_title_settings))) //
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void waitForIdle(UiDevice uiDevice) {
|
|
||||||
uiDevice.waitForIdle();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void addFolderInCloud(ApplicationComponent appComponent, String path, CloudType cloudType) {
|
|
||||||
try {
|
|
||||||
CloudFolder vaultFolder = (CloudFolder) getNode(appComponent, getEncryptedCloud(appComponent, cloudType), path);
|
|
||||||
|
|
||||||
if (!appComponent.cloudContentRepository().exists(vaultFolder)) {
|
|
||||||
assertThat(appComponent.cloudContentRepository().create(vaultFolder), Matchers.is(notNullValue()));
|
|
||||||
}
|
|
||||||
} catch (BackendException e) {
|
|
||||||
throw new AssertionError("Error while adding testVault");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void removeFolderInCloud(ApplicationComponent appComponent, String path, CloudType cloudType) {
|
|
||||||
try {
|
|
||||||
CloudFolder vaultFolder = (CloudFolder) getNode(appComponent, getEncryptedCloud(appComponent, cloudType), path);
|
|
||||||
|
|
||||||
if (appComponent.cloudContentRepository().exists(vaultFolder)) {
|
|
||||||
appComponent.cloudContentRepository().delete(vaultFolder);
|
|
||||||
}
|
|
||||||
} catch (BackendException e) {
|
|
||||||
throw new AssertionError("Error while removing testVault");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Cloud getEncryptedCloud(ApplicationComponent appComponent, CloudType cloudType) throws BackendException {
|
|
||||||
Cloud cloud;
|
|
||||||
if (cloudType.equals(CloudType.LOCAL)) {
|
|
||||||
cloud = appComponent.cloudRepository().clouds(cloudType).get(1);
|
|
||||||
} else {
|
|
||||||
cloud = appComponent.cloudRepository().clouds(cloudType).get(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return cloud;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static CloudNode getNode(ApplicationComponent appComponent, Cloud cloud, String path) throws BackendException {
|
|
||||||
return appComponent.cloudContentRepository().resolve(cloud, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void removeFolderInVault(ApplicationComponent appComponent, String name, CloudType cloudType) {
|
|
||||||
try {
|
|
||||||
Cloud decryptedCloud = getDecryptedCloud(appComponent, cloudType);
|
|
||||||
CloudFolder root = appComponent.cloudContentRepository().root(decryptedCloud);
|
|
||||||
CloudFolder folder = appComponent.cloudContentRepository().folder(root, name);
|
|
||||||
appComponent.cloudContentRepository().delete(folder);
|
|
||||||
} catch (BackendException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void addFolderInVaultsRoot(ApplicationComponent appComponent, String name, CloudType cloudType) {
|
|
||||||
try {
|
|
||||||
Cloud decryptedCloud = getDecryptedCloud(appComponent, cloudType);
|
|
||||||
CloudFolder root = appComponent.cloudContentRepository().root(decryptedCloud);
|
|
||||||
CloudFolder folder = appComponent.cloudContentRepository().folder(root, name);
|
|
||||||
assertThat(appComponent.cloudContentRepository().create(folder), is(notNullValue()));
|
|
||||||
} catch (BackendException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Cloud getDecryptedCloud(ApplicationComponent appComponent, CloudType cloudType) throws BackendException {
|
|
||||||
List<Vault> vaults = appComponent.vaultRepository().vaults();
|
|
||||||
for (Vault vault : vaults) {
|
|
||||||
if (vault.getCloudType().equals(cloudType)) {
|
|
||||||
return appComponent.cloudRepository().decryptedViewOf(vault);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new AssertionError("Cloud for vault not found");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
package org.cryptomator.presentation.ui.activity;
|
|
||||||
|
|
||||||
import androidx.test.InstrumentationRegistry;
|
|
||||||
import androidx.test.uiautomator.UiDevice;
|
|
||||||
|
|
||||||
import org.cryptomator.presentation.R;
|
|
||||||
import org.cryptomator.presentation.ui.RecyclerViewMatcher;
|
|
||||||
|
|
||||||
import static androidx.test.espresso.Espresso.onView;
|
|
||||||
import static androidx.test.espresso.action.ViewActions.click;
|
|
||||||
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
|
||||||
import static java.lang.String.format;
|
|
||||||
import static org.cryptomator.domain.executor.BackgroundTasks.awaitCompleted;
|
|
||||||
import static org.hamcrest.Matchers.allOf;
|
|
||||||
|
|
||||||
public class BasicNodeOperationsUtil {
|
|
||||||
|
|
||||||
static void openSettings(UiDevice device, int nodePosition) {
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
waitForIdle(device);
|
|
||||||
|
|
||||||
onView(withRecyclerView(R.id.recyclerView) //
|
|
||||||
.atPositionOnView(nodePosition, R.id.settings)) //
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void waitForIdle(UiDevice uiDevice) {
|
|
||||||
uiDevice.waitForIdle();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void checkEmptyFolderHint() {
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(allOf( //
|
|
||||||
withId(R.id.tv_empty_folder_hint), //
|
|
||||||
withText(R.string.screen_file_browser_msg_empty_folder))) //
|
|
||||||
.check(matches(withText(R.string.screen_file_browser_msg_empty_folder)));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void checkFileOrFolderAlreadyExistsErrorMessage(String nodeName) {
|
|
||||||
onView(withId(R.id.tv_error)) //
|
|
||||||
.check(matches(withText(format( //
|
|
||||||
InstrumentationRegistry //
|
|
||||||
.getTargetContext() //
|
|
||||||
.getString(R.string.error_file_or_folder_exists), //
|
|
||||||
nodeName))));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RecyclerViewMatcher withRecyclerView(final int recyclerViewId) {
|
|
||||||
return new RecyclerViewMatcher(recyclerViewId);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,248 +0,0 @@
|
|||||||
package org.cryptomator.presentation.ui.activity;
|
|
||||||
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.view.ViewParent;
|
|
||||||
|
|
||||||
import androidx.test.InstrumentationRegistry;
|
|
||||||
import androidx.test.espresso.ViewInteraction;
|
|
||||||
import androidx.test.espresso.contrib.RecyclerViewActions;
|
|
||||||
import androidx.test.rule.ActivityTestRule;
|
|
||||||
import androidx.test.uiautomator.UiDevice;
|
|
||||||
import androidx.test.uiautomator.UiObject;
|
|
||||||
import androidx.test.uiautomator.UiObjectNotFoundException;
|
|
||||||
import androidx.test.uiautomator.UiScrollable;
|
|
||||||
import androidx.test.uiautomator.UiSelector;
|
|
||||||
|
|
||||||
import org.cryptomator.presentation.R;
|
|
||||||
import org.hamcrest.Description;
|
|
||||||
import org.hamcrest.Matcher;
|
|
||||||
import org.hamcrest.TypeSafeMatcher;
|
|
||||||
import org.junit.FixMethodOrder;
|
|
||||||
import org.junit.Rule;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runners.MethodSorters;
|
|
||||||
|
|
||||||
import static androidx.test.espresso.Espresso.onView;
|
|
||||||
import static androidx.test.espresso.action.ViewActions.click;
|
|
||||||
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
|
||||||
import static androidx.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition;
|
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withChild;
|
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
|
||||||
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
|
||||||
import static java.lang.Thread.sleep;
|
|
||||||
import static org.cryptomator.domain.executor.BackgroundTasks.awaitCompleted;
|
|
||||||
import static org.cryptomator.presentation.ui.TestUtil.DROPBOX;
|
|
||||||
import static org.cryptomator.presentation.ui.TestUtil.GOOGLE_DRIVE;
|
|
||||||
import static org.cryptomator.presentation.ui.TestUtil.LOCAL;
|
|
||||||
import static org.cryptomator.presentation.ui.TestUtil.ONEDRIVE;
|
|
||||||
import static org.cryptomator.presentation.ui.TestUtil.WEBDAV;
|
|
||||||
import static org.cryptomator.presentation.ui.TestUtil.openSettings;
|
|
||||||
import static org.cryptomator.presentation.ui.activity.BasicNodeOperationsUtil.withRecyclerView;
|
|
||||||
import static org.hamcrest.core.AllOf.allOf;
|
|
||||||
|
|
||||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
|
||||||
public class CloudsOperationsTest {
|
|
||||||
|
|
||||||
private final UiDevice device = UiDevice.getInstance(getInstrumentation());
|
|
||||||
@Rule
|
|
||||||
public ActivityTestRule<SplashActivity> activityTestRule //
|
|
||||||
= new ActivityTestRule<>(SplashActivity.class);
|
|
||||||
|
|
||||||
public static void openCloudServices(UiDevice device) {
|
|
||||||
openSettings(device);
|
|
||||||
|
|
||||||
ViewInteraction recyclerView = onView(allOf(withId(R.id.recycler_view), childAtPosition(withId(android.R.id.list_container), 0)));
|
|
||||||
recyclerView.perform(RecyclerViewActions.actionOnItemAtPosition(3, click()));
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void checkLoginResult(String cloudName, int cloudPosition) {
|
|
||||||
String displayText = InstrumentationRegistry //
|
|
||||||
.getTargetContext() //
|
|
||||||
.getString(R.string.screen_cloud_settings_sign_out_from_cloud) + " " + cloudName;
|
|
||||||
|
|
||||||
UiObject signOutText = UiDevice.getInstance(getInstrumentation()).findObject(new UiSelector().text(displayText));
|
|
||||||
signOutText.waitForExists(15000);
|
|
||||||
|
|
||||||
onView(withRecyclerView(R.id.recyclerView) //
|
|
||||||
.atPositionOnView(cloudPosition, R.id.cloudName)) //
|
|
||||||
.check(matches(withText(displayText)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Matcher<View> childAtPosition(final Matcher<View> parentMatcher, final int position) {
|
|
||||||
|
|
||||||
return new TypeSafeMatcher<View>() {
|
|
||||||
@Override
|
|
||||||
public void describeTo(Description description) {
|
|
||||||
description.appendText("Child at position " + position + " in parent ");
|
|
||||||
parentMatcher.describeTo(description);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean matchesSafely(View view) {
|
|
||||||
ViewParent parent = view.getParent();
|
|
||||||
return parent instanceof ViewGroup && parentMatcher.matches(parent) //
|
|
||||||
&& view.equals(((ViewGroup) parent).getChildAt(position));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test01EnableDebugModeLeadsToDebugMode() {
|
|
||||||
openSettings(device);
|
|
||||||
|
|
||||||
try {
|
|
||||||
new UiScrollable(new UiSelector().scrollable(true)).scrollToEnd(10);
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(withChild(withText(R.string.screen_settings_debug_mode_label))) //
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(withId(android.R.id.button1)) //
|
|
||||||
.perform(click());
|
|
||||||
} catch (UiObjectNotFoundException e) {
|
|
||||||
throw new AssertionError("Scrolling down failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test02LoginDropboxCloudLeadsToLoggedInDropboxCloud() {
|
|
||||||
openCloudServices(device);
|
|
||||||
|
|
||||||
onView(withId(R.id.recyclerView)) //
|
|
||||||
.perform(actionOnItemAtPosition(DROPBOX, click()));
|
|
||||||
|
|
||||||
try {
|
|
||||||
device //
|
|
||||||
.findObject(new UiSelector().resourceId("android:id/button_once")) //
|
|
||||||
.click();
|
|
||||||
|
|
||||||
device.waitForIdle();
|
|
||||||
|
|
||||||
device //
|
|
||||||
.findObject(new UiSelector().text("Email")) //
|
|
||||||
.setText("");
|
|
||||||
|
|
||||||
device //
|
|
||||||
.findObject(new UiSelector().text("Password")) //
|
|
||||||
.setText("");
|
|
||||||
|
|
||||||
device //
|
|
||||||
.findObject(new UiSelector().description("Sign in")) //
|
|
||||||
.click();
|
|
||||||
|
|
||||||
device.waitForIdle();
|
|
||||||
|
|
||||||
device //
|
|
||||||
.findObject(new UiSelector().description("Allow")) //
|
|
||||||
.click();
|
|
||||||
|
|
||||||
} catch (UiObjectNotFoundException e) {
|
|
||||||
throw new AssertionError("Dropbox login failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
device.waitForIdle();
|
|
||||||
|
|
||||||
checkLoginResult("Dropbox", DROPBOX);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test03LoginGoogleDriveCloudLeadsToLoggedInGoogleDriveCloud() {
|
|
||||||
openCloudServices(device);
|
|
||||||
|
|
||||||
onView(withId(R.id.recyclerView)) //
|
|
||||||
.perform(actionOnItemAtPosition(GOOGLE_DRIVE, click()));
|
|
||||||
|
|
||||||
try {
|
|
||||||
device //
|
|
||||||
.findObject(new UiSelector().resourceId("android:id/text1")) //
|
|
||||||
.click();
|
|
||||||
|
|
||||||
device //
|
|
||||||
.findObject(new UiSelector().resourceId("android:id/button1")) //
|
|
||||||
.click();
|
|
||||||
} catch (UiObjectNotFoundException e) {
|
|
||||||
throw new AssertionError("GoogleDrive login failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
device.waitForIdle();
|
|
||||||
|
|
||||||
checkLoginResult("Google Drive", GOOGLE_DRIVE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test04LoginOneDriveLeadsToLoggedInOneDriveCloud() {
|
|
||||||
openCloudServices(device);
|
|
||||||
|
|
||||||
onView(withId(R.id.recyclerView)) //
|
|
||||||
.perform(actionOnItemAtPosition(ONEDRIVE, click()));
|
|
||||||
|
|
||||||
try {
|
|
||||||
try {
|
|
||||||
sleep(500);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
device //
|
|
||||||
.findObject(new UiSelector().resourceId("i0116")) //
|
|
||||||
.setText("");
|
|
||||||
|
|
||||||
device //
|
|
||||||
.findObject(new UiSelector().resourceId("idSIButton9")) //
|
|
||||||
.click();
|
|
||||||
|
|
||||||
device.waitForWindowUpdate(null, 500);
|
|
||||||
|
|
||||||
device //
|
|
||||||
.findObject(new UiSelector().resourceId("i0118")) //
|
|
||||||
.setText("");
|
|
||||||
|
|
||||||
device.waitForWindowUpdate(null, 500);
|
|
||||||
|
|
||||||
device //
|
|
||||||
.findObject(new UiSelector().resourceId("idSIButton9")) //
|
|
||||||
.click();
|
|
||||||
|
|
||||||
try {
|
|
||||||
device //
|
|
||||||
.findObject(new UiSelector().resourceId("idSIButton9")) //
|
|
||||||
.click();
|
|
||||||
} catch (UiObjectNotFoundException e) {
|
|
||||||
// Do nothing because second click is normaly not necessary
|
|
||||||
}
|
|
||||||
} catch (UiObjectNotFoundException e) {
|
|
||||||
throw new AssertionError("OneDrive login failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
checkLoginResult("OneDrive", ONEDRIVE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test05LoginWebdavCloudLeadsToLoggedInWebdavCloud() {
|
|
||||||
openCloudServices(device);
|
|
||||||
|
|
||||||
onView(withId(R.id.recyclerView)) //
|
|
||||||
.perform(actionOnItemAtPosition(WEBDAV, click()));
|
|
||||||
|
|
||||||
LoginWebdavClouds.loginWebdavClouds(activityTestRule.getActivity());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test06LoginLocalCloudLeadsToLoggedInLocalCloud() {
|
|
||||||
openCloudServices(device);
|
|
||||||
|
|
||||||
onView(withId(R.id.recyclerView)) //
|
|
||||||
.perform(actionOnItemAtPosition(LOCAL, click()));
|
|
||||||
|
|
||||||
LoginLocalClouds.loginLocalClouds(device);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,668 +0,0 @@
|
|||||||
package org.cryptomator.presentation.ui.activity;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import androidx.test.InstrumentationRegistry;
|
|
||||||
import androidx.test.rule.ActivityTestRule;
|
|
||||||
import androidx.test.uiautomator.UiDevice;
|
|
||||||
import androidx.test.uiautomator.UiObjectNotFoundException;
|
|
||||||
import androidx.test.uiautomator.UiSelector;
|
|
||||||
|
|
||||||
import org.cryptomator.domain.CloudType;
|
|
||||||
import org.cryptomator.presentation.CryptomatorApp;
|
|
||||||
import org.cryptomator.presentation.R;
|
|
||||||
import org.cryptomator.presentation.di.component.ApplicationComponent;
|
|
||||||
import org.junit.FixMethodOrder;
|
|
||||||
import org.junit.Rule;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.MethodSorters;
|
|
||||||
import org.junit.runners.Parameterized;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import static android.R.id.button1;
|
|
||||||
import static androidx.test.espresso.Espresso.onView;
|
|
||||||
import static androidx.test.espresso.Espresso.pressBack;
|
|
||||||
import static androidx.test.espresso.action.ViewActions.click;
|
|
||||||
import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard;
|
|
||||||
import static androidx.test.espresso.action.ViewActions.replaceText;
|
|
||||||
import static androidx.test.espresso.action.ViewActions.swipeDown;
|
|
||||||
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
|
||||||
import static androidx.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition;
|
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
|
||||||
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
|
||||||
import static java.lang.String.format;
|
|
||||||
import static org.cryptomator.domain.executor.BackgroundTasks.awaitCompleted;
|
|
||||||
import static org.cryptomator.presentation.ui.TestUtil.DROPBOX;
|
|
||||||
import static org.cryptomator.presentation.ui.TestUtil.GOOGLE_DRIVE;
|
|
||||||
import static org.cryptomator.presentation.ui.TestUtil.LOCAL;
|
|
||||||
import static org.cryptomator.presentation.ui.TestUtil.ONEDRIVE;
|
|
||||||
import static org.cryptomator.presentation.ui.TestUtil.WEBDAV;
|
|
||||||
import static org.cryptomator.presentation.ui.TestUtil.addFolderInVaultsRoot;
|
|
||||||
import static org.cryptomator.presentation.ui.TestUtil.chooseSdCard;
|
|
||||||
import static org.cryptomator.presentation.ui.TestUtil.isToastDisplayed;
|
|
||||||
import static org.cryptomator.presentation.ui.TestUtil.removeFolderInVault;
|
|
||||||
import static org.cryptomator.presentation.ui.activity.BasicNodeOperationsUtil.checkEmptyFolderHint;
|
|
||||||
import static org.cryptomator.presentation.ui.activity.BasicNodeOperationsUtil.checkFileOrFolderAlreadyExistsErrorMessage;
|
|
||||||
import static org.cryptomator.presentation.ui.activity.BasicNodeOperationsUtil.openSettings;
|
|
||||||
import static org.cryptomator.presentation.ui.activity.BasicNodeOperationsUtil.withRecyclerView;
|
|
||||||
import static org.cryptomator.presentation.ui.activity.FolderOperationsTest.openFolder;
|
|
||||||
import static org.hamcrest.Matchers.allOf;
|
|
||||||
import static org.hamcrest.Matchers.is;
|
|
||||||
import static org.hamcrest.Matchers.not;
|
|
||||||
import static org.junit.Assume.assumeThat;
|
|
||||||
|
|
||||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
|
||||||
@RunWith(Parameterized.class)
|
|
||||||
public class FileOperationsTest {
|
|
||||||
|
|
||||||
private final UiDevice device = UiDevice.getInstance(getInstrumentation());
|
|
||||||
private final Context context = InstrumentationRegistry.getTargetContext();
|
|
||||||
private final Integer cloudId;
|
|
||||||
@Rule
|
|
||||||
public ActivityTestRule<SplashActivity> activityTestRule //
|
|
||||||
= new ActivityTestRule<>(SplashActivity.class);
|
|
||||||
private String packageName;
|
|
||||||
|
|
||||||
public FileOperationsTest(Integer cloudId, String cloudName) {
|
|
||||||
this.cloudId = cloudId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Parameterized.Parameters(name = "{1}")
|
|
||||||
public static Iterable<Object[]> data() {
|
|
||||||
return Arrays.asList(new Object[][] {{DROPBOX, "DROPBOX"}, {GOOGLE_DRIVE, "GOOGLE_DRIVE"}, {ONEDRIVE, "ONEDRIVE"}, {WEBDAV, "WEBDAV"}, {LOCAL, "LOCAL"}});
|
|
||||||
}
|
|
||||||
|
|
||||||
static void isPermissionShown(UiDevice device) {
|
|
||||||
if (!device //
|
|
||||||
.findObject(new UiSelector().text("ALLOW")) //
|
|
||||||
.waitForExists(1000L)) {
|
|
||||||
throw new AssertionError("View with text <???> not found!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void grantPermission(UiDevice device) {
|
|
||||||
try {
|
|
||||||
device //
|
|
||||||
.findObject(new UiSelector().text("ALLOW")) //
|
|
||||||
.click();
|
|
||||||
} catch (UiObjectNotFoundException e) {
|
|
||||||
throw new AssertionError("Permission not found");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void openFile(int nodePosition) {
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(withRecyclerView(R.id.recyclerView) //
|
|
||||||
.atPositionOnView(nodePosition, R.id.cloudFileText)) //
|
|
||||||
.perform(click());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test00UploadFileWithCancelPressedWhileUploadingLeadsToNoNewFileInVault() {
|
|
||||||
assumeThat(cloudId, is(not(LOCAL)));
|
|
||||||
|
|
||||||
packageName = activityTestRule.getActivity().getPackageName();
|
|
||||||
|
|
||||||
String nodeName = "foo.pdf";
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(withId(R.id.recyclerView)) //
|
|
||||||
.perform(actionOnItemAtPosition(cloudId, click()));
|
|
||||||
|
|
||||||
uploadFile(nodeName);
|
|
||||||
|
|
||||||
onView(withId(android.R.id.button3)) //
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(withId(R.id.recyclerView)) //
|
|
||||||
.perform(swipeDown());
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
try {
|
|
||||||
onView(withRecyclerView(R.id.recyclerView) //
|
|
||||||
.atPositionOnView(1, R.id.cloudFileText)) //
|
|
||||||
.check(matches(withText(nodeName)));
|
|
||||||
|
|
||||||
throw new AssertionError("Canceling the upload should not lead to new cloud node");
|
|
||||||
} catch (NullPointerException e) {
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
pressBack();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test01UploadFileLeadsToNewFileInVault() {
|
|
||||||
packageName = activityTestRule.getActivity().getPackageName();
|
|
||||||
String nodeName = "lala.png";
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(withId(R.id.recyclerView)) //
|
|
||||||
.perform(actionOnItemAtPosition(cloudId, click()));
|
|
||||||
|
|
||||||
uploadFile(nodeName);
|
|
||||||
|
|
||||||
device.waitForWindowUpdate(packageName, 15000);
|
|
||||||
|
|
||||||
checkFileDisplayText(nodeName, 1);
|
|
||||||
|
|
||||||
pressBack();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test02UploadAlreadyExistingFileAndCancelLeadsToNoNewFileInVault() {
|
|
||||||
String nodeName = "lala.png";
|
|
||||||
String subString;
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(withId(R.id.recyclerView)) //
|
|
||||||
.perform(actionOnItemAtPosition(cloudId, click()));
|
|
||||||
|
|
||||||
// subString = subtextFromFile();
|
|
||||||
|
|
||||||
uploadFile(nodeName);
|
|
||||||
|
|
||||||
device.waitForWindowUpdate(packageName, 500);
|
|
||||||
|
|
||||||
onView(withText(R.string.dialog_button_cancel)) //
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
// assertThat(subtextFromFile(), equalTo(subString));
|
|
||||||
|
|
||||||
pressBack();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test03UploadAlreadyExistingFileAndReplaceLeadsToUpdatedFileInVault() {
|
|
||||||
String nodeName = "lala.png";
|
|
||||||
String subString;
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(withId(R.id.recyclerView)) //
|
|
||||||
.perform(actionOnItemAtPosition(cloudId, click()));
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
subString = subtextFromFile();
|
|
||||||
|
|
||||||
uploadFile(nodeName);
|
|
||||||
|
|
||||||
device.waitForWindowUpdate(packageName, 500);
|
|
||||||
|
|
||||||
onView(withText(R.string.dialog_existing_file_positive_button)) //
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
// assertThat(subtextFromFile(), not(equalTo(subString)));
|
|
||||||
|
|
||||||
pressBack();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test04OpenFileLeadsToOpenFile() {
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(withId(R.id.recyclerView)) //
|
|
||||||
.perform(actionOnItemAtPosition(cloudId, click()));
|
|
||||||
|
|
||||||
openFile(1);
|
|
||||||
|
|
||||||
device.waitForWindowUpdate(null, 25000);
|
|
||||||
|
|
||||||
device.pressBack();
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
pressBack();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test05RenameFileLeadsToFileWithNewName() {
|
|
||||||
String fileName = "foo.png";
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(withId(R.id.recyclerView)) //
|
|
||||||
.perform(actionOnItemAtPosition(cloudId, click()));
|
|
||||||
|
|
||||||
renameFileTo(fileName, 1);
|
|
||||||
|
|
||||||
checkFileDisplayText(fileName, 1);
|
|
||||||
|
|
||||||
pressBack();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test06RenameFileToExistingFolderLeadsToNothingChanged() {
|
|
||||||
String fileName = "testFolder";
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(withId(R.id.recyclerView)) //
|
|
||||||
.perform(actionOnItemAtPosition(cloudId, click()));
|
|
||||||
|
|
||||||
renameFileTo(fileName, 1);
|
|
||||||
|
|
||||||
checkFileOrFolderAlreadyExistsErrorMessage(fileName);
|
|
||||||
|
|
||||||
onView(withId(android.R.id.button2)) //
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
checkFileDisplayText("foo.png", 1);
|
|
||||||
|
|
||||||
pressBack();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test07MoveFileLeadsToFileWithNewLocation() {
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(withId(R.id.recyclerView)) //
|
|
||||||
.perform(actionOnItemAtPosition(cloudId, click()));
|
|
||||||
|
|
||||||
openSettings(device, 1);
|
|
||||||
|
|
||||||
openMoveFile();
|
|
||||||
|
|
||||||
openFolder(0);
|
|
||||||
|
|
||||||
onView(withId(R.id.chooseLocationButton)) //
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
openFolder(0);
|
|
||||||
|
|
||||||
checkFileDisplayText("foo.png", 0);
|
|
||||||
|
|
||||||
pressBack();
|
|
||||||
pressBack();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test08MoveWithExistingFileLeadsToNoNewFile() {
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(withId(R.id.recyclerView)) //
|
|
||||||
.perform(actionOnItemAtPosition(cloudId, click()));
|
|
||||||
|
|
||||||
openFolder(0);
|
|
||||||
|
|
||||||
openSettings(device, 0);
|
|
||||||
|
|
||||||
openMoveFile();
|
|
||||||
|
|
||||||
onView(withId(R.id.chooseLocationButton)) //
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
isToastDisplayed( //
|
|
||||||
format( //
|
|
||||||
InstrumentationRegistry //
|
|
||||||
.getTargetContext() //
|
|
||||||
.getString(R.string.error_file_or_folder_exists), //
|
|
||||||
"foo.png"), //
|
|
||||||
activityTestRule);
|
|
||||||
|
|
||||||
pressBack();
|
|
||||||
pressBack();
|
|
||||||
pressBack();
|
|
||||||
pressBack();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test09MoveWithExistingFolderLeadsToNoNewFile() {
|
|
||||||
ApplicationComponent appComponent = ((CryptomatorApp) activityTestRule.getActivity().getApplication()).getComponent();
|
|
||||||
|
|
||||||
String moveFileNameAndTmpFolder = "foo.png";
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(withId(R.id.recyclerView)) //
|
|
||||||
.perform(actionOnItemAtPosition(cloudId, click()));
|
|
||||||
|
|
||||||
addFolderInVaultsRoot(appComponent, moveFileNameAndTmpFolder, CloudType.values()[cloudId]);
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
refreshCloudNodes();
|
|
||||||
|
|
||||||
openFolder(1);
|
|
||||||
|
|
||||||
openSettings(device, 0);
|
|
||||||
|
|
||||||
openMoveFile();
|
|
||||||
|
|
||||||
pressBack();
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(withId(R.id.chooseLocationButton)) //
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
isToastDisplayed( //
|
|
||||||
InstrumentationRegistry //
|
|
||||||
.getTargetContext() //
|
|
||||||
.getString(R.string.error_file_or_folder_exists), //
|
|
||||||
activityTestRule);
|
|
||||||
|
|
||||||
removeFolderInVault(appComponent, moveFileNameAndTmpFolder, CloudType.values()[cloudId]);
|
|
||||||
|
|
||||||
pressBack();
|
|
||||||
pressBack();
|
|
||||||
pressBack();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test10ShareFileWithCryptomatorLeadsToNewFileInVault() {
|
|
||||||
String newFileName = "fooBar.png";
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(withId(R.id.recyclerView)) //
|
|
||||||
.perform(actionOnItemAtPosition(cloudId, click()));
|
|
||||||
|
|
||||||
openFolder(0);
|
|
||||||
|
|
||||||
shareFile(newFileName, cloudId, 0);
|
|
||||||
|
|
||||||
isToastDisplayed(context.getString(R.string.screen_share_files_msg_success), activityTestRule);
|
|
||||||
|
|
||||||
refreshCloudNodes();
|
|
||||||
|
|
||||||
checkFileDisplayText(newFileName, 1);
|
|
||||||
|
|
||||||
pressBack();
|
|
||||||
pressBack();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test11ShareExistingFileAndReplaceWithCryptomatorLeadsToUpdatedFileInVault() {
|
|
||||||
String newFileName = "fooBar.png";
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(withId(R.id.recyclerView)) //
|
|
||||||
.perform(actionOnItemAtPosition(cloudId, click()));
|
|
||||||
|
|
||||||
openFolder(0);
|
|
||||||
|
|
||||||
shareFile(newFileName, cloudId, 0);
|
|
||||||
|
|
||||||
onView(withId(android.R.id.button1)) //
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
isToastDisplayed(context.getString(R.string.screen_share_files_msg_success), activityTestRule);
|
|
||||||
|
|
||||||
refreshCloudNodes();
|
|
||||||
|
|
||||||
checkFileDisplayText(newFileName, 1); // check before and compare with after upload
|
|
||||||
|
|
||||||
pressBack();
|
|
||||||
pressBack();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test12ShareExistingFileWithoutReplaceWithCryptomatorLeadsToNotUpdatedFileInVault() {
|
|
||||||
String newFileName = "fooBar.png";
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(withId(R.id.recyclerView)) //
|
|
||||||
.perform(actionOnItemAtPosition(cloudId, click()));
|
|
||||||
|
|
||||||
openFolder(0);
|
|
||||||
|
|
||||||
shareFile(newFileName, cloudId, 0);
|
|
||||||
|
|
||||||
onView(withId(android.R.id.button3)) //
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
pressBack();
|
|
||||||
pressBack();
|
|
||||||
pressBack();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test13RenameFileToExistingFileLeadsToNothingChanged() {
|
|
||||||
String fileName = "fooBar.png";
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(withId(R.id.recyclerView)) //
|
|
||||||
.perform(actionOnItemAtPosition(cloudId, click()));
|
|
||||||
|
|
||||||
openFolder(0);
|
|
||||||
|
|
||||||
renameFileTo(fileName, 0);
|
|
||||||
|
|
||||||
checkFileOrFolderAlreadyExistsErrorMessage(fileName);
|
|
||||||
|
|
||||||
onView(withId(android.R.id.button2)) //
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
checkFileDisplayText("foo.png", 0);
|
|
||||||
|
|
||||||
pressBack();
|
|
||||||
pressBack();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test14DeleteFileLeadsToRemovedFile() {
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(withId(R.id.recyclerView)) //
|
|
||||||
.perform(actionOnItemAtPosition(cloudId, click()));
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
openFolder(0);
|
|
||||||
|
|
||||||
deleteNodeOnPosition(0);
|
|
||||||
awaitCompleted();
|
|
||||||
deleteNodeOnPosition(0);
|
|
||||||
|
|
||||||
checkEmptyFolderHint();
|
|
||||||
|
|
||||||
pressBack();
|
|
||||||
|
|
||||||
deleteTestFolder();
|
|
||||||
|
|
||||||
pressBack();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void deleteNodeOnPosition(int position) {
|
|
||||||
openSettings(device, position);
|
|
||||||
|
|
||||||
onView(withId(R.id.delete_file)) //
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(withId(android.R.id.button1)) //
|
|
||||||
.perform(click());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void refreshCloudNodes() {
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(withId(R.id.recyclerView)) //
|
|
||||||
.perform(swipeDown());
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void shareFile(String newFilename, int vaultPosition, int filePosition) {
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
openSettings(device, filePosition);
|
|
||||||
|
|
||||||
openEncryptWithCryptomator();
|
|
||||||
|
|
||||||
chooseShareLocation(vaultPosition);
|
|
||||||
|
|
||||||
checkPathInSharingScreen("/testFolder", vaultPosition);
|
|
||||||
|
|
||||||
onView(withId(R.id.fileName)) //
|
|
||||||
.perform(replaceText(newFilename), closeSoftKeyboard());
|
|
||||||
|
|
||||||
onView(withId(R.id.saveFiles)) //
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void chooseShareLocation(int nodePosition) {
|
|
||||||
onView(withRecyclerView(R.id.locationsRecyclerView) //
|
|
||||||
.atPositionOnView(nodePosition, R.id.vaultName)) //
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
device.waitForWindowUpdate(packageName, 300);
|
|
||||||
|
|
||||||
onView(withRecyclerView(R.id.locationsRecyclerView) //
|
|
||||||
.atPositionOnView(nodePosition, R.id.chooseFolderLocation)) //
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
openFolder(0);
|
|
||||||
|
|
||||||
onView(withId(R.id.chooseLocationButton)) //
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkPathInSharingScreen(String path, int nodePosition) {
|
|
||||||
onView(withRecyclerView(R.id.locationsRecyclerView) //
|
|
||||||
.atPositionOnView(nodePosition, R.id.chosenLocation)) //
|
|
||||||
.check(matches(withText(path)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void deleteTestFolder() {
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
openSettings(device, 0);
|
|
||||||
|
|
||||||
onView(withId(R.id.delete_folder)) //
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(withId(android.R.id.button1)) //
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
checkEmptyFolderHint();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void uploadFile(String nodeName) {
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(withId(R.id.floatingActionButton)) //
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
onView(withId(R.id.upload_files)) //
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
chooseSdCard(device);
|
|
||||||
|
|
||||||
try {
|
|
||||||
device //
|
|
||||||
.findObject(new UiSelector().text(nodeName)) //
|
|
||||||
.click();
|
|
||||||
} catch (UiObjectNotFoundException e) {
|
|
||||||
throw new AssertionError("Image " + nodeName + " not available");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void renameFileTo(String name, int nodePosition) {
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
openSettings(device, nodePosition);
|
|
||||||
|
|
||||||
onView(withId(R.id.rename_file)) //
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
onView(withId(R.id.et_rename)) //
|
|
||||||
.perform(replaceText(name), closeSoftKeyboard());
|
|
||||||
|
|
||||||
onView(allOf( //
|
|
||||||
withId(button1), //
|
|
||||||
withText(R.string.dialog_rename_node_positive_button))) //
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkFileDisplayText(String assertNodeText, int nodePosition) {
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(withRecyclerView(R.id.recyclerView) //
|
|
||||||
.atPositionOnView(nodePosition, R.id.cloudFileText)) //
|
|
||||||
.check(matches(withText(assertNodeText)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void openMoveFile() {
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(withId(R.id.move_file)) //
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void openEncryptWithCryptomator() {
|
|
||||||
openShareFile();
|
|
||||||
try {
|
|
||||||
device //
|
|
||||||
.findObject(new UiSelector().text(context.getString(R.string.share_with_label))) //
|
|
||||||
.waitForExists(30000L);
|
|
||||||
device //
|
|
||||||
.findObject(new UiSelector().text(context.getString(R.string.share_with_label))) //
|
|
||||||
.click();
|
|
||||||
} catch (UiObjectNotFoundException e) {
|
|
||||||
throw new AssertionError("Share with Cryptomator not available");
|
|
||||||
}
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void openShareFile() {
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(withId(R.id.share_file)) //
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String subtextFromFile() {
|
|
||||||
try {
|
|
||||||
final UiSelector docList = new UiSelector() //
|
|
||||||
.resourceId("org.cryptomator:id/cloudFileContent");
|
|
||||||
|
|
||||||
return device //
|
|
||||||
.findObject(docList.childSelector(new UiSelector(). //
|
|
||||||
resourceId("org.cryptomator:id/cloudFileSubText") //
|
|
||||||
.index(0))) //
|
|
||||||
.getText();
|
|
||||||
} catch (UiObjectNotFoundException e) {
|
|
||||||
throw new AssertionError("Folder 0 not found");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,313 +0,0 @@
|
|||||||
package org.cryptomator.presentation.ui.activity;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import androidx.test.InstrumentationRegistry;
|
|
||||||
import androidx.test.rule.ActivityTestRule;
|
|
||||||
import androidx.test.uiautomator.UiDevice;
|
|
||||||
|
|
||||||
import org.cryptomator.presentation.R;
|
|
||||||
import org.junit.FixMethodOrder;
|
|
||||||
import org.junit.Rule;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.MethodSorters;
|
|
||||||
import org.junit.runners.Parameterized;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import static androidx.test.espresso.Espresso.onView;
|
|
||||||
import static androidx.test.espresso.Espresso.pressBack;
|
|
||||||
import static androidx.test.espresso.action.ViewActions.click;
|
|
||||||
import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard;
|
|
||||||
import static androidx.test.espresso.action.ViewActions.replaceText;
|
|
||||||
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
|
||||||
import static androidx.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition;
|
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
|
||||||
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
|
||||||
import static org.cryptomator.domain.executor.BackgroundTasks.awaitCompleted;
|
|
||||||
import static org.cryptomator.presentation.ui.TestUtil.DROPBOX;
|
|
||||||
import static org.cryptomator.presentation.ui.TestUtil.GOOGLE_DRIVE;
|
|
||||||
import static org.cryptomator.presentation.ui.TestUtil.LOCAL;
|
|
||||||
import static org.cryptomator.presentation.ui.TestUtil.ONEDRIVE;
|
|
||||||
import static org.cryptomator.presentation.ui.TestUtil.WEBDAV;
|
|
||||||
import static org.cryptomator.presentation.ui.TestUtil.isToastDisplayed;
|
|
||||||
import static org.cryptomator.presentation.ui.activity.BasicNodeOperationsUtil.checkEmptyFolderHint;
|
|
||||||
import static org.cryptomator.presentation.ui.activity.BasicNodeOperationsUtil.checkFileOrFolderAlreadyExistsErrorMessage;
|
|
||||||
import static org.cryptomator.presentation.ui.activity.BasicNodeOperationsUtil.openSettings;
|
|
||||||
import static org.cryptomator.presentation.ui.activity.BasicNodeOperationsUtil.withRecyclerView;
|
|
||||||
import static org.hamcrest.Matchers.allOf;
|
|
||||||
|
|
||||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
|
||||||
@RunWith(Parameterized.class)
|
|
||||||
public class FolderOperationsTest {
|
|
||||||
|
|
||||||
private final UiDevice device = UiDevice.getInstance(getInstrumentation());
|
|
||||||
private final Context context = InstrumentationRegistry.getTargetContext();
|
|
||||||
private final Integer cloudId;
|
|
||||||
@Rule
|
|
||||||
public ActivityTestRule<SplashActivity> activityTestRule //
|
|
||||||
= new ActivityTestRule<>(SplashActivity.class);
|
|
||||||
|
|
||||||
public FolderOperationsTest(Integer cloudId, String cloudName) {
|
|
||||||
this.cloudId = cloudId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Parameterized.Parameters(name = "{1}")
|
|
||||||
public static Iterable<Object[]> data() {
|
|
||||||
return Arrays.asList(new Object[][] {{DROPBOX, "DROPBOX"}, {GOOGLE_DRIVE, "GOOGLE_DRIVE"}, {ONEDRIVE, "ONEDRIVE"}, {WEBDAV, "WEBDAV"}, {LOCAL, "LOCAL"}});
|
|
||||||
}
|
|
||||||
|
|
||||||
static void openFolder(int nodePosition) {
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(withRecyclerView(R.id.recyclerView) //
|
|
||||||
.atPositionOnView(nodePosition, R.id.cloudFolderText)) //
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test00CreateFolderLeadsToFolderInVault() {
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(withId(R.id.recyclerView)) //
|
|
||||||
.perform(actionOnItemAtPosition(cloudId, click()));
|
|
||||||
|
|
||||||
String folderName = "testFolder";
|
|
||||||
createFolder(folderName);
|
|
||||||
|
|
||||||
checkFolderCreationResult(folderName, "/0/testVault", 0);
|
|
||||||
pressBack();
|
|
||||||
|
|
||||||
folderName = "testFolder1";
|
|
||||||
createFolder(folderName);
|
|
||||||
|
|
||||||
checkFolderCreationResult(folderName, "/0/testVault", 1);
|
|
||||||
|
|
||||||
pressBack();
|
|
||||||
pressBack();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test01CreateExistingFolderLeadsToNoNewFolderInVault() {
|
|
||||||
String folderName = "testFolder";
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(withId(R.id.recyclerView)) //
|
|
||||||
.perform(actionOnItemAtPosition(cloudId, click()));
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
createFolder(folderName);
|
|
||||||
|
|
||||||
checkFileOrFolderAlreadyExistsErrorMessage(folderName);
|
|
||||||
|
|
||||||
onView(withId(android.R.id.button2)) //
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
pressBack();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test02OpenFolderLeadsToOpenFolder() {
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(withId(R.id.recyclerView)) //
|
|
||||||
.perform(actionOnItemAtPosition(cloudId, click()));
|
|
||||||
|
|
||||||
openFolder(0);
|
|
||||||
|
|
||||||
checkEmptyFolderHint();
|
|
||||||
|
|
||||||
pressBack();
|
|
||||||
pressBack();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test03RenameFolderLeadsToFolderWithNewName() {
|
|
||||||
String newFolderName = "testFolder2";
|
|
||||||
int nodePosition = 1;
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(withId(R.id.recyclerView)) //
|
|
||||||
.perform(actionOnItemAtPosition(cloudId, click()));
|
|
||||||
|
|
||||||
renameFolderTo(newFolderName, nodePosition);
|
|
||||||
|
|
||||||
checkFolderDisplayText(newFolderName, nodePosition);
|
|
||||||
|
|
||||||
pressBack();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test04RenameFolderToAlreadyExistFolderLeadsToSameFolderName() {
|
|
||||||
String newFolderName = "testFolder";
|
|
||||||
int nodePosition = 1;
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(withId(R.id.recyclerView)) //
|
|
||||||
.perform(actionOnItemAtPosition(cloudId, click()));
|
|
||||||
|
|
||||||
renameFolderTo(newFolderName, nodePosition);
|
|
||||||
|
|
||||||
checkFileOrFolderAlreadyExistsErrorMessage(newFolderName);
|
|
||||||
|
|
||||||
onView(withId(android.R.id.button2)) //
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
pressBack();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test05MoveFolderLeadsToFolderWithNewLocation() {
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(withId(R.id.recyclerView)) //
|
|
||||||
.perform(actionOnItemAtPosition(cloudId, click()));
|
|
||||||
|
|
||||||
openSettings(device, 1);
|
|
||||||
|
|
||||||
openMoveFolder();
|
|
||||||
|
|
||||||
openFolder(0);
|
|
||||||
|
|
||||||
onView(withId(R.id.chooseLocationButton)) //
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
openFolder(0);
|
|
||||||
|
|
||||||
checkFolderDisplayText("testFolder2", 0);
|
|
||||||
|
|
||||||
openFolder(0);
|
|
||||||
|
|
||||||
checkEmptyFolderHint();
|
|
||||||
|
|
||||||
pressBack();
|
|
||||||
pressBack();
|
|
||||||
pressBack();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test06MoveFolderToAlreadyExistingFolderLeadsToErrorMessage() {
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(withId(R.id.recyclerView)) //
|
|
||||||
.perform(actionOnItemAtPosition(cloudId, click()));
|
|
||||||
|
|
||||||
openSettings(device, 0);
|
|
||||||
|
|
||||||
openMoveFolder();
|
|
||||||
|
|
||||||
onView(withId(R.id.chooseLocationButton)) //
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
isToastDisplayed( //
|
|
||||||
context.getString(R.string.error_file_or_folder_exists), //
|
|
||||||
activityTestRule);
|
|
||||||
|
|
||||||
pressBack();
|
|
||||||
pressBack();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test07DeleteFolderLeadsToRemovedFolder() {
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(withId(R.id.recyclerView)) //
|
|
||||||
.perform(actionOnItemAtPosition(cloudId, click()));
|
|
||||||
|
|
||||||
openFolder(0);
|
|
||||||
|
|
||||||
openSettings(device, 0);
|
|
||||||
|
|
||||||
onView(withId(R.id.delete_folder)) //
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(withId(android.R.id.button1)) //
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
checkEmptyFolderHint();
|
|
||||||
|
|
||||||
pressBack();
|
|
||||||
pressBack();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void openMoveFolder() {
|
|
||||||
onView(withId(R.id.move_folder)) //
|
|
||||||
.perform(click());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createFolder(String name) {
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
onView(withId(R.id.floatingActionButton)) //
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
onView(withId(R.id.create_new_folder)) //
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
onView(withId(R.id.et_folder_name)) //
|
|
||||||
.perform(replaceText(name), closeSoftKeyboard());
|
|
||||||
|
|
||||||
onView(allOf( //
|
|
||||||
withId(android.R.id.button1), //
|
|
||||||
withText(R.string.screen_enter_vault_name_button_text))) //
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkFolderCreationResult(String folderName, String path, int position) {
|
|
||||||
checkFolderDisplayText(folderName, position);
|
|
||||||
|
|
||||||
openSettings(device, position);
|
|
||||||
|
|
||||||
onView(allOf( //
|
|
||||||
withId(R.id.tv_folder_path), //
|
|
||||||
withText(path))) //
|
|
||||||
.check(matches(withText(path)));
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void renameFolderTo(String name, int nodePosition) {
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
openSettings(device, nodePosition);
|
|
||||||
|
|
||||||
onView(withId(R.id.change_cloud)) //
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
onView(withId(R.id.et_rename)) //
|
|
||||||
.perform(replaceText(name), closeSoftKeyboard());
|
|
||||||
|
|
||||||
onView(allOf( //
|
|
||||||
withId(android.R.id.button1), //
|
|
||||||
withText(R.string.dialog_rename_node_positive_button))) //
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkFolderDisplayText(String assertNodeText, int nodePosition) {
|
|
||||||
onView(withRecyclerView(R.id.recyclerView) //
|
|
||||||
.atPositionOnView(nodePosition, R.id.cloudFolderText)) //
|
|
||||||
.check(matches(withText(assertNodeText)));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,80 +0,0 @@
|
|||||||
package org.cryptomator.presentation.ui.activity;
|
|
||||||
|
|
||||||
import androidx.test.uiautomator.UiDevice;
|
|
||||||
import androidx.test.uiautomator.UiObjectNotFoundException;
|
|
||||||
import androidx.test.uiautomator.UiSelector;
|
|
||||||
|
|
||||||
import org.cryptomator.presentation.R;
|
|
||||||
|
|
||||||
import static androidx.test.espresso.Espresso.onView;
|
|
||||||
import static androidx.test.espresso.action.ViewActions.click;
|
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
|
||||||
import static org.cryptomator.domain.executor.BackgroundTasks.awaitCompleted;
|
|
||||||
import static org.cryptomator.presentation.ui.TestUtil.chooseSdCard;
|
|
||||||
import static org.cryptomator.presentation.ui.activity.BasicNodeOperationsUtil.withRecyclerView;
|
|
||||||
|
|
||||||
public class LoginLocalClouds {
|
|
||||||
|
|
||||||
private static UiDevice device;
|
|
||||||
|
|
||||||
public static void loginLocalClouds(UiDevice uiDevice) {
|
|
||||||
device = uiDevice;
|
|
||||||
String folderName = "0";
|
|
||||||
createNewStorageAccessCloudCloud(folderName);
|
|
||||||
awaitCompleted();
|
|
||||||
checkResult(folderName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void createNewStorageAccessCloudCloud(String folderName) {
|
|
||||||
onView(withId(R.id.floating_action_button)) //
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
chooseSdCard(device);
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
openFolder0();
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
chooseFolder(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void openFolder0() {
|
|
||||||
try {
|
|
||||||
final UiSelector docList = new UiSelector() //
|
|
||||||
.resourceId("com.android.documentsui:id/container_directory") //
|
|
||||||
.childSelector( //
|
|
||||||
new UiSelector() //
|
|
||||||
.resourceId("com.android.documentsui:id/dir_list"));
|
|
||||||
|
|
||||||
device //
|
|
||||||
.findObject(docList.childSelector(new UiSelector().text("0"))) //
|
|
||||||
.click();
|
|
||||||
} catch (UiObjectNotFoundException e) {
|
|
||||||
throw new AssertionError("Folder 0 not found");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void chooseFolder(UiDevice device) {
|
|
||||||
try {
|
|
||||||
|
|
||||||
final UiSelector docList = new UiSelector() //
|
|
||||||
.resourceId("com.android.documentsui:id/container_save");
|
|
||||||
|
|
||||||
device //
|
|
||||||
.findObject(docList.childSelector(new UiSelector().resourceId("android:id/button1"))) //
|
|
||||||
.click();
|
|
||||||
} catch (UiObjectNotFoundException e) {
|
|
||||||
throw new AssertionError("Folder 0 not found");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void checkResult(String folderName) {
|
|
||||||
onView(withRecyclerView(R.id.recyclerView) //
|
|
||||||
.atPositionOnView(0, R.id.cloudText)) //
|
|
||||||
.perform(click());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,168 +0,0 @@
|
|||||||
package org.cryptomator.presentation.ui.activity;
|
|
||||||
|
|
||||||
import org.cryptomator.presentation.R;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static androidx.test.espresso.Espresso.onView;
|
|
||||||
import static androidx.test.espresso.action.ViewActions.click;
|
|
||||||
import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard;
|
|
||||||
import static androidx.test.espresso.action.ViewActions.pressBack;
|
|
||||||
import static androidx.test.espresso.action.ViewActions.replaceText;
|
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
|
||||||
import static org.cryptomator.domain.executor.BackgroundTasks.awaitCompleted;
|
|
||||||
import static org.cryptomator.presentation.ui.activity.LoginWebdavClouds.WebdavCloudCredentials.notSpecialWebdavClouds;
|
|
||||||
import static org.hamcrest.Matchers.allOf;
|
|
||||||
|
|
||||||
class LoginWebdavClouds {
|
|
||||||
|
|
||||||
private static final String localUrl = "192.168.0.108";
|
|
||||||
|
|
||||||
static void loginWebdavClouds(SplashActivity activity) {
|
|
||||||
loginStandardWebdavClouds();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* loginAuthenticationFailWebdavCloud(activity);
|
|
||||||
*
|
|
||||||
* loginSelfSignedWebdavCloud();
|
|
||||||
*
|
|
||||||
* loginRedirectToHttpsWebdavCloud();
|
|
||||||
*
|
|
||||||
* loginRedirectToUrlWebdavCloud();
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void loginStandardWebdavClouds() {
|
|
||||||
for (WebdavCloudCredentials webdavCloudCredential : notSpecialWebdavClouds()) {
|
|
||||||
createNewCloud();
|
|
||||||
|
|
||||||
enterCredentials(webdavCloudCredential);
|
|
||||||
|
|
||||||
startLoginProcess();
|
|
||||||
|
|
||||||
awaitCompleted();
|
|
||||||
|
|
||||||
checkResult(webdavCloudCredential);
|
|
||||||
}
|
|
||||||
|
|
||||||
pressBack();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void createNewCloud() {
|
|
||||||
onView(withId(R.id.floating_action_button)) //
|
|
||||||
.perform(click());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void startLoginProcess() {
|
|
||||||
onView(withId(R.id.createCloudButton)) //
|
|
||||||
.perform(click());
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* private static void loginAuthenticationFailWebdavCloud(SplashActivity activity) {
|
|
||||||
* createNewCloud();
|
|
||||||
* enterCredentials(AUTHENTICATION_FAIL);
|
|
||||||
* startLoginProcess();
|
|
||||||
*
|
|
||||||
* onView(withText(R.string.error_authentication_failed)) //
|
|
||||||
* .inRoot(withDecorView(not(is(activity.getWindow().getDecorView())))) //
|
|
||||||
* .check(matches(isDisplayed()));
|
|
||||||
*
|
|
||||||
* onView(allOf( //
|
|
||||||
* withId(R.id.et_url_port), //
|
|
||||||
* withText(AUTHENTICATION_FAIL.url)));
|
|
||||||
*
|
|
||||||
* onView(allOf( //
|
|
||||||
* withId(R.id.et_user), //
|
|
||||||
* withText(AUTHENTICATION_FAIL.username)));
|
|
||||||
*
|
|
||||||
* onView(allOf( //
|
|
||||||
* withId(R.id.et_password), //
|
|
||||||
* withText(AUTHENTICATION_FAIL.password)));
|
|
||||||
*
|
|
||||||
* pressBack();
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* private static void loginSelfSignedWebdavCloud() {
|
|
||||||
* createNewCloud();
|
|
||||||
* enterCredentials(SELF_SIGNED_HTTPS);
|
|
||||||
* startLoginProcess();
|
|
||||||
* clickOk();
|
|
||||||
* startLoginProcess();
|
|
||||||
* checkResult(SELF_SIGNED_HTTPS);
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* private static void loginRedirectToHttpsWebdavCloud() {
|
|
||||||
* createNewCloud();
|
|
||||||
* enterCredentials(REDIRECT_TO_HTTPS);
|
|
||||||
* startLoginProcess();
|
|
||||||
* clickOk();
|
|
||||||
* clickOk();
|
|
||||||
* startLoginProcess();
|
|
||||||
* checkResult(REDIRECT_TO_HTTPS);
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* private static void loginRedirectToUrlWebdavCloud() {
|
|
||||||
* createNewCloud();
|
|
||||||
* enterCredentials(REDIRECT_TO_URL);
|
|
||||||
* startLoginProcess();
|
|
||||||
* clickOk();
|
|
||||||
* startLoginProcess();
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
|
|
||||||
private static void enterCredentials(WebdavCloudCredentials webdavCloudCredential) {
|
|
||||||
onView(withId(R.id.urlPortEditText)) //
|
|
||||||
.perform(replaceText(webdavCloudCredential.url));
|
|
||||||
|
|
||||||
onView(withId(R.id.userNameEditText)) //
|
|
||||||
.perform(replaceText(webdavCloudCredential.username));
|
|
||||||
|
|
||||||
onView(withId(R.id.passwordEditText)) //
|
|
||||||
.perform( //
|
|
||||||
replaceText(webdavCloudCredential.password), //
|
|
||||||
closeSoftKeyboard());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void checkResult(WebdavCloudCredentials webdavCloudCredential) {
|
|
||||||
onView(allOf( //
|
|
||||||
withId(R.id.cloudText), //
|
|
||||||
withText(webdavCloudCredential.displayUrl)));
|
|
||||||
|
|
||||||
onView(allOf( //
|
|
||||||
withId(R.id.cloudSubText), //
|
|
||||||
withText(webdavCloudCredential.username + " • ")));
|
|
||||||
}
|
|
||||||
|
|
||||||
enum WebdavCloudCredentials {
|
|
||||||
GMX("https://webdav.mc.gmx.net/", "webdav.mc.gmx.net", "jraufelder@gmx.de", "mG7!3B3Mx"), //
|
|
||||||
/*
|
|
||||||
* FREENET("https://webmail.freenet.de/webdav", "webmail.freenet.de", "milestone@freenet.de", "rF7!3B3Et")
|
|
||||||
*
|
|
||||||
* , //
|
|
||||||
* /*
|
|
||||||
* AUTHENTICATION_FAIL("https://webdav.mc.gmx.net/", "webdav.mc.gmx.net", "bla@bla.de", "bla"), //
|
|
||||||
* SELF_SIGNED_HTTPS("https://" + localUrl + "/webdav", localUrl + "/webdav", "bla@bla.de", "bla"), //
|
|
||||||
* REDIRECT_TO_HTTPS("http://" + localUrl + "/webdav", localUrl + "/webdav", "bla@bla.de", "bla"), //
|
|
||||||
* REDIRECT_TO_URL("https://" + localUrl + "/bar/baz", localUrl + "/bar/baz", "bla@bla.de", "bla")
|
|
||||||
*/;
|
|
||||||
|
|
||||||
private final String url;
|
|
||||||
private final String displayUrl;
|
|
||||||
private final String username;
|
|
||||||
private final String password;
|
|
||||||
|
|
||||||
WebdavCloudCredentials(String url, String displayUrl, String username, String password) {
|
|
||||||
this.url = url;
|
|
||||||
this.displayUrl = displayUrl;
|
|
||||||
this.username = username;
|
|
||||||
this.password = password;
|
|
||||||
}
|
|
||||||
|
|
||||||
static List<WebdavCloudCredentials> notSpecialWebdavClouds() {
|
|
||||||
return Collections.singletonList(GMX/* , FREENET */);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user