Merge branch 'release/1.7.0'
This commit is contained in:
commit
d0a3a05afb
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,6 +1,3 @@
|
||||
[submodule "msa-auth-for-android"]
|
||||
path = lib/msa-auth-for-android
|
||||
url = https://github.com/SailReal/msa-auth-for-android.git
|
||||
[submodule "subsampling-scale-image-view"]
|
||||
path = lib/subsampling-scale-image-view
|
||||
url = https://github.com/SailReal/subsampling-scale-image-view.git
|
||||
|
13
.idea/misc.xml
generated
13
.idea/misc.xml
generated
@ -1,5 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DesignSurface">
|
||||
<option name="filePathToZoomLevelMap">
|
||||
<map>
|
||||
<entry key="presentation/src/main/res/drawable/ic_lock.xml" value="0.1962962962962963" />
|
||||
<entry key="presentation/src/main/res/layout/dialog_app_update.xml" value="0.36614583333333334" />
|
||||
</map>
|
||||
</option>
|
||||
</component>
|
||||
<component name="NullableNotNullManager">
|
||||
<option name="myDefaultNullable" value="android.support.annotation.Nullable" />
|
||||
<option name="myDefaultNotNull" value="org.jetbrains.annotations.NotNull" />
|
||||
@ -26,7 +34,7 @@
|
||||
</option>
|
||||
<option name="myNotNulls">
|
||||
<value>
|
||||
<list size="14">
|
||||
<list size="15">
|
||||
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
|
||||
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
|
||||
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
|
||||
@ -41,11 +49,12 @@
|
||||
<item index="11" class="java.lang.String" itemvalue="org.eclipse.jdt.annotation.NonNull" />
|
||||
<item index="12" class="java.lang.String" itemvalue="io.reactivex.annotations.NonNull" />
|
||||
<item index="13" class="java.lang.String" itemvalue="io.reactivex.rxjava3.annotations.NonNull" />
|
||||
<item index="14" class="java.lang.String" itemvalue="lombok.NonNull" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="false" project-jdk-name="JDK" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="JDK" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
|
3
.idea/vcs.xml
generated
3
.idea/vcs.xml
generated
@ -2,8 +2,7 @@
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/lib/msa-auth-for-android" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/lib/pcloud-sdk-java" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/lib/subsampling-scale-image-view" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
</project>
|
54
Gemfile.lock
54
Gemfile.lock
@ -10,24 +10,24 @@ GEM
|
||||
artifactory (3.0.15)
|
||||
atomos (0.1.3)
|
||||
aws-eventstream (1.2.0)
|
||||
aws-partitions (1.535.0)
|
||||
aws-sdk-core (3.123.0)
|
||||
aws-partitions (1.554.0)
|
||||
aws-sdk-core (3.126.1)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
aws-partitions (~> 1, >= 1.525.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
jmespath (~> 1.0)
|
||||
aws-sdk-kms (1.51.0)
|
||||
aws-sdk-core (~> 3, >= 3.122.0)
|
||||
aws-sdk-kms (1.54.0)
|
||||
aws-sdk-core (~> 3, >= 3.126.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-s3 (1.107.0)
|
||||
aws-sdk-core (~> 3, >= 3.122.0)
|
||||
aws-sdk-s3 (1.112.0)
|
||||
aws-sdk-core (~> 3, >= 3.126.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.4)
|
||||
aws-sigv4 (1.4.0)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
babosa (1.0.4)
|
||||
bcrypt_pbkdf (1.1.0)
|
||||
claide (1.0.3)
|
||||
claide (1.1.0)
|
||||
colored (1.2)
|
||||
colored2 (3.1.2)
|
||||
commander (4.6.0)
|
||||
@ -38,19 +38,20 @@ GEM
|
||||
domain_name (0.5.20190701)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
dotenv (2.7.6)
|
||||
ed25519 (1.2.4)
|
||||
ed25519 (1.3.0)
|
||||
emoji_regex (3.2.3)
|
||||
excon (0.88.0)
|
||||
faraday (1.8.0)
|
||||
excon (0.91.0)
|
||||
faraday (1.9.3)
|
||||
faraday-em_http (~> 1.0)
|
||||
faraday-em_synchrony (~> 1.0)
|
||||
faraday-excon (~> 1.1)
|
||||
faraday-httpclient (~> 1.0.1)
|
||||
faraday-httpclient (~> 1.0)
|
||||
faraday-multipart (~> 1.0)
|
||||
faraday-net_http (~> 1.0)
|
||||
faraday-net_http_persistent (~> 1.1)
|
||||
faraday-net_http_persistent (~> 1.0)
|
||||
faraday-patron (~> 1.0)
|
||||
faraday-rack (~> 1.0)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
faraday-retry (~> 1.0)
|
||||
ruby2_keywords (>= 0.0.4)
|
||||
faraday-cookie_jar (0.0.7)
|
||||
faraday (>= 0.8.0)
|
||||
@ -59,14 +60,17 @@ GEM
|
||||
faraday-em_synchrony (1.0.0)
|
||||
faraday-excon (1.1.0)
|
||||
faraday-httpclient (1.0.1)
|
||||
faraday-multipart (1.0.3)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
faraday-net_http (1.0.1)
|
||||
faraday-net_http_persistent (1.2.0)
|
||||
faraday-patron (1.0.0)
|
||||
faraday-rack (1.0.0)
|
||||
faraday-retry (1.0.3)
|
||||
faraday_middleware (1.2.0)
|
||||
faraday (~> 1.0)
|
||||
fastimage (2.2.5)
|
||||
fastlane (2.198.1)
|
||||
fastimage (2.2.6)
|
||||
fastlane (2.204.3)
|
||||
CFPropertyList (>= 2.3, < 4.0.0)
|
||||
addressable (>= 2.8, < 3.0.0)
|
||||
artifactory (~> 3.0)
|
||||
@ -111,9 +115,9 @@ GEM
|
||||
mime-types (~> 3.3)
|
||||
fastlane-plugin-get_version_name (0.2.2)
|
||||
gh_inspector (1.1.3)
|
||||
google-apis-androidpublisher_v3 (0.13.0)
|
||||
google-apis-androidpublisher_v3 (0.16.0)
|
||||
google-apis-core (>= 0.4, < 2.a)
|
||||
google-apis-core (0.4.1)
|
||||
google-apis-core (0.4.2)
|
||||
addressable (~> 2.5, >= 2.5.1)
|
||||
googleauth (>= 0.16.2, < 2.a)
|
||||
httpclient (>= 2.8.1, < 3.a)
|
||||
@ -122,11 +126,11 @@ GEM
|
||||
retriable (>= 2.0, < 4.a)
|
||||
rexml
|
||||
webrick
|
||||
google-apis-iamcredentials_v1 (0.8.0)
|
||||
google-apis-iamcredentials_v1 (0.10.0)
|
||||
google-apis-core (>= 0.4, < 2.a)
|
||||
google-apis-playcustomapp_v1 (0.6.0)
|
||||
google-apis-playcustomapp_v1 (0.7.0)
|
||||
google-apis-core (>= 0.4, < 2.a)
|
||||
google-apis-storage_v1 (0.9.0)
|
||||
google-apis-storage_v1 (0.11.0)
|
||||
google-apis-core (>= 0.4, < 2.a)
|
||||
google-cloud-core (1.6.0)
|
||||
google-cloud-env (~> 1.0)
|
||||
@ -134,15 +138,15 @@ GEM
|
||||
google-cloud-env (1.5.0)
|
||||
faraday (>= 0.17.3, < 2.0)
|
||||
google-cloud-errors (1.2.0)
|
||||
google-cloud-storage (1.34.1)
|
||||
addressable (~> 2.5)
|
||||
google-cloud-storage (1.36.1)
|
||||
addressable (~> 2.8)
|
||||
digest-crc (~> 0.4)
|
||||
google-apis-iamcredentials_v1 (~> 0.1)
|
||||
google-apis-storage_v1 (~> 0.1)
|
||||
google-cloud-core (~> 1.6)
|
||||
googleauth (>= 0.16.2, < 2.a)
|
||||
mini_mime (~> 1.0)
|
||||
googleauth (1.1.0)
|
||||
googleauth (1.1.1)
|
||||
faraday (>= 0.17.3, < 2.0)
|
||||
jwt (>= 1.4, < 3.0)
|
||||
memoist (~> 0.16)
|
||||
@ -153,13 +157,13 @@ GEM
|
||||
http-cookie (1.0.4)
|
||||
domain_name (~> 0.5)
|
||||
httpclient (2.8.3)
|
||||
jmespath (1.4.0)
|
||||
jmespath (1.6.0)
|
||||
json (2.6.1)
|
||||
jwt (2.3.0)
|
||||
memoist (0.16.2)
|
||||
mime-types (3.4.1)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2021.1115)
|
||||
mime-types-data (3.2022.0105)
|
||||
mini_magick (4.11.0)
|
||||
mini_mime (1.1.2)
|
||||
multi_json (1.15.0)
|
||||
|
@ -2,13 +2,13 @@ apply from: 'buildsystem/dependencies.gradle'
|
||||
apply plugin: "com.vanniktech.android.junit.jacoco"
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.6.0'
|
||||
ext.kotlin_version = '1.6.10'
|
||||
repositories {
|
||||
mavenCentral()
|
||||
google()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.0.3'
|
||||
classpath 'com.android.tools.build:gradle:7.1.1'
|
||||
classpath 'org.greenrobot:greendao-gradle-plugin:3.3.0'
|
||||
classpath 'com.vanniktech:gradle-android-junit-jacoco-plugin:0.16.0'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
@ -39,16 +39,13 @@ allprojects {
|
||||
ext {
|
||||
androidApplicationId = 'org.cryptomator'
|
||||
androidVersionCode = getVersionCode()
|
||||
androidVersionName = '1.6.8'
|
||||
androidVersionName = '1.7.0'
|
||||
}
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven {
|
||||
url "https://maven.google.com"
|
||||
}
|
||||
flatDir {
|
||||
dirs '../libs'
|
||||
}
|
||||
google()
|
||||
}
|
||||
}
|
||||
|
@ -2,12 +2,25 @@ allprojects {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven { url 'https://jitpack.io' }
|
||||
// needed for 'com.microsoft.device.display' required by 'com.microsoft.graph:microsoft-graph'
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven {
|
||||
url 'https://pkgs.dev.azure.com/MicrosoftDeviceSDK/DuoSDK-Public/_packaging/Duo-SDK-Feed/maven/v1'
|
||||
name 'Duo-SDK-Feed'
|
||||
}
|
||||
}
|
||||
filter {
|
||||
// this repository *only* contains artifacts with group "com.microsoft.device.display"
|
||||
includeGroup "com.microsoft.device.display"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ext {
|
||||
androidBuildToolsVersion = "30.0.2"
|
||||
androidMinSdkVersion = 24
|
||||
androidBuildToolsVersion = "30.0.3"
|
||||
androidMinSdkVersion = 26
|
||||
androidTargetSdkVersion = 30
|
||||
androidCompileSdkVersion = 30
|
||||
|
||||
@ -29,11 +42,11 @@ ext {
|
||||
rxAndroidVersion = '2.1.1'
|
||||
rxBindingVersion = '2.2.0'
|
||||
|
||||
daggerVersion = '2.40.2'
|
||||
daggerVersion = '2.40.5'
|
||||
|
||||
gsonVersion = '2.8.9'
|
||||
gsonVersion = '2.9.0'
|
||||
|
||||
okHttpVersion = '4.9.2'
|
||||
okHttpVersion = '4.9.3'
|
||||
okHttpDigestVersion = '2.6'
|
||||
|
||||
velocityVersion = '2.3'
|
||||
@ -52,20 +65,21 @@ ext {
|
||||
// cloud provider libs
|
||||
cryptolibVersion = '2.0.2'
|
||||
|
||||
dropboxVersion = '5.0.0'
|
||||
dropboxVersion = '5.1.1'
|
||||
|
||||
googleApiServicesVersion = 'v3-rev20210919-1.32.1'
|
||||
googleApiServicesVersion = 'v3-rev20220110-1.32.1'
|
||||
googlePlayServicesVersion = '19.2.0'
|
||||
googleClientVersion = '1.32.1' // keep in sync with https://github.com/SailReal/google-http-java-client
|
||||
googleClientVersion = '1.33.2' // 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`,
|
||||
copying `google-http-client-*.jar` and `google-http-client-android-*.jar` into the lib folder of this project
|
||||
*/
|
||||
trackingFreeGoogleCLientVersion = '1.40.1'
|
||||
trackingFreeGoogleCLientVersion = '1.41.4'
|
||||
|
||||
msgraphVersion = '2.10.0'
|
||||
msgraphVersion = '5.14.0'
|
||||
msgraphAuthVersion = '2.2.3'
|
||||
|
||||
minIoVersion = '8.3.3'
|
||||
minIoVersion = '8.3.6'
|
||||
staxVersion = '1.2.0' // needed for minIO
|
||||
|
||||
commonsCodecVersion = '1.15'
|
||||
@ -76,7 +90,7 @@ ext {
|
||||
|
||||
jUnitVersion = '5.8.2'
|
||||
assertJVersion = '1.7.1'
|
||||
mockitoVersion = '4.1.0'
|
||||
mockitoVersion = '4.3.1'
|
||||
mockitoKotlinVersion = '4.0.0'
|
||||
hamcrestVersion = '1.3'
|
||||
dexmakerVersion = '1.0'
|
||||
@ -139,6 +153,7 @@ ext {
|
||||
mockitoInline : "org.mockito:mockito-inline:${mockitoVersion}",
|
||||
mockitoKotlin : "org.mockito.kotlin:mockito-kotlin:${mockitoKotlinVersion}",
|
||||
msgraph : "com.microsoft.graph:microsoft-graph:${msgraphVersion}",
|
||||
msgraphAuth : "com.microsoft.identity.client:msal:${msgraphAuthVersion}",
|
||||
multidex : "androidx.multidex:multidex:${multidexVersion}",
|
||||
okHttp : "com.squareup.okhttp3:okhttp:${okHttpVersion}",
|
||||
okHttpDigest : "io.github.rburgst:okhttp-digest:${okHttpDigestVersion}",
|
||||
|
@ -4,8 +4,6 @@ apply plugin: 'kotlin-android'
|
||||
apply plugin: 'de.mannodermaus.android-junit5'
|
||||
|
||||
android {
|
||||
defaultPublishConfig "debug"
|
||||
|
||||
def globalConfiguration = rootProject.extensions.getByName("ext")
|
||||
|
||||
compileSdkVersion globalConfiguration["androidCompileSdkVersion"]
|
||||
@ -28,11 +26,6 @@ android {
|
||||
coreLibraryDesugaringEnabled true
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
quiet true
|
||||
abortOnError false
|
||||
ignoreWarnings true
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
@ -75,14 +68,21 @@ android {
|
||||
java.srcDirs = ['src/main/java', 'src/main/java/', 'src/foss/java', 'src/foss/java/']
|
||||
}
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
exclude 'META-INF/DEPENDENCIES'
|
||||
resources {
|
||||
excludes += ['META-INF/DEPENDENCIES', 'META-INF/NOTICE.md', 'META-INF/INDEX.LIST']
|
||||
}
|
||||
}
|
||||
|
||||
lint {
|
||||
abortOnError false
|
||||
ignoreWarnings true
|
||||
quiet true
|
||||
}
|
||||
}
|
||||
|
||||
greendao {
|
||||
schemaVersion 10
|
||||
schemaVersion 11
|
||||
}
|
||||
|
||||
configurations.all {
|
||||
@ -95,7 +95,6 @@ dependencies {
|
||||
|
||||
implementation project(':domain')
|
||||
implementation project(':util')
|
||||
implementation project(':msa-auth-for-android')
|
||||
implementation project(':pcloud-sdk-java')
|
||||
|
||||
coreLibraryDesugaring dependencies.coreDesugaring
|
||||
@ -115,6 +114,7 @@ dependencies {
|
||||
|
||||
// cloud
|
||||
implementation dependencies.dropbox
|
||||
implementation dependencies.msgraphAuth
|
||||
implementation dependencies.msgraph
|
||||
|
||||
implementation dependencies.stax
|
||||
|
@ -51,6 +51,7 @@ class UpgradeDatabaseTest {
|
||||
Upgrade7To8().applyTo(db, 7)
|
||||
Upgrade8To9(sharedPreferencesHandler).applyTo(db, 8)
|
||||
Upgrade9To10(sharedPreferencesHandler).applyTo(db, 9)
|
||||
Upgrade10To11().applyTo(db, 10)
|
||||
|
||||
CloudEntityDao(DaoConfig(db, CloudEntityDao::class.java)).loadAll()
|
||||
VaultEntityDao(DaoConfig(db, VaultEntityDao::class.java)).loadAll()
|
||||
@ -470,4 +471,113 @@ class UpgradeDatabaseTest {
|
||||
Assert.assertThat(sharedPreferencesHandler.vaultsRemovedDuringMigration(), CoreMatchers.`is`(Pair("LOCAL", arrayListOf("pathOfVault26"))))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun upgrade10To11EmptyOnedriveCloudRemovesCloud() {
|
||||
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)
|
||||
|
||||
Sql.insertInto("VAULT_ENTITY") //
|
||||
.integer("_id", 25) //
|
||||
.integer("FOLDER_CLOUD_ID", 3) //
|
||||
.text("FOLDER_PATH", "path") //
|
||||
.text("FOLDER_NAME", "name") //
|
||||
.text("CLOUD_TYPE", CloudType.ONEDRIVE.name) //
|
||||
.text("PASSWORD", "password") //
|
||||
.integer("POSITION", 10) //
|
||||
.executeOn(db)
|
||||
|
||||
Sql.query("CLOUD_ENTITY").executeOn(db).use {
|
||||
Assert.assertThat(it.count, CoreMatchers.`is`(3))
|
||||
}
|
||||
|
||||
Upgrade10To11().applyTo(db, 10)
|
||||
|
||||
Sql.query("VAULT_ENTITY").executeOn(db).use {
|
||||
Assert.assertThat(it.count, CoreMatchers.`is`(1))
|
||||
}
|
||||
|
||||
Sql.query("VAULT_ENTITY").executeOn(db).use {
|
||||
it.moveToFirst()
|
||||
Assert.assertThat(it.getString(it.getColumnIndex("FOLDER_CLOUD_ID")), CoreMatchers.`is`("3"))
|
||||
Assert.assertThat(it.getString(it.getColumnIndex("FOLDER_PATH")), CoreMatchers.`is`("path"))
|
||||
Assert.assertThat(it.getString(it.getColumnIndex("FOLDER_NAME")), CoreMatchers.`is`("name"))
|
||||
Assert.assertThat(it.getString(it.getColumnIndex("CLOUD_TYPE")), CoreMatchers.`is`(CloudType.ONEDRIVE.name))
|
||||
Assert.assertThat(it.getString(it.getColumnIndex("PASSWORD")), CoreMatchers.`is`("password"))
|
||||
Assert.assertThat(it.getString(it.getColumnIndex("POSITION")), CoreMatchers.`is`("10"))
|
||||
Assert.assertThat(it.getString(it.getColumnIndex("FORMAT")), CoreMatchers.`is`("8"))
|
||||
Assert.assertThat(it.getString(it.getColumnIndex("SHORTENING_THRESHOLD")), CoreMatchers.`is`("220"))
|
||||
}
|
||||
|
||||
Sql.query("CLOUD_ENTITY").executeOn(db).use {
|
||||
Assert.assertThat(it.count, CoreMatchers.`is`(2))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun upgrade10To11UsedOnedriveCloudPreservesCloud() {
|
||||
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)
|
||||
|
||||
Sql.insertInto("VAULT_ENTITY") //
|
||||
.integer("_id", 25) //
|
||||
.integer("FOLDER_CLOUD_ID", 3) //
|
||||
.text("FOLDER_PATH", "path") //
|
||||
.text("FOLDER_NAME", "name") //
|
||||
.text("CLOUD_TYPE", CloudType.ONEDRIVE.name) //
|
||||
.text("PASSWORD", "password") //
|
||||
.integer("POSITION", 10) //
|
||||
.executeOn(db)
|
||||
|
||||
Sql.query("CLOUD_ENTITY").executeOn(db).use {
|
||||
while (it.moveToNext()) {
|
||||
Sql.update("CLOUD_ENTITY")
|
||||
.where("_id", Sql.eq(3L))
|
||||
.set("ACCESS_TOKEN", Sql.toString("Access token 3000"))
|
||||
.set("USERNAME", Sql.toString("foo@bar.baz"))
|
||||
.executeOn(db)
|
||||
}
|
||||
}
|
||||
Sql.query("CLOUD_ENTITY").executeOn(db).use {
|
||||
Assert.assertThat(it.count, CoreMatchers.`is`(3))
|
||||
}
|
||||
|
||||
Upgrade10To11().applyTo(db, 10)
|
||||
|
||||
Sql.query("VAULT_ENTITY").executeOn(db).use {
|
||||
Assert.assertThat(it.count, CoreMatchers.`is`(1))
|
||||
}
|
||||
|
||||
Sql.query("VAULT_ENTITY").executeOn(db).use {
|
||||
it.moveToFirst()
|
||||
Assert.assertThat(it.getString(it.getColumnIndex("FOLDER_CLOUD_ID")), CoreMatchers.`is`("3"))
|
||||
Assert.assertThat(it.getString(it.getColumnIndex("FOLDER_PATH")), CoreMatchers.`is`("path"))
|
||||
Assert.assertThat(it.getString(it.getColumnIndex("FOLDER_NAME")), CoreMatchers.`is`("name"))
|
||||
Assert.assertThat(it.getString(it.getColumnIndex("CLOUD_TYPE")), CoreMatchers.`is`(CloudType.ONEDRIVE.name))
|
||||
Assert.assertThat(it.getString(it.getColumnIndex("PASSWORD")), CoreMatchers.`is`("password"))
|
||||
Assert.assertThat(it.getString(it.getColumnIndex("POSITION")), CoreMatchers.`is`("10"))
|
||||
Assert.assertThat(it.getString(it.getColumnIndex("FORMAT")), CoreMatchers.`is`("8"))
|
||||
Assert.assertThat(it.getString(it.getColumnIndex("SHORTENING_THRESHOLD")), CoreMatchers.`is`("220"))
|
||||
}
|
||||
|
||||
Sql.query("CLOUD_ENTITY").executeOn(db).use {
|
||||
Assert.assertThat(it.count, CoreMatchers.`is`(3))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -118,11 +118,10 @@ internal class CryptoCloudContentRepository(context: Context, cloudContentReposi
|
||||
}
|
||||
|
||||
cryptoImpl = when (cloud.vault.format) {
|
||||
7 -> CryptoImplVaultFormat7(context, cryptor, cloudContentRepository, vaultLocation, DirIdCacheFormat7())
|
||||
8 -> CryptoImplVaultFormat8(context, cryptor, cloudContentRepository, vaultLocation, DirIdCacheFormat7(), cloud.vault.shorteningThreshold)
|
||||
7 -> CryptoImplVaultFormat7(context, cryptor, cloudContentRepository, vaultLocation, DirIdCacheFormat7())
|
||||
6, 5 -> CryptoImplVaultFormatPre7(context, cryptor, cloudContentRepository, vaultLocation, DirIdCacheFormatPre7())
|
||||
else -> throw IllegalStateException(String.format("No CryptoImpl for vault format %d.", cloud.vault.format))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -78,4 +78,13 @@ public class CryptoCloudContentRepositoryFactory implements CloudContentReposito
|
||||
throw new IllegalStateException(format("Cryptor already registered for vault %s", vault));
|
||||
}
|
||||
}
|
||||
|
||||
public void updateCloudInCryptor(Vault vault, Cloud cloud) {
|
||||
try {
|
||||
Cryptor cryptor = cryptors.get(vault).get();
|
||||
cryptors.replace(vault, Vault.aCopyOf(vault).withCloud(cloud).build(), cryptor);
|
||||
} catch (MissingCryptorException e) {
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,8 @@ abstract class Cryptors internal constructor() {
|
||||
|
||||
abstract fun putIfAbsent(vault: Vault, cryptor: Cryptor): Boolean
|
||||
|
||||
abstract fun replace(old: Vault, updated: Vault, cryptor: Cryptor)
|
||||
|
||||
class Delegating : Cryptors() {
|
||||
|
||||
private val fallback = Default()
|
||||
@ -65,6 +67,10 @@ abstract class Cryptors internal constructor() {
|
||||
return delegate().putIfAbsent(vault, cryptor)
|
||||
}
|
||||
|
||||
override fun replace(old: Vault, updated: Vault, cryptor: Cryptor) {
|
||||
return delegate().replace(old, updated, cryptor)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private fun delegate(): Cryptors {
|
||||
return delegate ?: fallback
|
||||
@ -108,6 +114,11 @@ abstract class Cryptors internal constructor() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun replace(old: Vault, updated: Vault, cryptor: Cryptor) {
|
||||
cryptors.remove(old)
|
||||
cryptors[updated] = cryptor
|
||||
}
|
||||
|
||||
fun setOnChangeListener(onChangeListener: Runnable) {
|
||||
this.onChangeListener = onChangeListener
|
||||
}
|
||||
|
@ -1,25 +0,0 @@
|
||||
package org.cryptomator.data.cloud.onedrive;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.cryptomator.data.BuildConfig;
|
||||
import org.cryptomator.data.cloud.onedrive.graph.MSAAuthAndroidAdapter;
|
||||
|
||||
public class MSAAuthAndroidAdapterImpl extends MSAAuthAndroidAdapter {
|
||||
|
||||
private static final String[] SCOPES = new String[] {"https://graph.microsoft.com/Files.ReadWrite", "offline_access", "openid"};
|
||||
|
||||
public MSAAuthAndroidAdapterImpl(Context context, String refreshToken) {
|
||||
super(context, refreshToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientId() {
|
||||
return BuildConfig.ONEDRIVE_API_KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getScopes() {
|
||||
return SCOPES;
|
||||
}
|
||||
}
|
@ -1,68 +1,58 @@
|
||||
package org.cryptomator.data.cloud.onedrive
|
||||
|
||||
import android.content.Context
|
||||
import com.microsoft.graph.authentication.IAuthenticationProvider
|
||||
import com.microsoft.graph.core.DefaultClientConfig
|
||||
import com.microsoft.graph.models.extensions.IGraphServiceClient
|
||||
import com.microsoft.graph.requests.extensions.GraphServiceClient
|
||||
import com.microsoft.graph.authentication.BaseAuthenticationProvider
|
||||
import com.microsoft.graph.httpcore.HttpClients
|
||||
import com.microsoft.graph.requests.GraphServiceClient
|
||||
import org.cryptomator.data.cloud.okhttplogging.HttpLoggingInterceptor
|
||||
import org.cryptomator.data.cloud.onedrive.graph.MSAAuthAndroidAdapter
|
||||
import org.cryptomator.data.util.NetworkTimeout
|
||||
import org.cryptomator.util.SharedPreferencesHandler
|
||||
import org.cryptomator.util.crypto.CredentialCryptor
|
||||
import java.net.URL
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import timber.log.Timber
|
||||
|
||||
|
||||
class OnedriveClientFactory private constructor() {
|
||||
|
||||
companion object {
|
||||
|
||||
@Volatile
|
||||
private var instance: IGraphServiceClient? = null
|
||||
fun createInstance(context: Context, encryptedToken: String, sharedPreferencesHandler: SharedPreferencesHandler): GraphServiceClient<Request> {
|
||||
val tokenAuthenticationProvider = object : BaseAuthenticationProvider() {
|
||||
val token = CompletableFuture.completedFuture(CredentialCryptor.getInstance(context).decrypt(encryptedToken))
|
||||
override fun getAuthorizationTokenAsync(requestUrl: URL): CompletableFuture<String> {
|
||||
return if (shouldAuthenticateRequestWithUrl(requestUrl)) {
|
||||
token
|
||||
} else {
|
||||
CompletableFuture.completedFuture(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Volatile
|
||||
private var authenticationAdapter: MSAAuthAndroidAdapter? = null
|
||||
|
||||
@Synchronized
|
||||
fun getInstance(context: Context, refreshToken: String?): IGraphServiceClient = instance ?: createClient(context, refreshToken).also { instance = it }
|
||||
|
||||
@Synchronized
|
||||
fun getAuthAdapter(context: Context, refreshToken: String?): MSAAuthAndroidAdapter = authenticationAdapter ?: MSAAuthAndroidAdapterImpl(context, refreshToken).also { authenticationAdapter = it }
|
||||
|
||||
private fun createClient(context: Context, refreshToken: String?): IGraphServiceClient {
|
||||
val builder = OkHttpClient() //
|
||||
.newBuilder() //
|
||||
val httpClient = HttpClients.createDefault(tokenAuthenticationProvider)
|
||||
.newBuilder()
|
||||
.connectTimeout(NetworkTimeout.CONNECTION.timeout, NetworkTimeout.CONNECTION.unit) //
|
||||
.readTimeout(NetworkTimeout.READ.timeout, NetworkTimeout.READ.unit) //
|
||||
.writeTimeout(NetworkTimeout.WRITE.timeout, NetworkTimeout.WRITE.unit) //
|
||||
.addInterceptor(httpLoggingInterceptor(context))
|
||||
|
||||
val onedriveHttpProvider = OnedriveHttpProvider(object : DefaultClientConfig() {
|
||||
override fun getAuthenticationProvider(): IAuthenticationProvider {
|
||||
return getAuthAdapter(context, refreshToken)
|
||||
}
|
||||
}, builder.build())
|
||||
.addInterceptor(httpLoggingInterceptor(context)) //
|
||||
.build();
|
||||
|
||||
return GraphServiceClient //
|
||||
.builder() //
|
||||
.authenticationProvider(authenticationAdapter) //
|
||||
.httpProvider(onedriveHttpProvider) //
|
||||
.httpClient(httpClient) //
|
||||
.authenticationProvider(tokenAuthenticationProvider) //
|
||||
.buildClient()
|
||||
}
|
||||
|
||||
|
||||
private fun httpLoggingInterceptor(context: Context): Interceptor {
|
||||
val logger = object : HttpLoggingInterceptor.Logger {
|
||||
override fun log(message: String) {
|
||||
Timber.tag("OkHttp").d(message)
|
||||
}
|
||||
}
|
||||
|
||||
return HttpLoggingInterceptor(logger, context)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun logout() {
|
||||
instance = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,10 @@ package org.cryptomator.data.cloud.onedrive
|
||||
|
||||
import android.content.Context
|
||||
import com.microsoft.graph.core.GraphErrorCodes
|
||||
import com.microsoft.graph.http.GraphServiceException
|
||||
import com.microsoft.graph.requests.GraphServiceClient
|
||||
import com.microsoft.identity.common.exception.ClientException
|
||||
import org.cryptomator.data.cloud.InterceptingCloudContentRepository
|
||||
import org.cryptomator.data.cloud.onedrive.graph.ClientException
|
||||
import org.cryptomator.domain.OnedriveCloud
|
||||
import org.cryptomator.domain.exception.BackendException
|
||||
import org.cryptomator.domain.exception.FatalBackendException
|
||||
@ -20,8 +22,10 @@ import java.io.File
|
||||
import java.io.IOException
|
||||
import java.io.OutputStream
|
||||
import java.net.SocketTimeoutException
|
||||
import okhttp3.Request
|
||||
|
||||
internal class OnedriveCloudContentRepository(private val cloud: OnedriveCloud, context: Context) : InterceptingCloudContentRepository<OnedriveCloud, OnedriveNode, OnedriveFolder, OnedriveFile>(Intercepted(cloud, context)) {
|
||||
internal class OnedriveCloudContentRepository(private val cloud: OnedriveCloud, context: Context, graphServiceClient: GraphServiceClient<Request>)
|
||||
: InterceptingCloudContentRepository<OnedriveCloud, OnedriveNode, OnedriveFolder, OnedriveFile>(Intercepted(cloud, context, graphServiceClient)) {
|
||||
|
||||
@Throws(BackendException::class)
|
||||
override fun throwWrappedIfRequired(e: Exception) {
|
||||
@ -44,13 +48,14 @@ internal class OnedriveCloudContentRepository(private val cloud: OnedriveCloud,
|
||||
|
||||
private fun isAuthenticationError(e: Throwable?): Boolean {
|
||||
return (e != null //
|
||||
&& (e is ClientException && e.errorCode() == GraphErrorCodes.AUTHENTICATION_FAILURE //
|
||||
&& (e is ClientException && e.errorCode == GraphErrorCodes.AUTHENTICATION_FAILURE.name //
|
||||
|| e is GraphServiceException && e.serviceError?.code?.equals("InvalidAuthenticationToken") == true
|
||||
|| isAuthenticationError(e.cause)))
|
||||
}
|
||||
|
||||
private class Intercepted(cloud: OnedriveCloud, context: Context) : CloudContentRepository<OnedriveCloud, OnedriveNode, OnedriveFolder, OnedriveFile> {
|
||||
private class Intercepted(cloud: OnedriveCloud, context: Context, graphServiceClient: GraphServiceClient<Request>) : CloudContentRepository<OnedriveCloud, OnedriveNode, OnedriveFolder, OnedriveFile> {
|
||||
|
||||
private val oneDriveImpl: OnedriveImpl = OnedriveImpl(cloud, context, OnedriveIdCache())
|
||||
private val oneDriveImpl: OnedriveImpl = OnedriveImpl(cloud, context, graphServiceClient, OnedriveIdCache())
|
||||
|
||||
override fun root(cloud: OnedriveCloud): OnedriveFolder {
|
||||
return oneDriveImpl.root()
|
||||
@ -141,7 +146,7 @@ internal class OnedriveCloudContentRepository(private val cloud: OnedriveCloud,
|
||||
|
||||
@Throws(BackendException::class)
|
||||
override fun checkAuthenticationAndRetrieveCurrentAccount(cloud: OnedriveCloud): String {
|
||||
return oneDriveImpl.currentAccount()
|
||||
return oneDriveImpl.currentAccount(cloud.username())
|
||||
}
|
||||
|
||||
override fun logout(cloud: OnedriveCloud) {
|
||||
|
@ -1,25 +1,28 @@
|
||||
package org.cryptomator.data.cloud.onedrive;
|
||||
|
||||
import static org.cryptomator.domain.CloudType.ONEDRIVE;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.cryptomator.data.repository.CloudContentRepositoryFactory;
|
||||
import org.cryptomator.domain.Cloud;
|
||||
import org.cryptomator.domain.OnedriveCloud;
|
||||
import org.cryptomator.domain.repository.CloudContentRepository;
|
||||
import org.cryptomator.util.SharedPreferencesHandler;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import static org.cryptomator.domain.CloudType.ONEDRIVE;
|
||||
|
||||
@Singleton
|
||||
public class OnedriveCloudContentRepositoryFactory implements CloudContentRepositoryFactory {
|
||||
|
||||
private final Context context;
|
||||
private final SharedPreferencesHandler sharedPreferencesHandler;
|
||||
|
||||
@Inject
|
||||
public OnedriveCloudContentRepositoryFactory(Context context) {
|
||||
public OnedriveCloudContentRepositoryFactory(Context context, SharedPreferencesHandler sharedPreferencesHandler) {
|
||||
this.context = context;
|
||||
this.sharedPreferencesHandler = sharedPreferencesHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -29,6 +32,7 @@ public class OnedriveCloudContentRepositoryFactory implements CloudContentReposi
|
||||
|
||||
@Override
|
||||
public CloudContentRepository<OnedriveCloud, OnedriveNode, OnedriveFolder, OnedriveFile> cloudContentRepositoryFor(Cloud cloud) {
|
||||
return new OnedriveCloudContentRepository((OnedriveCloud) cloud, context);
|
||||
OnedriveCloud onedriveCloud = (OnedriveCloud) cloud;
|
||||
return new OnedriveCloudContentRepository(onedriveCloud, context, OnedriveClientFactory.Companion.createInstance(context, onedriveCloud.accessToken(), sharedPreferencesHandler));
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package org.cryptomator.data.cloud.onedrive
|
||||
|
||||
import com.microsoft.graph.models.extensions.DriveItem
|
||||
import com.microsoft.graph.models.DriveItem
|
||||
import org.cryptomator.domain.exception.FatalBackendException
|
||||
import java.util.Date
|
||||
|
||||
internal object OnedriveCloudNodeFactory {
|
||||
@ -15,11 +16,15 @@ internal object OnedriveCloudNodeFactory {
|
||||
}
|
||||
|
||||
private fun file(parent: OnedriveFolder, item: DriveItem): OnedriveFile {
|
||||
return OnedriveFile(parent, item.name, getNodePath(parent, item.name), item.size, lastModified(item))
|
||||
item.name?.let {
|
||||
return OnedriveFile(parent, it, getNodePath(parent, it), item.size, lastModified(item))
|
||||
} ?: throw FatalBackendException("Item name shouldn't be null")
|
||||
}
|
||||
|
||||
fun file(parent: OnedriveFolder, item: DriveItem, lastModified: Date?): OnedriveFile {
|
||||
return OnedriveFile(parent, item.name, getNodePath(parent, item.name), item.size, lastModified)
|
||||
item.name?.let {
|
||||
return OnedriveFile(parent, it, getNodePath(parent, it), item.size, lastModified)
|
||||
} ?: throw FatalBackendException("Item name shouldn't be null")
|
||||
}
|
||||
|
||||
fun file(parent: OnedriveFolder, name: String, size: Long?): OnedriveFile {
|
||||
@ -31,7 +36,9 @@ internal object OnedriveCloudNodeFactory {
|
||||
}
|
||||
|
||||
fun folder(parent: OnedriveFolder, item: DriveItem): OnedriveFolder {
|
||||
return OnedriveFolder(parent, item.name, getNodePath(parent, item.name))
|
||||
item.name?.let {
|
||||
return OnedriveFolder(parent, it, getNodePath(parent, it))
|
||||
} ?: throw FatalBackendException("Item name shouldn't be null")
|
||||
}
|
||||
|
||||
fun folder(parent: OnedriveFolder, name: String): OnedriveFolder {
|
||||
@ -48,25 +55,27 @@ internal object OnedriveCloudNodeFactory {
|
||||
|
||||
@JvmStatic
|
||||
fun getId(item: DriveItem): String {
|
||||
return if (item.remoteItem != null) item.remoteItem.id
|
||||
else item.id
|
||||
return if (item.remoteItem != null) item.remoteItem?.id!!
|
||||
else item.id!!
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getDriveId(item: DriveItem): String? {
|
||||
return when {
|
||||
item.remoteItem != null -> item.remoteItem.parentReference.driveId
|
||||
item.parentReference != null -> item.parentReference.driveId
|
||||
item.remoteItem != null -> item.remoteItem?.parentReference?.driveId
|
||||
item.parentReference != null -> item.parentReference?.driveId
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun isFolder(item: DriveItem): Boolean {
|
||||
return item.folder != null || item.remoteItem != null && item.remoteItem.folder != null
|
||||
return item.folder != null || item.remoteItem != null && item.remoteItem?.folder != null
|
||||
}
|
||||
|
||||
private fun lastModified(item: DriveItem): Date? {
|
||||
return item.lastModifiedDateTime?.time
|
||||
return item.lastModifiedDateTime?.let {
|
||||
return Date.from(it.toInstant())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,575 +0,0 @@
|
||||
// ------------------------------------------------------------------------------
|
||||
// Copyright (c) 2015 Microsoft Corporation
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF WILDCARD_MIME_TYPE KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR WILDCARD_MIME_TYPE CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
// ------------------------------------------------------------------------------
|
||||
package org.cryptomator.data.cloud.onedrive;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.microsoft.graph.authentication.IAuthenticationProvider;
|
||||
import com.microsoft.graph.concurrency.ICallback;
|
||||
import com.microsoft.graph.concurrency.IExecutors;
|
||||
import com.microsoft.graph.concurrency.IProgressCallback;
|
||||
import com.microsoft.graph.core.ClientException;
|
||||
import com.microsoft.graph.core.Constants;
|
||||
import com.microsoft.graph.core.DefaultConnectionConfig;
|
||||
import com.microsoft.graph.core.IClientConfig;
|
||||
import com.microsoft.graph.core.IConnectionConfig;
|
||||
import com.microsoft.graph.http.GraphServiceException;
|
||||
import com.microsoft.graph.http.HttpMethod;
|
||||
import com.microsoft.graph.http.HttpResponseCode;
|
||||
import com.microsoft.graph.http.HttpResponseHeadersHelper;
|
||||
import com.microsoft.graph.http.IHttpProvider;
|
||||
import com.microsoft.graph.http.IHttpRequest;
|
||||
import com.microsoft.graph.http.IStatefulResponseHandler;
|
||||
import com.microsoft.graph.httpcore.HttpClients;
|
||||
import com.microsoft.graph.httpcore.ICoreAuthenticationProvider;
|
||||
import com.microsoft.graph.httpcore.middlewareoption.RedirectOptions;
|
||||
import com.microsoft.graph.httpcore.middlewareoption.RetryOptions;
|
||||
import com.microsoft.graph.logger.ILogger;
|
||||
import com.microsoft.graph.logger.LoggerLevel;
|
||||
import com.microsoft.graph.options.HeaderOption;
|
||||
import com.microsoft.graph.serializer.ISerializer;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URL;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Scanner;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Protocol;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
import okio.BufferedSink;
|
||||
|
||||
/**
|
||||
* Http provider based off of URLConnection.
|
||||
*/
|
||||
public class OnedriveHttpProvider implements IHttpProvider {
|
||||
|
||||
private final HttpResponseHeadersHelper responseHeadersHelper = new HttpResponseHeadersHelper();
|
||||
|
||||
/**
|
||||
* The serializer
|
||||
*/
|
||||
private final ISerializer serializer;
|
||||
|
||||
/**
|
||||
* The authentication provider
|
||||
*/
|
||||
private final IAuthenticationProvider authenticationProvider;
|
||||
|
||||
/**
|
||||
* The executors
|
||||
*/
|
||||
private final IExecutors executors;
|
||||
|
||||
/**
|
||||
* The logger
|
||||
*/
|
||||
private final ILogger logger;
|
||||
|
||||
/**
|
||||
* The connection config
|
||||
*/
|
||||
private IConnectionConfig connectionConfig;
|
||||
|
||||
/**
|
||||
* The OkHttpClient that handles all requests
|
||||
*/
|
||||
private OkHttpClient corehttpClient;
|
||||
|
||||
/**
|
||||
* Creates the DefaultHttpProvider
|
||||
*
|
||||
* @param serializer the serializer
|
||||
* @param authenticationProvider the authentication provider
|
||||
* @param executors the executors
|
||||
* @param logger the logger for diagnostic information
|
||||
*/
|
||||
public OnedriveHttpProvider(final ISerializer serializer, final IAuthenticationProvider authenticationProvider, final IExecutors executors, final ILogger logger) {
|
||||
this.serializer = serializer;
|
||||
this.authenticationProvider = authenticationProvider;
|
||||
this.executors = executors;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the DefaultHttpProvider
|
||||
*
|
||||
* @param clientConfig the client configuration to use for the provider
|
||||
* @param httpClient the http client to execute the requests with
|
||||
*/
|
||||
public OnedriveHttpProvider(final IClientConfig clientConfig, final OkHttpClient httpClient) {
|
||||
this(clientConfig.getSerializer(), clientConfig.getAuthenticationProvider(), clientConfig.getExecutors(), clientConfig.getLogger());
|
||||
this.corehttpClient = httpClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads in a stream and converts it into a string
|
||||
*
|
||||
* @param input the response body stream
|
||||
* @return the string result
|
||||
*/
|
||||
public static String streamToString(final InputStream input) {
|
||||
final String httpStreamEncoding = "UTF-8";
|
||||
final String endOfFile = "\\A";
|
||||
final Scanner scanner = new Scanner(input, httpStreamEncoding);
|
||||
String scannerString = "";
|
||||
try {
|
||||
scanner.useDelimiter(endOfFile);
|
||||
scannerString = scanner.next();
|
||||
} finally {
|
||||
scanner.close();
|
||||
}
|
||||
return scannerString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for the given header in a list of HeaderOptions
|
||||
*
|
||||
* @param headers the list of headers to search through
|
||||
* @param header the header name to search for (case insensitive)
|
||||
* @return true if the header has already been set
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static boolean hasHeader(List<HeaderOption> headers, String header) {
|
||||
for (HeaderOption option : headers) {
|
||||
if (option.getName().equalsIgnoreCase(header)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the serializer for this HTTP provider
|
||||
*
|
||||
* @return the serializer for this provider
|
||||
*/
|
||||
@Override
|
||||
public ISerializer getSerializer() {
|
||||
return serializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the HTTP request asynchronously
|
||||
*
|
||||
* @param request the request description
|
||||
* @param callback the callback to be called after success or failure
|
||||
* @param resultClass the class of the response from the service
|
||||
* @param serializable the object to send to the service in the body of the request
|
||||
* @param <Result> the type of the response object
|
||||
* @param <Body> the type of the object to send to the service in the body of the request
|
||||
*/
|
||||
@Override
|
||||
public <Result, Body> void send(final IHttpRequest request, final ICallback<? super Result> callback, final Class<Result> resultClass, final Body serializable) {
|
||||
final IProgressCallback<? super Result> progressCallback;
|
||||
if (callback instanceof IProgressCallback) {
|
||||
progressCallback = (IProgressCallback<? super Result>) callback;
|
||||
} else {
|
||||
progressCallback = null;
|
||||
}
|
||||
|
||||
executors.performOnBackground(() -> {
|
||||
try {
|
||||
executors.performOnForeground(sendRequestInternal(request, resultClass, serializable, progressCallback, null), callback);
|
||||
} catch (final ClientException e) {
|
||||
executors.performOnForeground(e, callback);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the HTTP request
|
||||
*
|
||||
* @param request the request description
|
||||
* @param resultClass the class of the response from the service
|
||||
* @param serializable the object to send to the service in the body of the request
|
||||
* @param <Result> the type of the response object
|
||||
* @param <Body> the type of the object to send to the service in the body of the request
|
||||
* @return the result from the request
|
||||
* @throws ClientException an exception occurs if the request was unable to complete for any reason
|
||||
*/
|
||||
@Override
|
||||
public <Result, Body> Result send(final IHttpRequest request, final Class<Result> resultClass, final Body serializable) throws ClientException {
|
||||
return send(request, resultClass, serializable, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the HTTP request
|
||||
*
|
||||
* @param request the request description
|
||||
* @param resultClass the class of the response from the service
|
||||
* @param serializable the object to send to the service in the body of the request
|
||||
* @param handler the handler for stateful response
|
||||
* @param <Result> the type of the response object
|
||||
* @param <Body> the type of the object to send to the service in the body of the request
|
||||
* @param <DeserializeType> the response handler for stateful response
|
||||
* @return the result from the request
|
||||
* @throws ClientException this exception occurs if the request was unable to complete for any reason
|
||||
*/
|
||||
public <Result, Body, DeserializeType> Result send(final IHttpRequest request, final Class<Result> resultClass, final Body serializable, final IStatefulResponseHandler<Result, DeserializeType> handler) throws ClientException {
|
||||
return sendRequestInternal(request, resultClass, serializable, null, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the HTTP request
|
||||
*
|
||||
* @param request the request description
|
||||
* @param resultClass the class of the response from the service
|
||||
* @param serializable the object to send to the service in the body of the request
|
||||
* @param progress the progress callback for the request
|
||||
* @param <Result> the type of the response object
|
||||
* @param <Body> the type of the object to send to the service in the body of the request
|
||||
* @return the result from the request
|
||||
* @throws ClientException an exception occurs if the request was unable to complete for any reason
|
||||
*/
|
||||
public <Result, Body> Request getHttpRequest(final IHttpRequest request, final Class<Result> resultClass, final Body serializable, final IProgressCallback<? super Result> progress) throws ClientException {
|
||||
final int defaultBufferSize = 4096;
|
||||
|
||||
final URL requestUrl = request.getRequestUrl();
|
||||
logger.logDebug("Starting to send request, URL " + requestUrl.toString());
|
||||
|
||||
if (this.connectionConfig == null) {
|
||||
this.connectionConfig = new DefaultConnectionConfig();
|
||||
}
|
||||
|
||||
// Request level middleware options
|
||||
RedirectOptions redirectOptions = new RedirectOptions(request.getMaxRedirects() > 0 ? request.getMaxRedirects() : this.connectionConfig.getMaxRedirects(), request.getShouldRedirect() != null ? request.getShouldRedirect() : this.connectionConfig.getShouldRedirect());
|
||||
RetryOptions retryOptions = new RetryOptions(request.getShouldRetry() != null ? request.getShouldRetry() : this.connectionConfig.getShouldRetry(), request.getMaxRetries() > 0 ? request.getMaxRetries() : this.connectionConfig.getMaxRetries(), request.getDelay() > 0 ? request.getDelay() : this.connectionConfig.getDelay());
|
||||
|
||||
Request coreHttpRequest = convertIHttpRequestToOkHttpRequest(request);
|
||||
Request.Builder corehttpRequestBuilder = coreHttpRequest.newBuilder().tag(RedirectOptions.class, redirectOptions).tag(RetryOptions.class, retryOptions);
|
||||
|
||||
String contenttype = null;
|
||||
|
||||
logger.logDebug("Request Method " + request.getHttpMethod().toString());
|
||||
List<HeaderOption> requestHeaders = request.getHeaders();
|
||||
|
||||
for (HeaderOption headerOption : requestHeaders) {
|
||||
if (headerOption.getName().equalsIgnoreCase(Constants.CONTENT_TYPE_HEADER_NAME)) {
|
||||
contenttype = headerOption.getValue().toString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
final byte[] bytesToWrite;
|
||||
corehttpRequestBuilder.addHeader("Accept", "*/*");
|
||||
if (serializable == null) {
|
||||
// Send an empty body through with a POST request
|
||||
// This ensures that the Content-Length header is properly set
|
||||
if (request.getHttpMethod() == HttpMethod.POST) {
|
||||
bytesToWrite = new byte[0];
|
||||
if (contenttype == null) {
|
||||
contenttype = Constants.BINARY_CONTENT_TYPE;
|
||||
}
|
||||
} else {
|
||||
bytesToWrite = null;
|
||||
}
|
||||
} else if (serializable instanceof byte[]) {
|
||||
logger.logDebug("Sending byte[] as request body");
|
||||
bytesToWrite = (byte[]) serializable;
|
||||
|
||||
// If the user hasn't specified a Content-Type for the request
|
||||
if (!hasHeader(requestHeaders, Constants.CONTENT_TYPE_HEADER_NAME)) {
|
||||
corehttpRequestBuilder.addHeader(Constants.CONTENT_TYPE_HEADER_NAME, Constants.BINARY_CONTENT_TYPE);
|
||||
contenttype = Constants.BINARY_CONTENT_TYPE;
|
||||
}
|
||||
} else {
|
||||
logger.logDebug("Sending " + serializable.getClass().getName() + " as request body");
|
||||
final String serializeObject = serializer.serializeObject(serializable);
|
||||
try {
|
||||
bytesToWrite = serializeObject.getBytes(Constants.JSON_ENCODING);
|
||||
} catch (final UnsupportedEncodingException ex) {
|
||||
final ClientException clientException = new ClientException("Unsupported encoding problem: ", ex);
|
||||
logger.logError("Unsupported encoding problem: " + ex.getMessage(), ex);
|
||||
throw clientException;
|
||||
}
|
||||
|
||||
// If the user hasn't specified a Content-Type for the request
|
||||
if (!hasHeader(requestHeaders, Constants.CONTENT_TYPE_HEADER_NAME)) {
|
||||
corehttpRequestBuilder.addHeader(Constants.CONTENT_TYPE_HEADER_NAME, Constants.JSON_CONTENT_TYPE);
|
||||
contenttype = Constants.JSON_CONTENT_TYPE;
|
||||
}
|
||||
}
|
||||
|
||||
RequestBody requestBody = null;
|
||||
// Handle cases where we've got a body to process.
|
||||
if (bytesToWrite != null) {
|
||||
final String mediaContentType = contenttype;
|
||||
requestBody = new RequestBody() {
|
||||
@Override
|
||||
public long contentLength() {
|
||||
return bytesToWrite.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(@NotNull BufferedSink sink) throws IOException {
|
||||
OutputStream out = sink.outputStream();
|
||||
int writtenSoFar = 0;
|
||||
BufferedOutputStream bos = new BufferedOutputStream(out);
|
||||
int toWrite;
|
||||
do {
|
||||
toWrite = Math.min(defaultBufferSize, bytesToWrite.length - writtenSoFar);
|
||||
bos.write(bytesToWrite, writtenSoFar, toWrite);
|
||||
writtenSoFar = writtenSoFar + toWrite;
|
||||
if (progress != null) {
|
||||
executors.performOnForeground(writtenSoFar, bytesToWrite.length, progress);
|
||||
}
|
||||
} while (toWrite > 0);
|
||||
bos.close();
|
||||
out.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaType contentType() {
|
||||
return MediaType.parse(mediaContentType);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
corehttpRequestBuilder.method(request.getHttpMethod().toString(), requestBody);
|
||||
return corehttpRequestBuilder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the HTTP request
|
||||
*
|
||||
* @param request the request description
|
||||
* @param resultClass the class of the response from the service
|
||||
* @param serializable the object to send to the service in the body of the request
|
||||
* @param progress the progress callback for the request
|
||||
* @param handler the handler for stateful response
|
||||
* @param <Result> the type of the response object
|
||||
* @param <Body> the type of the object to send to the service in the body of the request
|
||||
* @param <DeserializeType> the response handler for stateful response
|
||||
* @return the result from the request
|
||||
* @throws ClientException an exception occurs if the request was unable to complete for any reason
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private <Result, Body, DeserializeType> Result sendRequestInternal(final IHttpRequest request, final Class<Result> resultClass, final Body serializable, final IProgressCallback<? super Result> progress, final IStatefulResponseHandler<Result, DeserializeType> handler) throws ClientException {
|
||||
|
||||
try {
|
||||
if (this.connectionConfig == null) {
|
||||
this.connectionConfig = new DefaultConnectionConfig();
|
||||
}
|
||||
if (this.corehttpClient == null) {
|
||||
final ICoreAuthenticationProvider authProvider = request1 -> request1;
|
||||
this.corehttpClient = HttpClients.createDefault(authProvider).newBuilder().connectTimeout(connectionConfig.getConnectTimeout(), TimeUnit.MILLISECONDS).readTimeout(connectionConfig.getReadTimeout(), TimeUnit.MILLISECONDS).followRedirects(false) // TODO https://github.com/microsoftgraph/msgraph-sdk-java/issues/516
|
||||
.protocols(Collections.singletonList(Protocol.HTTP_1_1)) // https://stackoverflow.com/questions/62031298/sockettimeout-on-java-11-but-not-on-java-8
|
||||
.build();
|
||||
}
|
||||
if (authenticationProvider != null) { // TODO https://github.com/microsoftgraph/msgraph-sdk-java/issues/517
|
||||
authenticationProvider.authenticateRequest(request);
|
||||
}
|
||||
Request coreHttpRequest = getHttpRequest(request, resultClass, serializable, progress);
|
||||
Response response = corehttpClient.newCall(coreHttpRequest).execute();
|
||||
InputStream in = null;
|
||||
boolean isBinaryStreamInput = false;
|
||||
try {
|
||||
|
||||
// Call being executed
|
||||
|
||||
if (handler != null) {
|
||||
handler.configConnection(response);
|
||||
}
|
||||
|
||||
logger.logDebug(String.format("Response code %d, %s", response.code(), response.message()));
|
||||
|
||||
if (handler != null) {
|
||||
logger.logDebug("StatefulResponse is handling the HTTP response.");
|
||||
return handler.generateResult(request, response, this.getSerializer(), this.logger);
|
||||
}
|
||||
|
||||
if (response.code() >= HttpResponseCode.HTTP_CLIENT_ERROR) {
|
||||
logger.logDebug("Handling error response");
|
||||
in = response.body().byteStream();
|
||||
handleErrorResponse(request, serializable, response);
|
||||
}
|
||||
|
||||
if (response.code() == HttpResponseCode.HTTP_NOBODY || response.code() == HttpResponseCode.HTTP_NOT_MODIFIED) {
|
||||
logger.logDebug("Handling response with no body");
|
||||
return handleEmptyResponse(responseHeadersHelper.getResponseHeadersAsMapOfStringList(response), resultClass);
|
||||
}
|
||||
|
||||
if (response.code() == HttpResponseCode.HTTP_ACCEPTED) {
|
||||
logger.logDebug("Handling accepted response");
|
||||
return handleEmptyResponse(responseHeadersHelper.getResponseHeadersAsMapOfStringList(response), resultClass);
|
||||
}
|
||||
|
||||
in = new BufferedInputStream(response.body().byteStream());
|
||||
|
||||
final Map<String, String> headers = responseHeadersHelper.getResponseHeadersAsMapStringString(response);
|
||||
|
||||
if (response.body() == null || response.body().contentLength() == 0) {
|
||||
return (Result) null;
|
||||
}
|
||||
|
||||
final String contentType = headers.get(Constants.CONTENT_TYPE_HEADER_NAME);
|
||||
if (contentType != null && resultClass != InputStream.class && contentType.contains(Constants.JSON_CONTENT_TYPE)) {
|
||||
logger.logDebug("Response json");
|
||||
return handleJsonResponse(in, responseHeadersHelper.getResponseHeadersAsMapOfStringList(response), resultClass);
|
||||
} else if (resultClass == InputStream.class) {
|
||||
logger.logDebug("Response binary");
|
||||
isBinaryStreamInput = true;
|
||||
return (Result) handleBinaryStream(in);
|
||||
} else {
|
||||
return (Result) null;
|
||||
}
|
||||
} finally {
|
||||
if (!isBinaryStreamInput) {
|
||||
try {
|
||||
if (in != null) {
|
||||
in.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.logError(e.getMessage(), e);
|
||||
}
|
||||
if (response != null) {
|
||||
response.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (final GraphServiceException ex) {
|
||||
final boolean shouldLogVerbosely = logger.getLoggingLevel() == LoggerLevel.DEBUG;
|
||||
logger.logError("Graph service exception " + ex.getMessage(shouldLogVerbosely), ex);
|
||||
throw ex;
|
||||
} catch (final Exception ex) {
|
||||
final ClientException clientException = new ClientException("Error during http request", ex);
|
||||
logger.logError("Error during http request", clientException);
|
||||
throw clientException;
|
||||
}
|
||||
}
|
||||
|
||||
private Request convertIHttpRequestToOkHttpRequest(IHttpRequest request) {
|
||||
if (request != null) {
|
||||
Request.Builder requestBuilder = new Request.Builder();
|
||||
requestBuilder.url(request.getRequestUrl());
|
||||
for (final HeaderOption header : request.getHeaders()) {
|
||||
requestBuilder.addHeader(header.getName(), header.getValue().toString());
|
||||
}
|
||||
return requestBuilder.build();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the event of an error response
|
||||
*
|
||||
* @param request the request that caused the failed response
|
||||
* @param serializable the body of the request
|
||||
* @param connection the URL connection
|
||||
* @param <Body> the type of the request body
|
||||
* @throws IOException an exception occurs if there were any problems interacting with the connection object
|
||||
*/
|
||||
private <Body> void handleErrorResponse(final IHttpRequest request, final Body serializable, final Response response) throws IOException {
|
||||
throw GraphServiceException.createFromConnection(request, serializable, serializer, response, logger);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the cause where the response is a binary stream
|
||||
*
|
||||
* @param in the input stream from the response
|
||||
* @return the input stream to return to the caller
|
||||
*/
|
||||
private InputStream handleBinaryStream(final InputStream in) {
|
||||
return in;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the cause where the response is a JSON object
|
||||
*
|
||||
* @param in the input stream from the response
|
||||
* @param responseHeaders the response header
|
||||
* @param clazz the class of the response object
|
||||
* @param <Result> the type of the response object
|
||||
* @return the JSON object
|
||||
*/
|
||||
private <Result> Result handleJsonResponse(final InputStream in, Map<String, List<String>> responseHeaders, final Class<Result> clazz) {
|
||||
if (clazz == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final String rawJson = streamToString(in);
|
||||
return getSerializer().deserializeObject(rawJson, clazz, responseHeaders);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the case where the response body is empty
|
||||
*
|
||||
* @param responseHeaders the response headers
|
||||
* @param clazz the type of the response object
|
||||
* @return the JSON object
|
||||
*/
|
||||
private <Result> Result handleEmptyResponse(Map<String, List<String>> responseHeaders, final Class<Result> clazz) throws UnsupportedEncodingException {
|
||||
// Create an empty object to attach the response headers to
|
||||
InputStream in = new ByteArrayInputStream("{}".getBytes(Constants.JSON_ENCODING));
|
||||
return handleJsonResponse(in, responseHeaders, clazz);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public ILogger getLogger() {
|
||||
return logger;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public IExecutors getExecutors() {
|
||||
return executors;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public IAuthenticationProvider getAuthenticationProvider() {
|
||||
return authenticationProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get connection config for read and connect timeout in requests
|
||||
*
|
||||
* @return Connection configuration to be used for timeout values
|
||||
*/
|
||||
public IConnectionConfig getConnectionConfig() {
|
||||
if (this.connectionConfig == null) {
|
||||
this.connectionConfig = new DefaultConnectionConfig();
|
||||
}
|
||||
return connectionConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set connection config for read and connect timeout in requests
|
||||
*
|
||||
* @param connectionConfig Connection configuration to be used for timeout values
|
||||
*/
|
||||
public void setConnectionConfig(IConnectionConfig connectionConfig) {
|
||||
this.connectionConfig = connectionConfig;
|
||||
}
|
||||
}
|
@ -2,26 +2,25 @@ package org.cryptomator.data.cloud.onedrive
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import com.microsoft.graph.concurrency.ChunkedUploadProvider
|
||||
import com.microsoft.graph.http.GraphServiceException
|
||||
import com.microsoft.graph.models.extensions.DriveItem
|
||||
import com.microsoft.graph.models.extensions.DriveItemUploadableProperties
|
||||
import com.microsoft.graph.models.extensions.Folder
|
||||
import com.microsoft.graph.models.extensions.IGraphServiceClient
|
||||
import com.microsoft.graph.models.extensions.ItemReference
|
||||
import com.microsoft.graph.models.DriveItem
|
||||
import com.microsoft.graph.models.DriveItemCreateUploadSessionParameterSet
|
||||
import com.microsoft.graph.models.DriveItemUploadableProperties
|
||||
import com.microsoft.graph.models.Folder
|
||||
import com.microsoft.graph.models.ItemReference
|
||||
import com.microsoft.graph.options.Option
|
||||
import com.microsoft.graph.options.QueryOption
|
||||
import com.microsoft.graph.requests.extensions.IDriveRequestBuilder
|
||||
import com.microsoft.graph.requests.DriveRequestBuilder
|
||||
import com.microsoft.graph.requests.GraphServiceClient
|
||||
import com.microsoft.graph.tasks.LargeFileUploadTask
|
||||
import com.tomclaw.cache.DiskLruCache
|
||||
import org.cryptomator.data.cloud.onedrive.OnedriveCloudNodeFactory.folder
|
||||
import org.cryptomator.data.cloud.onedrive.OnedriveCloudNodeFactory.from
|
||||
import org.cryptomator.data.cloud.onedrive.OnedriveCloudNodeFactory.getDriveId
|
||||
import org.cryptomator.data.cloud.onedrive.OnedriveCloudNodeFactory.getId
|
||||
import org.cryptomator.data.cloud.onedrive.OnedriveCloudNodeFactory.isFolder
|
||||
import org.cryptomator.data.cloud.onedrive.graph.ClientException
|
||||
import org.cryptomator.data.cloud.onedrive.graph.ICallback
|
||||
import org.cryptomator.data.cloud.onedrive.graph.IProgressCallback
|
||||
import org.cryptomator.data.util.CopyStream
|
||||
import org.cryptomator.data.util.TransferredBytesAwareInputStream
|
||||
import org.cryptomator.data.util.TransferredBytesAwareOutputStream
|
||||
import org.cryptomator.domain.OnedriveCloud
|
||||
import org.cryptomator.domain.exception.BackendException
|
||||
@ -41,26 +40,23 @@ import org.cryptomator.util.file.LruFileCacheUtil.Companion.retrieveFromLruCache
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.io.OutputStream
|
||||
import java.util.ArrayList
|
||||
import java.util.Date
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import java.util.concurrent.ExecutionException
|
||||
import okhttp3.Request
|
||||
import timber.log.Timber
|
||||
|
||||
internal class OnedriveImpl(cloud: OnedriveCloud, context: Context, nodeInfoCache: OnedriveIdCache) {
|
||||
internal class OnedriveImpl(cloud: OnedriveCloud, context: Context, graphServiceClient: GraphServiceClient<Request>, nodeInfoCache: OnedriveIdCache) {
|
||||
|
||||
private val cloud: OnedriveCloud
|
||||
private val context: Context
|
||||
private val graphServiceClient: GraphServiceClient<Request>
|
||||
private val nodeInfoCache: OnedriveIdCache
|
||||
private val sharedPreferencesHandler: SharedPreferencesHandler
|
||||
private var diskLruCache: DiskLruCache? = null
|
||||
|
||||
private fun client(): IGraphServiceClient {
|
||||
return OnedriveClientFactory.getInstance(context, cloud.accessToken())
|
||||
}
|
||||
|
||||
private fun drive(driveId: String?): IDriveRequestBuilder {
|
||||
return if (driveId == null) client().me().drive() else client().drives(driveId)
|
||||
private fun drive(driveId: String?): DriveRequestBuilder {
|
||||
return if (driveId == null) graphServiceClient.me().drive() else graphServiceClient.drives(driveId)
|
||||
}
|
||||
|
||||
fun root(): OnedriveFolder {
|
||||
@ -90,11 +86,7 @@ internal class OnedriveImpl(cloud: OnedriveCloud, context: Context, nodeInfoCach
|
||||
|
||||
private fun childByName(parentId: String, parentDriveId: String, name: String): DriveItem? {
|
||||
return try {
|
||||
drive(parentDriveId) //
|
||||
.items(parentId) //
|
||||
.itemWithPath(Uri.encode(name)) //
|
||||
.buildRequest() //
|
||||
.get()
|
||||
drive(parentDriveId).items(parentId).itemWithPath(Uri.encode(name)).buildRequest().get()
|
||||
} catch (e: GraphServiceException) {
|
||||
if (isNotFoundError(e)) {
|
||||
null
|
||||
@ -138,18 +130,14 @@ internal class OnedriveImpl(cloud: OnedriveCloud, context: Context, nodeInfoCach
|
||||
fun list(folder: OnedriveFolder): List<OnedriveNode> {
|
||||
val result: MutableList<OnedriveNode> = ArrayList()
|
||||
val nodeInfo = requireNodeInfo(folder)
|
||||
var page = drive(nodeInfo.driveId) //
|
||||
.items(nodeInfo.id) //
|
||||
.children() //
|
||||
.buildRequest() //
|
||||
.get()
|
||||
var page = drive(nodeInfo.driveId).items(nodeInfo.id).children().buildRequest().get()
|
||||
do {
|
||||
removeChildNodeInfo(folder)
|
||||
page.currentPage?.forEach {
|
||||
page?.currentPage?.forEach {
|
||||
result.add(cacheNodeInfo(from(folder, it), it))
|
||||
}
|
||||
page = if (page.nextPage != null) {
|
||||
page.nextPage.buildRequest().get()
|
||||
page = if (page?.nextPage != null) {
|
||||
page.nextPage?.buildRequest()?.get()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
@ -170,10 +158,7 @@ internal class OnedriveImpl(cloud: OnedriveCloud, context: Context, nodeInfoCach
|
||||
folderToCreate.name = folder.name
|
||||
folderToCreate.folder = Folder()
|
||||
val parentNodeInfo = requireNodeInfo(parentFolder)
|
||||
val createdFolder = drive(parentNodeInfo.driveId) //
|
||||
.items(parentNodeInfo.id).children() //
|
||||
.buildRequest() //
|
||||
.post(folderToCreate)
|
||||
val createdFolder = drive(parentNodeInfo.driveId).items(parentNodeInfo.id).children().buildRequest().post(folderToCreate)
|
||||
return cacheNodeInfo(folder(parentFolder, createdFolder), createdFolder)
|
||||
} ?: throw ParentFolderIsNullException(folder.name)
|
||||
}
|
||||
@ -192,12 +177,10 @@ internal class OnedriveImpl(cloud: OnedriveCloud, context: Context, nodeInfoCach
|
||||
targetParentReference.driveId = targetNodeInfo?.driveId
|
||||
targetItem.parentReference = targetParentReference
|
||||
val sourceNodeInfo = requireNodeInfo(source)
|
||||
val movedItem = drive(sourceNodeInfo.driveId) //
|
||||
.items(sourceNodeInfo.id) //
|
||||
.buildRequest() //
|
||||
.patch(targetItem)
|
||||
removeNodeInfo(source)
|
||||
return cacheNodeInfo(from(targetsParent, movedItem), movedItem)
|
||||
drive(sourceNodeInfo.driveId).items(sourceNodeInfo.id).buildRequest().patch(targetItem)?.let {
|
||||
removeNodeInfo(source)
|
||||
return cacheNodeInfo(from(targetsParent, it), it)
|
||||
} ?: throw FatalBackendException("Failed to move file, response is null")
|
||||
} ?: throw ParentFolderIsNullException(target.name)
|
||||
}
|
||||
|
||||
@ -214,7 +197,7 @@ internal class OnedriveImpl(cloud: OnedriveCloud, context: Context, nodeInfoCach
|
||||
val conflictBehaviorOption: Option = QueryOption("@name.conflictBehavior", uploadMode)
|
||||
val result = CompletableFuture<DriveItem>()
|
||||
if (size <= CHUNKED_UPLOAD_MAX_SIZE) {
|
||||
uploadFile(file, data, progressAware, result, conflictBehaviorOption)
|
||||
uploadFile(file, data, progressAware, result, conflictBehaviorOption, size)
|
||||
} else {
|
||||
try {
|
||||
chunkedUploadFile(file, data, progressAware, result, conflictBehaviorOption, size)
|
||||
@ -233,88 +216,67 @@ internal class OnedriveImpl(cloud: OnedriveCloud, context: Context, nodeInfoCach
|
||||
}
|
||||
|
||||
@Throws(NoSuchCloudFileException::class)
|
||||
private fun uploadFile( //
|
||||
file: OnedriveFile, //
|
||||
data: DataSource, //
|
||||
progressAware: ProgressAware<UploadState>, //
|
||||
result: CompletableFuture<DriveItem>, //
|
||||
conflictBehaviorOption: Option
|
||||
) {
|
||||
val parentNodeInfo = requireNodeInfo(file.parent)
|
||||
try {
|
||||
data.open(context)?.use { inputStream ->
|
||||
drive(parentNodeInfo.driveId) //
|
||||
.items(parentNodeInfo.id) //
|
||||
.itemWithPath(file.name) //
|
||||
.content() //
|
||||
.buildRequest(listOf(conflictBehaviorOption)) //
|
||||
.put(CopyStream.toByteArray(inputStream), object : IProgressCallback<DriveItem> {
|
||||
override fun progress(current: Long, max: Long) {
|
||||
progressAware //
|
||||
.onProgress(
|
||||
Progress.progress(UploadState.upload(file)) //
|
||||
.between(0) //
|
||||
.and(max) //
|
||||
.withValue(current)
|
||||
)
|
||||
private fun uploadFile(file: OnedriveFile, data: DataSource, progressAware: ProgressAware<UploadState>, result: CompletableFuture<DriveItem>, conflictBehaviorOption: Option, size: Long) {
|
||||
data.open(context)?.use { inputStream ->
|
||||
object : TransferredBytesAwareInputStream(inputStream) {
|
||||
override fun bytesTransferred(transferred: Long) {
|
||||
progressAware.onProgress(Progress.progress(UploadState.upload(file)).between(0).and(size).withValue(transferred))
|
||||
}
|
||||
}.use {
|
||||
val parentNodeInfo = requireNodeInfo(file.parent)
|
||||
try {
|
||||
drive(parentNodeInfo.driveId) //
|
||||
.items(parentNodeInfo.id) //
|
||||
.itemWithPath(file.name) //
|
||||
.content() //
|
||||
.buildRequest(listOf(conflictBehaviorOption)) //
|
||||
.putAsync(CopyStream.toByteArray(it)) //
|
||||
.whenComplete { driveItem, error ->
|
||||
run {
|
||||
if (error == null) {
|
||||
progressAware.onProgress(Progress.completed(UploadState.upload(file)))
|
||||
result.complete(driveItem)
|
||||
cacheNodeInfo(file, driveItem)
|
||||
} else {
|
||||
result.completeExceptionally(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun success(item: DriveItem) {
|
||||
progressAware.onProgress(Progress.completed(UploadState.upload(file)))
|
||||
result.complete(item)
|
||||
cacheNodeInfo(file, item)
|
||||
}
|
||||
|
||||
override fun failure(ex: com.microsoft.graph.core.ClientException) {
|
||||
result.completeExceptionally(ex)
|
||||
}
|
||||
})
|
||||
} ?: throw FatalBackendException("InputStream shouldn't be null")
|
||||
} catch (e: IOException) {
|
||||
throw FatalBackendException(e)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
throw FatalBackendException(e)
|
||||
}
|
||||
}
|
||||
} ?: throw FatalBackendException("InputStream shouldn't bee null")
|
||||
}
|
||||
|
||||
@Throws(IOException::class, NoSuchCloudFileException::class)
|
||||
private fun chunkedUploadFile( //
|
||||
file: OnedriveFile, //
|
||||
data: DataSource, //
|
||||
progressAware: ProgressAware<UploadState>, //
|
||||
result: CompletableFuture<DriveItem>, //
|
||||
conflictBehaviorOption: Option, //
|
||||
size: Long
|
||||
) {
|
||||
private fun chunkedUploadFile(file: OnedriveFile, data: DataSource, progressAware: ProgressAware<UploadState>, result: CompletableFuture<DriveItem>, conflictBehaviorOption: Option, size: Long) {
|
||||
val parentNodeInfo = requireNodeInfo(file.parent)
|
||||
val uploadSession = drive(parentNodeInfo.driveId) //
|
||||
drive(parentNodeInfo.driveId) //
|
||||
.items(parentNodeInfo.id) //
|
||||
.itemWithPath(file.name) //
|
||||
.createUploadSession(DriveItemUploadableProperties()) //
|
||||
.createUploadSession(DriveItemCreateUploadSessionParameterSet.newBuilder().withItem(DriveItemUploadableProperties()).build()) //
|
||||
.buildRequest() //
|
||||
.post()
|
||||
data.open(context).use { inputStream ->
|
||||
ChunkedUploadProvider(uploadSession, client(), inputStream, size, DriveItem::class.java) //
|
||||
.upload(listOf(conflictBehaviorOption), object : IProgressCallback<DriveItem> {
|
||||
override fun progress(current: Long, max: Long) {
|
||||
progressAware.onProgress(
|
||||
Progress //
|
||||
.progress(UploadState.upload(file)) //
|
||||
.between(0) //
|
||||
.and(max) //
|
||||
.withValue(current)
|
||||
)
|
||||
}
|
||||
|
||||
override fun success(item: DriveItem) {
|
||||
progressAware.onProgress(Progress.completed(UploadState.upload(file)))
|
||||
result.complete(item)
|
||||
cacheNodeInfo(file, item)
|
||||
}
|
||||
|
||||
override fun failure(ex: com.microsoft.graph.core.ClientException) {
|
||||
result.completeExceptionally(ex)
|
||||
}
|
||||
}, CHUNKED_UPLOAD_CHUNK_SIZE, CHUNKED_UPLOAD_MAX_ATTEMPTS)
|
||||
}
|
||||
.post()?.let { uploadSession ->
|
||||
data.open(context)?.use { inputStream ->
|
||||
LargeFileUploadTask(uploadSession, graphServiceClient, inputStream, size, DriveItem::class.java) //
|
||||
.uploadAsync(CHUNKED_UPLOAD_CHUNK_SIZE, listOf(conflictBehaviorOption)) { current, max ->
|
||||
progressAware.onProgress(
|
||||
Progress.progress(UploadState.upload(file)).between(0).and(max).withValue(current)
|
||||
)
|
||||
}.whenComplete { driveItemResult, error ->
|
||||
run {
|
||||
if (error == null && driveItemResult.responseBody != null) {
|
||||
progressAware.onProgress(Progress.completed(UploadState.upload(file)))
|
||||
result.complete(driveItemResult.responseBody)
|
||||
cacheNodeInfo(file, driveItemResult.responseBody!!)
|
||||
} else {
|
||||
result.completeExceptionally(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
} ?: throw FatalBackendException("InputStream shouldn't bee null")
|
||||
} ?: throw FatalBackendException("Failed to create upload session, response is null")
|
||||
}
|
||||
|
||||
@Throws(BackendException::class, IOException::class)
|
||||
@ -340,27 +302,12 @@ internal class OnedriveImpl(cloud: OnedriveCloud, context: Context, nodeInfoCach
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun writeToData(
|
||||
file: OnedriveFile, //
|
||||
nodeInfo: OnedriveIdCache.NodeInfo, //
|
||||
data: OutputStream, //
|
||||
encryptedTmpFile: File?, //
|
||||
cacheKey: String?, //
|
||||
progressAware: ProgressAware<DownloadState>
|
||||
) {
|
||||
val request = drive(nodeInfo.driveId) //
|
||||
.items(nodeInfo.id) //
|
||||
.content() //
|
||||
.buildRequest()
|
||||
request.get().use { inputStream ->
|
||||
private fun writeToData(file: OnedriveFile, nodeInfo: OnedriveIdCache.NodeInfo, data: OutputStream, encryptedTmpFile: File?, cacheKey: String?, progressAware: ProgressAware<DownloadState>) {
|
||||
val request = drive(nodeInfo.driveId).items(nodeInfo.id).content().buildRequest()
|
||||
request.get()?.use { inputStream ->
|
||||
object : TransferredBytesAwareOutputStream(data) {
|
||||
override fun bytesTransferred(transferred: Long) {
|
||||
progressAware.onProgress( //
|
||||
Progress.progress(DownloadState.download(file)) //
|
||||
.between(0) //
|
||||
.and(file.size ?: Long.MAX_VALUE) //
|
||||
.withValue(transferred)
|
||||
)
|
||||
progressAware.onProgress(Progress.progress(DownloadState.download(file)).between(0).and(file.size ?: Long.MAX_VALUE).withValue(transferred))
|
||||
}
|
||||
}.use { out -> CopyStream.copyStreamToStream(inputStream, out) }
|
||||
}
|
||||
@ -391,10 +338,7 @@ internal class OnedriveImpl(cloud: OnedriveCloud, context: Context, nodeInfoCach
|
||||
@Throws(NoSuchCloudFileException::class)
|
||||
fun delete(node: OnedriveNode) {
|
||||
val nodeInfo = requireNodeInfo(node)
|
||||
drive(nodeInfo.driveId) //
|
||||
.items(nodeInfo.id) //
|
||||
.buildRequest() //
|
||||
.delete()
|
||||
drive(nodeInfo.driveId).items(nodeInfo.id).buildRequest().delete()
|
||||
removeNodeInfo(node)
|
||||
}
|
||||
|
||||
@ -440,8 +384,9 @@ internal class OnedriveImpl(cloud: OnedriveCloud, context: Context, nodeInfoCach
|
||||
}
|
||||
|
||||
private fun loadRootNodeInfo(): OnedriveIdCache.NodeInfo {
|
||||
val item = drive(null).root().buildRequest().get()
|
||||
return OnedriveIdCache.NodeInfo(getId(item), getDriveId(item), true, item.cTag)
|
||||
return drive(null).root().buildRequest().get()?.let { rootItem ->
|
||||
OnedriveIdCache.NodeInfo(getId(rootItem), getDriveId(rootItem), true, rootItem.cTag)
|
||||
} ?: throw FatalBackendException("Failed to load root item, item is null")
|
||||
}
|
||||
|
||||
private fun loadNonRootNodeInfo(node: OnedriveNode): OnedriveIdCache.NodeInfo? {
|
||||
@ -459,37 +404,20 @@ internal class OnedriveImpl(cloud: OnedriveCloud, context: Context, nodeInfoCach
|
||||
} ?: throw ParentFolderIsNullException(node.name)
|
||||
}
|
||||
|
||||
fun currentAccount(): String {
|
||||
return client().me().drive().buildRequest().get().owner.user.displayName
|
||||
fun currentAccount(username: String): String {
|
||||
// used to check authentication
|
||||
graphServiceClient.me().drive().buildRequest().get()?.owner?.user
|
||||
return username
|
||||
}
|
||||
|
||||
fun logout() {
|
||||
val result = CompletableFuture<Void?>()
|
||||
OnedriveClientFactory.getAuthAdapter(context, cloud.accessToken()).logout(object : ICallback<Void?> {
|
||||
override fun success(aVoid: Void?) {
|
||||
result.complete(null)
|
||||
}
|
||||
|
||||
override fun failure(e: ClientException) {
|
||||
result.completeExceptionally(e)
|
||||
}
|
||||
})
|
||||
try {
|
||||
result.get()
|
||||
} catch (e: InterruptedException) {
|
||||
throw FatalBackendException(e)
|
||||
} catch (e: ExecutionException) {
|
||||
throw FatalBackendException(e)
|
||||
}
|
||||
|
||||
OnedriveClientFactory.logout()
|
||||
// FIXME what about logout?
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val CHUNKED_UPLOAD_MAX_SIZE = 4L shl 20
|
||||
private const val CHUNKED_UPLOAD_CHUNK_SIZE = 327680 * 32
|
||||
private const val CHUNKED_UPLOAD_MAX_ATTEMPTS = 5
|
||||
private const val REPLACE_MODE = "replace"
|
||||
private const val NON_REPLACING_MODE = "rename"
|
||||
}
|
||||
@ -500,6 +428,7 @@ internal class OnedriveImpl(cloud: OnedriveCloud, context: Context, nodeInfoCach
|
||||
}
|
||||
this.cloud = cloud
|
||||
this.context = context
|
||||
this.graphServiceClient = graphServiceClient
|
||||
this.nodeInfoCache = nodeInfoCache
|
||||
sharedPreferencesHandler = SharedPreferencesHandler(context)
|
||||
}
|
||||
|
@ -1,29 +0,0 @@
|
||||
package org.cryptomator.data.cloud.onedrive.graph;
|
||||
|
||||
import com.microsoft.graph.core.GraphErrorCodes;
|
||||
|
||||
/**
|
||||
* An exception from the client.
|
||||
*/
|
||||
public class ClientException extends com.microsoft.graph.core.ClientException {
|
||||
|
||||
private static final long serialVersionUID = -10662352567392559L;
|
||||
|
||||
private final Enum<GraphErrorCodes> errorCode;
|
||||
|
||||
/**
|
||||
* Creates the client exception
|
||||
*
|
||||
* @param message the message to display
|
||||
* @param ex the exception from
|
||||
*/
|
||||
public ClientException(final String message, final Throwable ex, Enum<GraphErrorCodes> errorCode) {
|
||||
super(message, ex);
|
||||
|
||||
this.errorCode = errorCode;
|
||||
}
|
||||
|
||||
public Enum<GraphErrorCodes> errorCode() {
|
||||
return errorCode;
|
||||
}
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
package org.cryptomator.data.cloud.onedrive.graph;
|
||||
|
||||
import android.app.Activity;
|
||||
|
||||
import com.microsoft.graph.authentication.IAuthenticationProvider;
|
||||
|
||||
/**
|
||||
* An authentication adapter for signing requests, logging in, and logging out.
|
||||
*/
|
||||
public interface IAuthenticationAdapter extends IAuthenticationProvider {
|
||||
|
||||
/**
|
||||
* Logs out the user
|
||||
*
|
||||
* @param callback The callback when the logout is complete or an error occurs
|
||||
*/
|
||||
void logout(final ICallback<Void> callback);
|
||||
|
||||
/**
|
||||
* Login a user by popping UI
|
||||
*
|
||||
* @param activity The current activity
|
||||
* @param callback The callback when the login is complete or an error occurs
|
||||
*/
|
||||
void login(final Activity activity, final ICallback<String> callback);
|
||||
|
||||
/**
|
||||
* Login a user with no ui
|
||||
*
|
||||
* @param callback The callback when the login is complete or an error occurs
|
||||
*/
|
||||
void loginSilent(final ICallback<Void> callback);
|
||||
|
||||
/**
|
||||
* Gets the access token for the session of a logged in user
|
||||
*
|
||||
* @return the access token
|
||||
*/
|
||||
String getAccessToken() throws ClientException;
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
package org.cryptomator.data.cloud.onedrive.graph;
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Copyright (c) 2017 Microsoft Corporation
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* A callback that describes how to deal with success and failure
|
||||
*
|
||||
* @param <Result> the result type of the successful action
|
||||
*/
|
||||
public interface ICallback<Result> {
|
||||
|
||||
/**
|
||||
* How successful results are handled
|
||||
*
|
||||
* @param result the result
|
||||
*/
|
||||
void success(final Result result);
|
||||
|
||||
/**
|
||||
* How failures are handled
|
||||
*
|
||||
* @param ex the exception
|
||||
*/
|
||||
void failure(final ClientException ex);
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
package org.cryptomator.data.cloud.onedrive.graph;
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Copyright (c) 2017 Microsoft Corporation
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* A callback that describes how to deal with success, failure, and progress
|
||||
*
|
||||
* @param <Result> the result type of the successful action
|
||||
*/
|
||||
public interface IProgressCallback<Result> extends com.microsoft.graph.concurrency.IProgressCallback<Result> {
|
||||
|
||||
/**
|
||||
* How progress updates are handled for this callback
|
||||
*
|
||||
* @param current the current amount of progress
|
||||
* @param max the max amount of progress
|
||||
*/
|
||||
void progress(final long current, final long max);
|
||||
}
|
@ -1,275 +0,0 @@
|
||||
package org.cryptomator.data.cloud.onedrive.graph;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
|
||||
import com.microsoft.graph.http.IHttpRequest;
|
||||
import com.microsoft.graph.options.HeaderOption;
|
||||
import com.microsoft.services.msa.LiveAuthClient;
|
||||
import com.microsoft.services.msa.LiveAuthException;
|
||||
import com.microsoft.services.msa.LiveAuthListener;
|
||||
import com.microsoft.services.msa.LiveConnectSession;
|
||||
import com.microsoft.services.msa.LiveStatus;
|
||||
|
||||
import org.cryptomator.util.crypto.CredentialCryptor;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
import static com.microsoft.graph.core.GraphErrorCodes.AUTHENTICATION_FAILURE;
|
||||
|
||||
/**
|
||||
* Supports login, logout, and signing requests with authorization information.
|
||||
*/
|
||||
public abstract class MSAAuthAndroidAdapter implements IAuthenticationAdapter {
|
||||
|
||||
/**
|
||||
* The authorization header name.
|
||||
*/
|
||||
private static final String AUTHORIZATION_HEADER_NAME = "Authorization";
|
||||
|
||||
/**
|
||||
* The bearer prefix.
|
||||
*/
|
||||
private static final String OAUTH_BEARER_PREFIX = "bearer ";
|
||||
|
||||
/**
|
||||
* The live auth client.
|
||||
*/
|
||||
private final LiveAuthClient mLiveAuthClient;
|
||||
private Context context;
|
||||
|
||||
/**
|
||||
* Create a new instance of the provider
|
||||
*
|
||||
* @param context the application context instance
|
||||
* @param refreshToken
|
||||
*/
|
||||
protected MSAAuthAndroidAdapter(final Context context, String refreshToken) {
|
||||
this.context = context;
|
||||
mLiveAuthClient = new LiveAuthClient(context, getClientId(), Arrays.asList(getScopes()), MicrosoftOAuth2Endpoint.getInstance(), refreshToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* The client id for this authenticator.
|
||||
* http://graph.microsoft.io/en-us/app-registration
|
||||
*
|
||||
* @return The client id.
|
||||
*/
|
||||
protected abstract String getClientId();
|
||||
|
||||
/**
|
||||
* The scopes for this application.
|
||||
* http://graph.microsoft.io/en-us/docs/authorization/permission_scopes
|
||||
*
|
||||
* @return The scopes for this application.
|
||||
*/
|
||||
protected abstract String[] getScopes();
|
||||
|
||||
@Override
|
||||
public void authenticateRequest(final IHttpRequest request) {
|
||||
Timber.tag("MSAAuthAndroidAdapter").d("Authenticating request, %s", request.getRequestUrl());
|
||||
|
||||
// If the request already has an authorization header, do not intercept it.
|
||||
for (final HeaderOption option : request.getHeaders()) {
|
||||
if (option.getName().equals(AUTHORIZATION_HEADER_NAME)) {
|
||||
Timber.tag("MSAAuthAndroidAdapter").d("Found an existing authorization header!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
final String accessToken = getAccessToken();
|
||||
request.addHeader(AUTHORIZATION_HEADER_NAME, OAUTH_BEARER_PREFIX + accessToken);
|
||||
} catch (ClientException e) {
|
||||
final String message = "Unable to authenticate request, No active account found";
|
||||
final ClientException exception = new ClientException(message, e, AUTHENTICATION_FAILURE);
|
||||
Timber.tag("MSAAuthAndroidAdapter").e(exception, message);
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAccessToken() throws ClientException {
|
||||
if (hasValidSession()) {
|
||||
Timber.tag("MSAAuthAndroidAdapter").d("Found account information");
|
||||
if (mLiveAuthClient.getSession().isExpired()) {
|
||||
Timber.tag("MSAAuthAndroidAdapter").d("Account access token is expired, refreshing");
|
||||
loginSilentBlocking();
|
||||
}
|
||||
return mLiveAuthClient.getSession().getAccessToken();
|
||||
} else {
|
||||
final String message = "Unable to get access token, No active account found";
|
||||
final ClientException exception = new ClientException(message, null, AUTHENTICATION_FAILURE);
|
||||
Timber.tag("MSAAuthAndroidAdapter").e(exception, message);
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logout(final ICallback<Void> callback) {
|
||||
Timber.tag("MSAAuthAndroidAdapter").d("Logout started");
|
||||
|
||||
if (callback == null) {
|
||||
throw new IllegalArgumentException("callback");
|
||||
}
|
||||
|
||||
mLiveAuthClient.logout(new LiveAuthListener() {
|
||||
@Override
|
||||
public void onAuthComplete(final LiveStatus status, final LiveConnectSession session, final Object userState) {
|
||||
Timber.tag("MSAAuthAndroidAdapter").d("Logout complete");
|
||||
callback.success(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthError(final LiveAuthException exception, final Object userState) {
|
||||
final ClientException clientException = new ClientException("Logout failure", exception, AUTHENTICATION_FAILURE);
|
||||
Timber.tag("MSAAuthAndroidAdapter").e(clientException);
|
||||
callback.failure(clientException);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void login(final Activity activity, final ICallback<String> callback) {
|
||||
Timber.tag("MSAAuthAndroidAdapter").d("Login started");
|
||||
|
||||
if (callback == null) {
|
||||
throw new IllegalArgumentException("callback");
|
||||
}
|
||||
|
||||
if (hasValidSession()) {
|
||||
Timber.tag("MSAAuthAndroidAdapter").d("Already logged in");
|
||||
callback.success(null);
|
||||
return;
|
||||
}
|
||||
|
||||
final LiveAuthListener listener = new LiveAuthListener() {
|
||||
@Override
|
||||
public void onAuthComplete(final LiveStatus status, final LiveConnectSession session, final Object userState) {
|
||||
Timber.tag("MSAAuthAndroidAdapter").d(String.format("LiveStatus: %s, LiveConnectSession good?: %s, UserState %s", status, session != null, userState));
|
||||
|
||||
if (status == LiveStatus.NOT_CONNECTED && session.getRefreshToken() == null) {
|
||||
Timber.tag("MSAAuthAndroidAdapter").d("Received invalid login failure from silent authentication, ignoring.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (status == LiveStatus.CONNECTED) {
|
||||
Timber.tag("MSAAuthAndroidAdapter").d("Login completed");
|
||||
callback.success(encrypt(session.getRefreshToken()));
|
||||
return;
|
||||
}
|
||||
|
||||
final ClientException clientException = new ClientException("Unable to login successfully", null, AUTHENTICATION_FAILURE);
|
||||
Timber.tag("MSAAuthAndroidAdapter").e(clientException);
|
||||
callback.failure(clientException);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthError(final LiveAuthException exception, final Object userState) {
|
||||
final ClientException clientException = new ClientException("Login failure", exception, AUTHENTICATION_FAILURE);
|
||||
Timber.tag("MSAAuthAndroidAdapter").e(clientException);
|
||||
callback.failure(clientException);
|
||||
}
|
||||
};
|
||||
|
||||
// Make sure the login process is started with the current activity information
|
||||
activity.runOnUiThread(() -> mLiveAuthClient.login(activity, listener));
|
||||
}
|
||||
|
||||
private String encrypt(String refreshToken) {
|
||||
if (refreshToken == null) {
|
||||
return null;
|
||||
}
|
||||
return CredentialCryptor //
|
||||
.getInstance(context) //
|
||||
.encrypt(refreshToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Login a user with no ui
|
||||
*
|
||||
* @param callback The callback when the login is complete or an error occurs
|
||||
*/
|
||||
@Override
|
||||
public void loginSilent(final ICallback<Void> callback) {
|
||||
Timber.tag("MSAAuthAndroidAdapter").d("Login silent started");
|
||||
|
||||
if (callback == null) {
|
||||
throw new IllegalArgumentException("callback");
|
||||
}
|
||||
|
||||
final LiveAuthListener listener = new LiveAuthListener() {
|
||||
@Override
|
||||
public void onAuthComplete(final LiveStatus status, final LiveConnectSession session, final Object userState) {
|
||||
Timber.tag("MSAAuthAndroidAdapter").d(String.format("LiveStatus: %s, LiveConnectSession good?: %s, UserState %s", status, session != null, userState));
|
||||
|
||||
if (status == LiveStatus.CONNECTED) {
|
||||
Timber.tag("MSAAuthAndroidAdapter").d("Login completed");
|
||||
callback.success(null);
|
||||
return;
|
||||
}
|
||||
|
||||
final ClientException clientException = new ClientException("Unable to login silently", null, AUTHENTICATION_FAILURE);
|
||||
Timber.tag("MSAAuthAndroidAdapter").e(clientException);
|
||||
callback.failure(clientException);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthError(final LiveAuthException exception, final Object userState) {
|
||||
final ClientException clientException = new ClientException("Unable to login silently", null, AUTHENTICATION_FAILURE);
|
||||
Timber.tag("MSAAuthAndroidAdapter").e(clientException);
|
||||
callback.failure(clientException);
|
||||
}
|
||||
};
|
||||
|
||||
mLiveAuthClient.loginSilent(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Login silently while blocking for the call to return
|
||||
*
|
||||
* @return the result of the login attempt
|
||||
* @throws ClientException The exception if there was an issue during the login attempt
|
||||
*/
|
||||
private Void loginSilentBlocking() throws ClientException {
|
||||
Timber.tag("MSAAuthAndroidAdapter").d("Login silent blocking started");
|
||||
final SimpleWaiter waiter = new SimpleWaiter();
|
||||
final AtomicReference<Void> returnValue = new AtomicReference<>();
|
||||
final AtomicReference<ClientException> exceptionValue = new AtomicReference<>();
|
||||
|
||||
loginSilent(new ICallback<Void>() {
|
||||
@Override
|
||||
public void success(final Void aVoid) {
|
||||
returnValue.set(aVoid);
|
||||
waiter.signal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failure(ClientException ex) {
|
||||
exceptionValue.set(ex);
|
||||
waiter.signal();
|
||||
}
|
||||
});
|
||||
|
||||
waiter.waitForSignal();
|
||||
|
||||
// noinspection ThrowableResultOfMethodCallIgnored
|
||||
if (exceptionValue.get() != null) {
|
||||
throw exceptionValue.get();
|
||||
}
|
||||
|
||||
return returnValue.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the session object valid
|
||||
*
|
||||
* @return true, if the session is valid (but not necessary unexpired)
|
||||
*/
|
||||
private boolean hasValidSession() {
|
||||
return mLiveAuthClient.getSession() != null && mLiveAuthClient.getSession().getAccessToken() != null;
|
||||
}
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
package org.cryptomator.data.cloud.onedrive.graph;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import com.microsoft.services.msa.OAuthConfig;
|
||||
|
||||
import org.cryptomator.data.BuildConfig;
|
||||
|
||||
class MicrosoftOAuth2Endpoint implements OAuthConfig {
|
||||
|
||||
/**
|
||||
* The current instance of this class
|
||||
*/
|
||||
private static final MicrosoftOAuth2Endpoint sInstance = new MicrosoftOAuth2Endpoint();
|
||||
|
||||
/**
|
||||
* The current instance of this class
|
||||
*
|
||||
* @return The instance
|
||||
*/
|
||||
static MicrosoftOAuth2Endpoint getInstance() {
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri getAuthorizeUri() {
|
||||
return Uri.parse("https://login.microsoftonline.com/common/oauth2/v2.0/authorize");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri getDesktopUri() {
|
||||
return Uri.parse(BuildConfig.ONEDRIVE_API_REDIRCT_URI);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri getLogoutUri() {
|
||||
return Uri.parse("https://login.microsoftonline.com/common/oauth2/v2.0/logout");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri getTokenUri() {
|
||||
return Uri.parse("https://login.microsoftonline.com/common/oauth2/v2.0/token");
|
||||
}
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
package org.cryptomator.data.cloud.onedrive.graph;
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Copyright (c) 2015 Microsoft Corporation
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* A simple signal/waiter interface for synchronizing multi-threaded actions.
|
||||
*/
|
||||
public class SimpleWaiter {
|
||||
|
||||
/**
|
||||
* The internal lock object for this waiter.
|
||||
*/
|
||||
private final Object mInternalLock = new Object();
|
||||
|
||||
/**
|
||||
* Indicates if this waiter has been triggered.
|
||||
*/
|
||||
private boolean mTriggerState;
|
||||
|
||||
/**
|
||||
* BLOCKING: Waits for the signal to be triggered, or returns immediately if it has already been triggered.
|
||||
*/
|
||||
public void waitForSignal() {
|
||||
synchronized (mInternalLock) {
|
||||
if (this.mTriggerState) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mInternalLock.wait();
|
||||
} catch (final InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers the signal for this waiter.
|
||||
*/
|
||||
public void signal() {
|
||||
synchronized (mInternalLock) {
|
||||
mTriggerState = true;
|
||||
mInternalLock.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ package org.cryptomator.data.cloud.webdav
|
||||
|
||||
import android.content.Context
|
||||
import org.cryptomator.data.cloud.webdav.network.ConnectionHandlerHandlerImpl
|
||||
import org.cryptomator.data.cloud.webdav.network.ServerNotWebdavCompatibleException
|
||||
import org.cryptomator.data.util.CopyStream
|
||||
import org.cryptomator.data.util.TransferredBytesAwareInputStream
|
||||
import org.cryptomator.data.util.TransferredBytesAwareOutputStream
|
||||
@ -14,6 +15,7 @@ import org.cryptomator.domain.exception.FatalBackendException
|
||||
import org.cryptomator.domain.exception.NotFoundException
|
||||
import org.cryptomator.domain.exception.ParentFolderDoesNotExistException
|
||||
import org.cryptomator.domain.exception.ParentFolderIsNullException
|
||||
import org.cryptomator.domain.exception.authentication.WebDavNotSupportedException
|
||||
import org.cryptomator.domain.usecases.ProgressAware
|
||||
import org.cryptomator.domain.usecases.cloud.DataSource
|
||||
import org.cryptomator.domain.usecases.cloud.DownloadState
|
||||
@ -142,7 +144,11 @@ internal class WebDavImpl(private val cloud: WebDavCloud, private val connection
|
||||
|
||||
@Throws(BackendException::class)
|
||||
fun checkAuthenticationAndServerCompatibility(url: String) {
|
||||
connectionHandler.checkAuthenticationAndServerCompatibility(url)
|
||||
try {
|
||||
connectionHandler.checkAuthenticationAndServerCompatibility(url)
|
||||
} catch (ex: ServerNotWebdavCompatibleException) {
|
||||
throw WebDavNotSupportedException(cloud)
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(BackendException::class, IOException::class)
|
||||
|
@ -0,0 +1,7 @@
|
||||
package org.cryptomator.data.cloud.webdav.network;
|
||||
|
||||
import org.cryptomator.domain.exception.BackendException;
|
||||
|
||||
public class ServerNotWebdavCompatibleException extends BackendException {
|
||||
|
||||
}
|
@ -10,7 +10,6 @@ import org.cryptomator.domain.exception.FatalBackendException
|
||||
import org.cryptomator.domain.exception.ForbiddenException
|
||||
import org.cryptomator.domain.exception.NotFoundException
|
||||
import org.cryptomator.domain.exception.ParentFolderDoesNotExistException
|
||||
import org.cryptomator.domain.exception.ServerNotWebdavCompatibleException
|
||||
import org.cryptomator.domain.exception.TypeMismatchException
|
||||
import org.cryptomator.domain.exception.UnauthorizedException
|
||||
import org.xmlpull.v1.XmlPullParserException
|
||||
@ -18,7 +17,6 @@ import java.io.ByteArrayInputStream
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.net.HttpURLConnection
|
||||
import java.util.ArrayList
|
||||
import java.util.Collections
|
||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
import okhttp3.Request
|
||||
|
@ -1,5 +1,7 @@
|
||||
package org.cryptomator.data.db;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
@ -10,8 +12,6 @@ import java.util.Map;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
@Singleton
|
||||
class DatabaseUpgrades {
|
||||
|
||||
@ -28,7 +28,9 @@ class DatabaseUpgrades {
|
||||
Upgrade6To7 upgrade6To7, //
|
||||
Upgrade7To8 upgrade7To8, //
|
||||
Upgrade8To9 upgrade8To9, //
|
||||
Upgrade9To10 upgrade9To10) {
|
||||
Upgrade9To10 upgrade9To10, //
|
||||
Upgrade10To11 upgrade10To11
|
||||
) {
|
||||
|
||||
availableUpgrades = defineUpgrades( //
|
||||
upgrade0To1, //
|
||||
@ -40,11 +42,8 @@ class DatabaseUpgrades {
|
||||
upgrade6To7, //
|
||||
upgrade7To8, //
|
||||
upgrade8To9, //
|
||||
upgrade9To10);
|
||||
}
|
||||
|
||||
private static Comparator<DatabaseUpgrade> reverseOrder() {
|
||||
return (a, b) -> b.compareTo(a);
|
||||
upgrade9To10, //
|
||||
upgrade10To11);
|
||||
}
|
||||
|
||||
private Map<Integer, List<DatabaseUpgrade>> defineUpgrades(DatabaseUpgrade... upgrades) {
|
||||
@ -56,7 +55,7 @@ class DatabaseUpgrades {
|
||||
result.get(upgrade.from()).add(upgrade);
|
||||
}
|
||||
for (List<DatabaseUpgrade> list : result.values()) {
|
||||
Collections.sort(list, reverseOrder());
|
||||
Collections.sort(list, Comparator.reverseOrder());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
75
data/src/main/java/org/cryptomator/data/db/Upgrade10To11.kt
Normal file
75
data/src/main/java/org/cryptomator/data/db/Upgrade10To11.kt
Normal file
@ -0,0 +1,75 @@
|
||||
package org.cryptomator.data.db
|
||||
|
||||
import org.greenrobot.greendao.database.Database
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
internal class Upgrade10To11 @Inject constructor() : DatabaseUpgrade(10, 11) {
|
||||
private val defaultThreshold = 220
|
||||
private val defaultVaultFormat = 8
|
||||
private val onedriveCloudId = 3L
|
||||
|
||||
override fun internalApplyTo(db: Database, origin: Int) {
|
||||
db.beginTransaction()
|
||||
try {
|
||||
addFormatAndShorteningToDbEntity(db)
|
||||
addDefaultFormatAndShorteningThresholdToVaults(db)
|
||||
|
||||
deleteOnedriveCloudIfNotSetUp(db)
|
||||
|
||||
db.setTransactionSuccessful()
|
||||
} finally {
|
||||
db.endTransaction()
|
||||
}
|
||||
}
|
||||
|
||||
private fun addFormatAndShorteningToDbEntity(db: Database) {
|
||||
Sql.alterTable("VAULT_ENTITY").renameTo("VAULT_ENTITY_OLD").executeOn(db)
|
||||
Sql.createTable("VAULT_ENTITY") //
|
||||
.id() //
|
||||
.optionalInt("FOLDER_CLOUD_ID") //
|
||||
.optionalText("FOLDER_PATH") //
|
||||
.optionalText("FOLDER_NAME") //
|
||||
.requiredText("CLOUD_TYPE") //
|
||||
.optionalText("PASSWORD") //
|
||||
.optionalInt("POSITION") //
|
||||
.optionalInt("FORMAT") //
|
||||
.optionalInt("SHORTENING_THRESHOLD") //
|
||||
.foreignKey("FOLDER_CLOUD_ID", "CLOUD_ENTITY", Sql.SqlCreateTableBuilder.ForeignKeyBehaviour.ON_DELETE_SET_NULL) //
|
||||
.executeOn(db)
|
||||
|
||||
Sql.insertInto("VAULT_ENTITY") //
|
||||
.select("_id", "FOLDER_CLOUD_ID", "FOLDER_PATH", "FOLDER_NAME", "PASSWORD", "POSITION", "CLOUD_ENTITY.TYPE") //
|
||||
.columns("_id", "FOLDER_CLOUD_ID", "FOLDER_PATH", "FOLDER_NAME", "PASSWORD", "POSITION", "CLOUD_TYPE") //
|
||||
.from("VAULT_ENTITY_OLD") //
|
||||
.join("CLOUD_ENTITY", "VAULT_ENTITY_OLD.FOLDER_CLOUD_ID") //
|
||||
.executeOn(db)
|
||||
|
||||
Sql.dropIndex("IDX_VAULT_ENTITY_FOLDER_PATH_FOLDER_CLOUD_ID").executeOn(db)
|
||||
|
||||
Sql.createUniqueIndex("IDX_VAULT_ENTITY_FOLDER_PATH_FOLDER_CLOUD_ID") //
|
||||
.on("VAULT_ENTITY") //
|
||||
.asc("FOLDER_PATH") //
|
||||
.asc("FOLDER_CLOUD_ID") //
|
||||
.executeOn(db)
|
||||
|
||||
Sql.dropTable("VAULT_ENTITY_OLD").executeOn(db)
|
||||
}
|
||||
|
||||
|
||||
private fun addDefaultFormatAndShorteningThresholdToVaults(db: Database) {
|
||||
Sql.update("VAULT_ENTITY")
|
||||
.set("FORMAT", Sql.toInteger(defaultVaultFormat))
|
||||
.set("SHORTENING_THRESHOLD", Sql.toInteger(defaultThreshold))
|
||||
.executeOn(db)
|
||||
}
|
||||
|
||||
private fun deleteOnedriveCloudIfNotSetUp(db: Database) {
|
||||
Sql.deleteFrom("CLOUD_ENTITY")
|
||||
.where("_id", Sql.eq(onedriveCloudId))
|
||||
.where("TYPE", Sql.eq("ONEDRIVE"))
|
||||
.where("ACCESS_TOKEN", Sql.isNull())
|
||||
.executeOn(db)
|
||||
}
|
||||
}
|
@ -29,6 +29,11 @@ public class VaultEntity extends DatabaseEntity {
|
||||
private String password;
|
||||
|
||||
private Integer position;
|
||||
|
||||
private Integer format;
|
||||
|
||||
private Integer shorteningThreshold;
|
||||
|
||||
/**
|
||||
* Used for active entity operations.
|
||||
*/
|
||||
@ -42,8 +47,8 @@ public class VaultEntity extends DatabaseEntity {
|
||||
@Generated(hash = 229273163)
|
||||
private transient Long folderCloud__resolvedKey;
|
||||
|
||||
@Generated(hash = 825602374)
|
||||
public VaultEntity(Long id, Long folderCloudId, String folderPath, String folderName, @NotNull String cloudType, String password, Integer position) {
|
||||
@Generated(hash = 530735379)
|
||||
public VaultEntity(Long id, Long folderCloudId, String folderPath, String folderName, @NotNull String cloudType, String password, Integer position, Integer format, Integer shorteningThreshold) {
|
||||
this.id = id;
|
||||
this.folderCloudId = folderCloudId;
|
||||
this.folderPath = folderPath;
|
||||
@ -51,6 +56,8 @@ public class VaultEntity extends DatabaseEntity {
|
||||
this.cloudType = cloudType;
|
||||
this.password = password;
|
||||
this.position = position;
|
||||
this.format = format;
|
||||
this.shorteningThreshold = shorteningThreshold;
|
||||
}
|
||||
|
||||
@Generated(hash = 691253864)
|
||||
@ -182,6 +189,22 @@ public class VaultEntity extends DatabaseEntity {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
public Integer getFormat() {
|
||||
return this.format;
|
||||
}
|
||||
|
||||
public void setFormat(Integer format) {
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
public Integer getShorteningThreshold() {
|
||||
return this.shorteningThreshold;
|
||||
}
|
||||
|
||||
public void setShorteningThreshold(Integer shorteningThreshold) {
|
||||
this.shorteningThreshold = shorteningThreshold;
|
||||
}
|
||||
|
||||
/** called by internal mechanisms, do not call yourself. */
|
||||
@Generated(hash = 674742652)
|
||||
public void __setDaoSession(DaoSession daoSession) {
|
||||
|
@ -31,6 +31,8 @@ public class VaultEntityMapper extends EntityMapper<VaultEntity, Vault> {
|
||||
.withCloudType(CloudType.valueOf(entity.getCloudType())) //
|
||||
.withSavedPassword(entity.getPassword()) //
|
||||
.withPosition(entity.getPosition()) //
|
||||
.withFormat(entity.getFormat()) //
|
||||
.withShorteningThreshold(entity.getShorteningThreshold()) //
|
||||
.build();
|
||||
}
|
||||
|
||||
@ -53,6 +55,8 @@ public class VaultEntityMapper extends EntityMapper<VaultEntity, Vault> {
|
||||
entity.setCloudType(domainObject.getCloudType().name());
|
||||
entity.setPassword(domainObject.getPassword());
|
||||
entity.setPosition(domainObject.getPosition());
|
||||
entity.setFormat(domainObject.getFormat());
|
||||
entity.setShorteningThreshold(domainObject.getShorteningThreshold());
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ class CloudRepositoryImpl implements CloudRepository {
|
||||
|
||||
Cloud storedCloud = mapper.fromEntity(database.store(mapper.toEntity(cloud)));
|
||||
|
||||
dispatchingCloudContentRepository.removeCloudContentRepositoryFor(storedCloud);
|
||||
dispatchingCloudContentRepository.updateCloudContentRepositoryFor(storedCloud);
|
||||
database.clearCache();
|
||||
|
||||
return storedCloud;
|
||||
|
@ -204,6 +204,16 @@ class DispatchingCloudContentRepository @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun updateCloudContentRepositoryFor(cloud: Cloud) {
|
||||
val clouds = delegates.keys.iterator()
|
||||
while (clouds.hasNext()) {
|
||||
val current = clouds.next()
|
||||
if (cloudIsDelegateOfCryptoCloud(current, cloud)) {
|
||||
cryptoCloudContentRepositoryFactory.updateCloudInCryptor((current as CryptoCloud).vault, cloud)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun cloudIsDelegateOfCryptoCloud(potentialCryptoCloud: Cloud, cloud: Cloud): Boolean {
|
||||
if (potentialCryptoCloud is CryptoCloud) {
|
||||
val delegate = potentialCryptoCloud.vault.cloud
|
||||
@ -219,9 +229,9 @@ class DispatchingCloudContentRepository @Inject constructor(
|
||||
}
|
||||
|
||||
private fun delegateFor(cloud: Cloud): CloudContentRepository<Cloud, CloudNode, CloudFolder, CloudFile> {
|
||||
return delegates.getOrPut(cloud, {
|
||||
return delegates.getOrPut(cloud) {
|
||||
createCloudContentRepositoryFor(cloud)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun createCloudContentRepositoryFor(cloud: Cloud): CloudContentRepository<Cloud, CloudNode, CloudFolder, CloudFile> {
|
||||
|
@ -38,6 +38,7 @@ import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import okio.BufferedSink;
|
||||
import okio.BufferedSource;
|
||||
import okio.Okio;
|
||||
|
||||
@Singleton
|
||||
@ -112,20 +113,20 @@ public class UpdateCheckRepositoryImpl implements UpdateCheckRepository {
|
||||
|
||||
final Response response = httpClient.newCall(request).execute();
|
||||
|
||||
if (response.isSuccessful()) {
|
||||
final BufferedSink sink = Okio.buffer(Okio.sink(file));
|
||||
sink.writeAll(response.body().source());
|
||||
sink.flush();
|
||||
sink.close();
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
try (BufferedSource source = response.body().source(); BufferedSink sink = Okio.buffer(Okio.sink(file))) {
|
||||
sink.writeAll(source);
|
||||
sink.flush();
|
||||
|
||||
String apkSha256 = calculateSha256(file);
|
||||
String apkSha256 = calculateSha256(file);
|
||||
|
||||
if (!apkSha256.equals(entity.getApkSha256())) {
|
||||
file.delete();
|
||||
throw new HashMismatchUpdateCheckException(String.format( //
|
||||
"Sha of calculated hash (%s) doesn't match the specified one (%s)", //
|
||||
apkSha256, //
|
||||
entity.getApkSha256()));
|
||||
if (!apkSha256.equals(entity.getApkSha256())) {
|
||||
file.delete();
|
||||
throw new HashMismatchUpdateCheckException(String.format( //
|
||||
"Sha of calculated hash (%s) doesn't match the specified one (%s)", //
|
||||
apkSha256, //
|
||||
entity.getApkSha256()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new GeneralUpdateErrorException("Failed to load update file, status code is not correct: " + response.code());
|
||||
@ -174,7 +175,7 @@ public class UpdateCheckRepositoryImpl implements UpdateCheckRepository {
|
||||
}
|
||||
|
||||
private LatestVersion toLatestVersion(Response response) throws IOException, GeneralUpdateErrorException {
|
||||
if (response.isSuccessful()) {
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
return new LatestVersion(response.body().string());
|
||||
} else {
|
||||
throw new GeneralUpdateErrorException("Failed to update. Wrong status code in response from server: " + response.code());
|
||||
@ -182,7 +183,7 @@ public class UpdateCheckRepositoryImpl implements UpdateCheckRepository {
|
||||
}
|
||||
|
||||
private UpdateCheck toUpdateCheck(Response response, LatestVersion latestVersion) throws IOException, GeneralUpdateErrorException {
|
||||
if (response.isSuccessful()) {
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
final String releaseNote = response.body().string();
|
||||
return new UpdateCheckImpl(releaseNote, latestVersion);
|
||||
} else {
|
||||
|
@ -3,9 +3,9 @@ package org.cryptomator.data.util
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
enum class NetworkTimeout(val timeout: Long, val unit: TimeUnit) {
|
||||
CONNECTION(2L, TimeUnit.MINUTES), //
|
||||
READ(2L, TimeUnit.MINUTES), //
|
||||
WRITE(2L, TimeUnit.MINUTES);
|
||||
CONNECTION(1L, TimeUnit.MINUTES), //
|
||||
READ(1L, TimeUnit.MINUTES), //
|
||||
WRITE(1L, TimeUnit.MINUTES);
|
||||
|
||||
fun asMilliseconds(): Long {
|
||||
return unit.toMillis(timeout)
|
||||
|
@ -38,10 +38,16 @@ internal object GoogleDriveCloudNodeFactory {
|
||||
}
|
||||
|
||||
fun from(parent: GoogleDriveFolder, file: File): GoogleDriveNode {
|
||||
return if (isFolder(file)) {
|
||||
folder(parent, file)
|
||||
} else {
|
||||
file(parent, file)
|
||||
return when {
|
||||
isFolder(file) -> {
|
||||
folder(parent, file)
|
||||
}
|
||||
isShortcutFolder(file) -> {
|
||||
folder(parent, file.name, getNodePath(parent, file.name), file.shortcutDetails.targetId)
|
||||
}
|
||||
else -> {
|
||||
file(parent, file)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,6 +55,10 @@ internal object GoogleDriveCloudNodeFactory {
|
||||
return file.mimeType == "application/vnd.google-apps.folder"
|
||||
}
|
||||
|
||||
fun isShortcutFolder(file: File): Boolean {
|
||||
return file.mimeType == "application/vnd.google-apps.shortcut" && file.shortcutDetails.targetMimeType == "application/vnd.google-apps.folder"
|
||||
}
|
||||
|
||||
fun getNodePath(parent: GoogleDriveFolder, name: String): String {
|
||||
return parent.path + "/" + name
|
||||
}
|
||||
|
@ -25,7 +25,6 @@ import org.cryptomator.util.file.LruFileCacheUtil
|
||||
import org.cryptomator.util.file.LruFileCacheUtil.Companion.retrieveFromLruCache
|
||||
import java.io.IOException
|
||||
import java.io.OutputStream
|
||||
import java.util.ArrayList
|
||||
import timber.log.Timber
|
||||
|
||||
internal class GoogleDriveImpl(context: Context, googleDriveCloud: GoogleDriveCloud, idCache: GoogleDriveIdCache) {
|
||||
@ -57,7 +56,7 @@ internal class GoogleDriveImpl(context: Context, googleDriveCloud: GoogleDriveCl
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun findFile(parentDriveId: String?, name: String): File? {
|
||||
val fileListQuery = client().files().list().setFields("files(id,mimeType,name,size)")
|
||||
val fileListQuery = client().files().list().setFields("files(id,mimeType,name,size,shortcutDetails)")
|
||||
fileListQuery.q = "name contains '$name' and '$parentDriveId' in parents and trashed = false"
|
||||
return fileListQuery.execute().files.firstOrNull { it.name == name }
|
||||
}
|
||||
@ -99,6 +98,8 @@ internal class GoogleDriveImpl(context: Context, googleDriveCloud: GoogleDriveCl
|
||||
folder?.let {
|
||||
if (GoogleDriveCloudNodeFactory.isFolder(it)) {
|
||||
return idCache.cache(GoogleDriveCloudNodeFactory.folder(parent, it))
|
||||
} else if(GoogleDriveCloudNodeFactory.isShortcutFolder(it)) {
|
||||
return idCache.cache(GoogleDriveCloudNodeFactory.folder(parent, name, path, it.shortcutDetails.targetId))
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,7 +128,7 @@ internal class GoogleDriveImpl(context: Context, googleDriveCloud: GoogleDriveCl
|
||||
val fileListQuery = client() //
|
||||
.files() //
|
||||
.list() //
|
||||
.setFields("nextPageToken,files(id,mimeType,modifiedTime,name,size)") //
|
||||
.setFields("nextPageToken,files(id,mimeType,modifiedTime,name,size,shortcutDetails)") //
|
||||
.setPageSize(1000) //
|
||||
.setPageToken(pageToken)
|
||||
fileListQuery.q = "'" + folder.driveId + "' in parents and trashed = false"
|
||||
|
@ -3,8 +3,6 @@ apply plugin: 'kotlin-android'
|
||||
apply plugin: 'de.mannodermaus.android-junit5'
|
||||
|
||||
android {
|
||||
defaultPublishConfig "debug"
|
||||
|
||||
def globalConfiguration = rootProject.extensions.getByName("ext")
|
||||
|
||||
compileSdkVersion globalConfiguration["androidCompileSdkVersion"]
|
||||
@ -26,12 +24,12 @@ android {
|
||||
|
||||
coreLibraryDesugaringEnabled true
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
quiet true
|
||||
lint {
|
||||
abortOnError false
|
||||
ignoreWarnings true
|
||||
quiet true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
@ -55,7 +55,11 @@ public class OnedriveCloud implements Cloud {
|
||||
|
||||
@Override
|
||||
public boolean configurationMatches(Cloud cloud) {
|
||||
return true;
|
||||
return cloud instanceof OnedriveCloud && configurationMatches((OnedriveCloud) cloud);
|
||||
}
|
||||
|
||||
private boolean configurationMatches(OnedriveCloud cloud) {
|
||||
return username.equals(cloud.username);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
@ -1,5 +0,0 @@
|
||||
package org.cryptomator.domain.exception;
|
||||
|
||||
public class ServerNotWebdavCompatibleException extends BackendException {
|
||||
|
||||
}
|
@ -6,8 +6,6 @@ import org.cryptomator.domain.repository.VaultRepository;
|
||||
import org.cryptomator.generator.Parameter;
|
||||
import org.cryptomator.generator.UseCase;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@UseCase
|
||||
class DeleteVault {
|
||||
|
||||
@ -20,12 +18,7 @@ class DeleteVault {
|
||||
}
|
||||
|
||||
public Long execute() throws BackendException {
|
||||
Long vaultId = vaultRepository.delete(vault);
|
||||
|
||||
List<Vault> reorderVaults = MoveVaultHelper.Companion.reorderVaults(vaultRepository);
|
||||
MoveVaultHelper.Companion.updateVaultsInDatabase(reorderVaults, vaultRepository);
|
||||
|
||||
return vaultId;
|
||||
return vaultRepository.delete(vault);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,30 @@
|
||||
package org.cryptomator.domain.usecases.vault;
|
||||
|
||||
import org.cryptomator.domain.Vault;
|
||||
import org.cryptomator.domain.exception.BackendException;
|
||||
import org.cryptomator.domain.repository.VaultRepository;
|
||||
import org.cryptomator.generator.Parameter;
|
||||
import org.cryptomator.generator.UseCase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@UseCase
|
||||
class DeleteVaults {
|
||||
|
||||
private final VaultRepository vaultRepository;
|
||||
private final List<Vault> vaults;
|
||||
|
||||
public DeleteVaults(VaultRepository vaultRepository, @Parameter List<Vault> vaults) {
|
||||
this.vaultRepository = vaultRepository;
|
||||
this.vaults = vaults;
|
||||
}
|
||||
|
||||
public List<Long> execute() throws BackendException {
|
||||
List<Long> ids = new ArrayList<>();
|
||||
for (Vault vault : vaults) {
|
||||
ids.add(vaultRepository.delete(vault));
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
}
|
@ -3,7 +3,6 @@ package org.cryptomator.domain.usecases.vault;
|
||||
import org.cryptomator.domain.Vault
|
||||
import org.cryptomator.domain.repository.VaultRepository
|
||||
import java.util.Collections
|
||||
import java.util.Comparator
|
||||
|
||||
class MoveVaultHelper {
|
||||
|
||||
@ -40,7 +39,9 @@ class MoveVaultHelper {
|
||||
}
|
||||
|
||||
fun updateVaultsInDatabase(vaults: List<Vault>, vaultRepository: VaultRepository): List<Vault> {
|
||||
vaults.forEach { vault -> vaultRepository.store(vault) }
|
||||
for(vault in vaults) {
|
||||
vaultRepository.store(vault)
|
||||
}
|
||||
return vaultRepository.vaults()
|
||||
}
|
||||
}
|
||||
|
@ -7,18 +7,23 @@ import org.cryptomator.generator.Parameter;
|
||||
import org.cryptomator.generator.UseCase;
|
||||
|
||||
@UseCase
|
||||
class ReloadVault {
|
||||
class UpdateVaultParameterIfChangedRemotely {
|
||||
|
||||
private final VaultRepository vaultRepository;
|
||||
private final Vault vault;
|
||||
|
||||
public ReloadVault(VaultRepository vaultRepository, @Parameter Vault vault) {
|
||||
public UpdateVaultParameterIfChangedRemotely(VaultRepository vaultRepository, @Parameter Vault vault) {
|
||||
this.vaultRepository = vaultRepository;
|
||||
this.vault = vault;
|
||||
}
|
||||
|
||||
public Vault execute() throws BackendException {
|
||||
return vaultRepository.load(vault.getId());
|
||||
Vault oldVault = vaultRepository.load(vault.getId());
|
||||
if(oldVault.getFormat() == vault.getFormat() && oldVault.getShorteningThreshold() == vault.getShorteningThreshold()) {
|
||||
return vault;
|
||||
} else {
|
||||
return vaultRepository.store(vault);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because one or more lines are too long
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,6 @@
|
||||
- Zeige Dialog und Benachrichtigung an, wenn die Berechtigung "Dateien" widerrufen wird, erforderlich für den automatischen Upload
|
||||
- Das Abmelden von einer Cloud löscht nun auch die Anmeldeinformationen einer aktiven Verbindung zu ihr
|
||||
- Unterstützung für mehrere OneDrive-Konten hinzugefügt
|
||||
- Zugriff auf Tresore, die sich in "Shared with Me", "My Computer" und "My Drive" von Google Drive befinden, wurde mit Hilfe von Shortcuts hinzugefügt
|
||||
- Viele Übersetzungen hinzugefügt
|
||||
- Verbesserte Suchfunktion, nun wird eine "enthält"-Suche anstelle von "beginnt mit" verwendet
|
||||
- Absturz auf einigen Geräten während des Entsperrens aufgrund einer Keystore-Ausnahme behoben
|
||||
- Problem beim Löschen einer Cloud behoben
|
@ -1,2 +1,6 @@
|
||||
- Show information when "Storage" permission revoked, required for auto upload
|
||||
- Logging out of a cloud now also clears the credentials of an active connection to it
|
||||
- Added support for multiple OneDrive accounts
|
||||
- Added access to vaults located in "Shared with Me", "My Computer" and "My Drive" of Google Drive using shortcuts
|
||||
- Added a lot of translations
|
||||
- Improved search functionality, now a "contains" search is used instead of "starts with"
|
||||
- Fixed crash on some devices during unlock due to keystore exception
|
||||
- Fixed problem during deletion of a cloud
|
@ -1,8 +1,8 @@
|
||||
Cryptomator rend votre stockage dans le cloud beaucoup plus sûr. L'application chiffre les fichiers sur votre appareil mobile avant qu'ils ne soient envoyés dans votre cloud. Même si une tierce partie obtient un accès non autorisé à vos fichiers (par exemple, une attaque de pirates informatiques), vos fichiers sont à l'abri des regards indiscrets.
|
||||
Avec Cryptomator, la clé de vos données est dans vos mains. Cryptomator chiffre vos données rapidement et facilement. Vous les envoyez sur votre service cloud favori.
|
||||
|
||||
<b>SIMPLICITÉ</b>
|
||||
<b>SIMPLE D'UTILISATION</b>
|
||||
|
||||
Cryptomator a été développé en mettant l'accent sur la convivialité.
|
||||
Cryptomator est un outil d'auto-défense numérique. Il vous permet de protéger vos données stockées sur le cloud vous-même et en indépendance.
|
||||
|
||||
• Il suffit de créer un coffre-fort et d'y attribuer un mot de passe
|
||||
• Aucun compte ou configuration supplémentaire n'est nécessaire
|
||||
@ -10,30 +10,30 @@ Cryptomator a été développé en mettant l'accent sur la convivialité.
|
||||
|
||||
* à partir d'Android 6.0 et sur smartphones avec capteur biométrique d'empreintes digitales
|
||||
|
||||
<b>COMPATIBILITÉ</b>
|
||||
<b>COMPATIBLE</b>
|
||||
|
||||
Cryptomator est compatible avec les systèmes de stockage dans le cloud les plus couramment utilisés et disponible pour tous les principaux systèmes d'exploitation.
|
||||
|
||||
• Compatible avec Dropbox, Google Drive, OneDrive et les services de stockage dans le nuage basés sur WebDAV
|
||||
• Créer des coffres-forts dans le stockage local d'Android (par exemple, fonctionne avec des applications de synchronisation tierces)
|
||||
• Compatible avec Dropbox, Google Drive, OneDrive et les services de stockage dans le cloud basés sur WebDAV
|
||||
• Créer des coffres-forts sur le stockage local d'Android (fonctionne donc avec des applications de synchronisation tierces)
|
||||
• Accédez à vos coffres-forts sur tous vos appareils mobiles et ordinateurs
|
||||
|
||||
<b>SÉCURITÉ</b>
|
||||
<b>SÉCURISÉ</b>
|
||||
|
||||
Cryptomator pour Android est basé sur le projet open-source de Cryptomator pour Ordinateur de bureau.
|
||||
Vous n'avez pas à aveuglément accorder votre confiance à Cryptomator, car <a href="https://github.com/cryptomator/android">c'est un logiciel open-source</a>. Pour vous utilisateur, cela signifie que n'importe qui peut voir le code.
|
||||
|
||||
• Chiffrement du contenu et des noms de fichiers via AES et une longueur de clé de 256 bits
|
||||
• Le mot de passe du coffre-fort est sécurisé par cryptage pour une meilleure résistance aux attaque par force brut
|
||||
• Les coffre-forts sont automatiquement verrouillées après la mise en arrière-plan de l'application
|
||||
• La mise en œuvre de Crypto est basée sur la bibliothèque open-source CryptoLib et est documentée publiquement
|
||||
• Le mot de passe du coffre-fort est sécurisé par cryptage pour une meilleure résistance aux attaques par force brute
|
||||
• Les coffre-forts sont automatiquement verrouillés après la mise en arrière-plan de l'application
|
||||
• La mise en œuvre de Crypto est documentée publiquement
|
||||
|
||||
<b>UNE GÉNIALITUDE GÉNÉRAL</b>
|
||||
<b>LAURÉAT</b>
|
||||
|
||||
Cryptomator a reçu le prix de l'innovation CeBIT 2016 pour la sécurité pratique et la confidentialité. Nous sommes fiers d'assurer la sécurité et la confidentialité des centaines de milliers d'utilisateurs du Cryptomator.
|
||||
Cryptomator a reçu le prix de l'innovation CeBIT 2016 pour la sécurité pratique et la confidentialité. Nous sommes fiers d'assurer la sécurité et la confidentialité des centaines de milliers d'utilisateurs de Cryptomator.
|
||||
|
||||
<b>LA COMMUNAUTÉ CRYPTOMATOR</b>
|
||||
|
||||
Rejoignez la communauté de Cryptomator et participez aux conversations avec les autres utilisateurs de Cryptomator: https://community.cryptomator.org
|
||||
|
||||
- Suivez-nous sur Twitter @Cryptomator
|
||||
- Comme nous sur Facebook/Cryptomator
|
||||
- Suivez-nous sur Twitter <a href="https://twitter.com/Cryptomator">@Cryptomator</a>
|
||||
- Likez-nous sur Facebook <a href="https://facebook.com/Cryptomator">/Cryptomator</a>
|
||||
|
@ -1 +1 @@
|
||||
Verrouillé votre cloud: Prenez en mains la sécurité de vos données
|
||||
Verrouillez votre cloud: Prenez en main la sécurité de vos données
|
||||
|
@ -1,4 +1,8 @@
|
||||
<ul>
|
||||
<li>Show information when "Storage" permission revoked, required for auto upload</li>
|
||||
<li>Logging out of a cloud now also clears the credentials of an active connection to it</li>
|
||||
<li>Added support for multiple OneDrive accounts</li>
|
||||
<li>Added access to vaults located in "Shared with Me", "My Computer" and "My Drive" of Google Drive using shortcuts</li>
|
||||
<li>Added a lot of translations</li>
|
||||
<li>Improved search functionality, now a "contains" search is used instead of "starts with"</li>
|
||||
<li>Fixed crash on some devices during unlock due to keystore exception</li>
|
||||
<li>Fixed problem during deletion of a cloud</li>
|
||||
</ul>
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
Binary file not shown.
BIN
lib/google-http-client-1.41.4.jar
Normal file
BIN
lib/google-http-client-1.41.4.jar
Normal file
Binary file not shown.
Binary file not shown.
BIN
lib/google-http-client-android-1.41.4.jar
Normal file
BIN
lib/google-http-client-android-1.41.4.jar
Normal file
Binary file not shown.
@ -1 +0,0 @@
|
||||
Subproject commit 3b48a4973fddaec258edfb702fc242261d81cd1c
|
@ -1 +1 @@
|
||||
Subproject commit b2db89269e8e4059a7415e0364e427e71682700d
|
||||
Subproject commit dc4d0897f7917f026376d35f9a6eaf6edbc7115d
|
@ -3,6 +3,7 @@ apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
apply plugin: 'de.mannodermaus.android-junit5'
|
||||
apply from: 'prebuild.gradle'
|
||||
|
||||
android {
|
||||
signingConfigs {
|
||||
@ -38,11 +39,6 @@ android {
|
||||
coreLibraryDesugaringEnabled true
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
quiet true
|
||||
abortOnError false
|
||||
ignoreWarnings true
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
@ -51,9 +47,10 @@ android {
|
||||
shrinkResources false
|
||||
|
||||
buildConfigField "String", "DROPBOX_API_KEY", "\"" + getApiKey('DROPBOX_API_KEY') + "\""
|
||||
manifestPlaceholders = [DROPBOX_API_KEY: getApiKey('DROPBOX_API_KEY')]
|
||||
buildConfigField "String", "PCLOUD_CLIENT_ID", "\"" + getApiKey('PCLOUD_CLIENT_ID') + "\""
|
||||
|
||||
manifestPlaceholders = [DROPBOX_API_KEY: getApiKey('DROPBOX_API_KEY'), ONEDRIVE_API_KEY_DECODED: getOnedriveApiKey()]
|
||||
|
||||
resValue "string", "app_id", androidApplicationId
|
||||
}
|
||||
|
||||
@ -66,9 +63,10 @@ android {
|
||||
testCoverageEnabled false
|
||||
|
||||
buildConfigField "String", "DROPBOX_API_KEY", "\"" + getApiKey('DROPBOX_API_KEY_DEBUG') + "\""
|
||||
manifestPlaceholders = [DROPBOX_API_KEY: getApiKey('DROPBOX_API_KEY_DEBUG')]
|
||||
buildConfigField "String", "PCLOUD_CLIENT_ID", "\"" + getApiKey('PCLOUD_CLIENT_ID_DEBUG') + "\""
|
||||
|
||||
manifestPlaceholders = [DROPBOX_API_KEY: getApiKey('DROPBOX_API_KEY_DEBUG'), ONEDRIVE_API_KEY_DECODED: getOnedriveApiKey()]
|
||||
|
||||
applicationIdSuffix ".debug"
|
||||
versionNameSuffix '-DEBUG'
|
||||
|
||||
@ -105,10 +103,16 @@ android {
|
||||
java.srcDirs = ['src/main/java', 'src/main/java/', 'src/foss/java', 'src/foss/java/']
|
||||
}
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
exclude 'META-INF/jersey-module-version'
|
||||
exclude 'META-INF/DEPENDENCIES'
|
||||
resources {
|
||||
excludes += ['META-INF/jersey-module-version', 'META-INF/NOTICE.md', 'META-INF/DEPENDENCIES', 'META-INF/INDEX.LIST']
|
||||
}
|
||||
}
|
||||
|
||||
lint {
|
||||
abortOnError false
|
||||
ignoreWarnings true
|
||||
quiet true
|
||||
}
|
||||
}
|
||||
|
||||
@ -145,6 +149,7 @@ dependencies {
|
||||
// cloud
|
||||
implementation dependencies.dropbox
|
||||
implementation dependencies.msgraph
|
||||
implementation dependencies.msgraphAuth
|
||||
|
||||
playstoreImplementation(dependencies.googleApiServicesDrive) {
|
||||
exclude module: 'guava-jdk5'
|
||||
@ -248,6 +253,13 @@ static def getApiKey(key) {
|
||||
return System.getenv().getOrDefault(key, "")
|
||||
}
|
||||
|
||||
static def getOnedriveApiKey() {
|
||||
String onedrivePath = "" + getApiKey('ONEDRIVE_API_REDIRCT_URI')
|
||||
String idStr = onedrivePath.substring(onedrivePath.lastIndexOf('/') + 1)
|
||||
URI uri = new URI(idStr)
|
||||
return uri.path
|
||||
}
|
||||
|
||||
tasks.withType(Test) {
|
||||
testLogging {
|
||||
events "failed"
|
||||
|
36
presentation/prebuild.gradle
Normal file
36
presentation/prebuild.gradle
Normal file
@ -0,0 +1,36 @@
|
||||
import groovy.json.JsonOutput
|
||||
import groovy.json.JsonSlurper
|
||||
|
||||
task generateAppConfigurationFile() {
|
||||
def jsonSlurper = new JsonSlurper()
|
||||
|
||||
def apiKey = "" + getApiKey('ONEDRIVE_API_KEY')
|
||||
def redirectUri = "" + getApiKey('ONEDRIVE_API_REDIRCT_URI')
|
||||
|
||||
def jsonString = """
|
||||
{
|
||||
"client_id" : "${apiKey}",
|
||||
"authorization_user_agent" : "DEFAULT",
|
||||
"redirect_uri" : "${redirectUri}",
|
||||
"broker_redirect_uri_registered": true,
|
||||
"shared_device_mode_supported": true,
|
||||
"authorities" : [
|
||||
{
|
||||
"type": "AAD",
|
||||
"audience": {
|
||||
"type": "AzureADandPersonalMicrosoftAccount",
|
||||
"tenant_id": "common"
|
||||
}
|
||||
}
|
||||
]
|
||||
}"""
|
||||
|
||||
def config_file = new File('presentation/src/main/res/raw/auth_config_onedrive.json')
|
||||
config_file.write(JsonOutput.prettyPrint(JsonOutput.toJson(jsonSlurper.parseText(jsonString))))
|
||||
}
|
||||
|
||||
static def getApiKey(key) {
|
||||
return System.getenv().getOrDefault(key, "")
|
||||
}
|
||||
|
||||
build.dependsOn generateAppConfigurationFile
|
@ -6,9 +6,16 @@ import android.content.Intent.ACTION_OPEN_DOCUMENT_TREE
|
||||
import android.provider.DocumentsContract
|
||||
import android.widget.Toast
|
||||
import com.dropbox.core.android.Auth
|
||||
import org.cryptomator.data.cloud.onedrive.OnedriveClientFactory
|
||||
import org.cryptomator.data.cloud.onedrive.graph.ClientException
|
||||
import org.cryptomator.data.cloud.onedrive.graph.ICallback
|
||||
import com.microsoft.identity.client.AuthenticationCallback
|
||||
import com.microsoft.identity.client.IAccount
|
||||
import com.microsoft.identity.client.IAuthenticationResult
|
||||
import com.microsoft.identity.client.IMultipleAccountPublicClientApplication
|
||||
import com.microsoft.identity.client.IPublicClientApplication
|
||||
import com.microsoft.identity.client.PublicClientApplication
|
||||
import com.microsoft.identity.client.exception.MsalClientException
|
||||
import com.microsoft.identity.client.exception.MsalException
|
||||
import com.microsoft.identity.client.exception.MsalServiceException
|
||||
import com.microsoft.identity.client.exception.MsalUiRequiredException
|
||||
import org.cryptomator.data.util.X509CertificateHelper
|
||||
import org.cryptomator.domain.Cloud
|
||||
import org.cryptomator.domain.CloudType
|
||||
@ -225,29 +232,108 @@ class AuthenticateCloudPresenter @Inject constructor( //
|
||||
|
||||
private fun startAuthentication(cloud: CloudModel) {
|
||||
authenticationStarted = true
|
||||
val authenticationAdapter = OnedriveClientFactory.getAuthAdapter(context(), (cloud.toCloud() as OnedriveCloud).accessToken())
|
||||
authenticationAdapter.login(activity(), object : ICallback<String?> {
|
||||
override fun success(accessToken: String?) {
|
||||
if (accessToken == null) {
|
||||
Timber.tag("AuthicateCloudPrester").e("Onedrive access token is empty")
|
||||
|
||||
Toast.makeText(context(), R.string.notification_authenticating, Toast.LENGTH_SHORT).show()
|
||||
|
||||
PublicClientApplication.createMultipleAccountPublicClientApplication(
|
||||
context(),
|
||||
R.raw.auth_config_onedrive,
|
||||
object : IPublicClientApplication.IMultipleAccountApplicationCreatedListener {
|
||||
override fun onCreated(application: IMultipleAccountPublicClientApplication) {
|
||||
application.getAccounts(object : IPublicClientApplication.LoadAccountsCallback {
|
||||
override fun onTaskCompleted(accounts: List<IAccount>) {
|
||||
if (accounts.isEmpty()) {
|
||||
application.acquireToken(activity(), onedriveScopes(), getAuthInteractiveCallback(cloud))
|
||||
} else {
|
||||
accounts.find { account -> account.username == cloud.username() }?.let {
|
||||
application.acquireTokenSilentAsync(
|
||||
onedriveScopes(),
|
||||
it,
|
||||
"https://login.microsoftonline.com/common",
|
||||
getAuthSilentCallback(cloud, application)
|
||||
)
|
||||
} ?: application.acquireToken(activity(), onedriveScopes(), getAuthInteractiveCallback(cloud))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(e: MsalException) {
|
||||
Timber.tag("AuthenticateCloudPresenter").e(e, "Error to get accounts")
|
||||
failAuthentication(cloud.name())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onError(e: MsalException) {
|
||||
Timber.tag("AuthenticateCloudPresenter").i(e, "Error in configuration")
|
||||
failAuthentication(cloud.name())
|
||||
} else {
|
||||
showProgress(ProgressModel(ProgressStateModel.AUTHENTICATION))
|
||||
handleAuthenticationResult(cloud, accessToken)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun getAuthSilentCallback(cloud: CloudModel, application: IMultipleAccountPublicClientApplication): AuthenticationCallback {
|
||||
return object : AuthenticationCallback {
|
||||
|
||||
override fun onSuccess(authenticationResult: IAuthenticationResult) {
|
||||
Timber.tag("AuthenticateCloudPresenter").i("Successfully authenticated")
|
||||
handleAuthenticationResult(cloud, authenticationResult.accessToken)
|
||||
}
|
||||
|
||||
override fun onError(e: MsalException) {
|
||||
Timber.tag("AuthenticateCloudPresenter").e(e, "Failed to acquireToken")
|
||||
when (e) {
|
||||
is MsalClientException -> {
|
||||
/* Exception inside MSAL, more info inside MsalError.java */
|
||||
failAuthentication(cloud.name())
|
||||
}
|
||||
is MsalServiceException -> {
|
||||
/* Exception when communicating with the STS, likely config issue */
|
||||
failAuthentication(cloud.name())
|
||||
}
|
||||
is MsalUiRequiredException -> {
|
||||
/* Tokens expired or no session, retry with interactive */
|
||||
application.acquireToken(activity(), onedriveScopes(), getAuthInteractiveCallback(cloud))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun failure(ex: ClientException) {
|
||||
Timber.tag("AuthicateCloudPrester").e(ex)
|
||||
override fun onCancel() {
|
||||
Timber.tag("AuthenticateCloudPresenter").i("User cancelled login")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAuthInteractiveCallback(cloud: CloudModel): AuthenticationCallback {
|
||||
return object : AuthenticationCallback {
|
||||
|
||||
override fun onSuccess(authenticationResult: IAuthenticationResult) {
|
||||
Timber.tag("AuthenticateCloudPresenter").i("Successfully authenticated")
|
||||
handleAuthenticationResult(cloud, authenticationResult.accessToken, authenticationResult.account.username)
|
||||
}
|
||||
|
||||
override fun onError(e: MsalException) {
|
||||
Timber.tag("AuthenticateCloudPresenter").e(e, "Successfully authenticated")
|
||||
failAuthentication(cloud.name())
|
||||
}
|
||||
})
|
||||
|
||||
override fun onCancel() {
|
||||
Timber.tag("AuthenticateCloudPresenter").i("User cancelled login")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleAuthenticationResult(cloud: CloudModel, accessToken: String) {
|
||||
getUsernameAndSuceedAuthentication( //
|
||||
OnedriveCloud.aCopyOf(cloud.toCloud() as OnedriveCloud) //
|
||||
.withAccessToken(accessToken) //
|
||||
.withAccessToken(encrypt(accessToken)) //
|
||||
.build()
|
||||
)
|
||||
}
|
||||
|
||||
private fun handleAuthenticationResult(cloud: CloudModel, accessToken: String, username: String) {
|
||||
getUsernameAndSuceedAuthentication( //
|
||||
OnedriveCloud.aCopyOf(cloud.toCloud() as OnedriveCloud) //
|
||||
.withAccessToken(encrypt(accessToken)) //
|
||||
.withUsername(username)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
@ -512,6 +598,10 @@ class AuthenticateCloudPresenter @Inject constructor( //
|
||||
companion object {
|
||||
|
||||
const val WEBDAV_ACCEPTED_UNTRUSTED_CERTIFICATE = "acceptedUntrustedCertificate"
|
||||
|
||||
fun onedriveScopes(): Array<String> {
|
||||
return arrayOf("User.Read", "Files.ReadWrite")
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
|
@ -159,6 +159,17 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name="com.microsoft.identity.client.BrowserTabActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data
|
||||
android:host="org.cryptomator"
|
||||
android:path="/${ONEDRIVE_API_KEY_DECODED}"
|
||||
android:scheme="msauth" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
|
@ -23,6 +23,7 @@ enum class CloudTypeModel(builder: Builder) {
|
||||
.withCloudImageResource(R.drawable.onedrive) //
|
||||
.withVaultImageResource(R.drawable.onedrive_vault) //
|
||||
.withVaultSelectedImageResource(R.drawable.onedrive_vault_selected)
|
||||
.withMultiInstances()
|
||||
), //
|
||||
PCLOUD(
|
||||
Builder("PCLOUD", R.string.cloud_names_pcloud) //
|
||||
|
@ -11,7 +11,7 @@ class CryptoCloudModel(cloud: Cloud) : CloudModel(cloud) {
|
||||
throw IllegalStateException("Should not be invoked")
|
||||
}
|
||||
|
||||
override fun username(): String? {
|
||||
override fun username(): String {
|
||||
return ""
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,10 @@ class OnedriveCloudModel(cloud: Cloud) : CloudModel(cloud) {
|
||||
return cloud().username()
|
||||
}
|
||||
|
||||
fun id(): Long? {
|
||||
return cloud().id()
|
||||
}
|
||||
|
||||
private fun cloud(): OnedriveCloud {
|
||||
return toCloud() as OnedriveCloud
|
||||
}
|
||||
|
@ -6,9 +6,12 @@ import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.provider.DocumentsContract
|
||||
import android.widget.Toast
|
||||
import org.cryptomator.data.cloud.crypto.CryptoFolder
|
||||
import org.cryptomator.domain.Cloud
|
||||
import org.cryptomator.domain.CloudFile
|
||||
import org.cryptomator.domain.CloudFolder
|
||||
import org.cryptomator.domain.CloudNode
|
||||
import org.cryptomator.domain.Vault
|
||||
import org.cryptomator.domain.di.PerView
|
||||
import org.cryptomator.domain.exception.CloudNodeAlreadyExistsException
|
||||
import org.cryptomator.domain.exception.EmptyDirFileException
|
||||
@ -20,6 +23,7 @@ import org.cryptomator.domain.usecases.CloudFolderRecursiveListing
|
||||
import org.cryptomator.domain.usecases.CloudNodeRecursiveListing
|
||||
import org.cryptomator.domain.usecases.CopyDataUseCase
|
||||
import org.cryptomator.domain.usecases.DownloadFile
|
||||
import org.cryptomator.domain.usecases.GetDecryptedCloudForVaultUseCase
|
||||
import org.cryptomator.domain.usecases.ResultRenamed
|
||||
import org.cryptomator.domain.usecases.cloud.CreateFolderUseCase
|
||||
import org.cryptomator.domain.usecases.cloud.DeleteNodesUseCase
|
||||
@ -107,6 +111,7 @@ class BrowseFilesPresenter @Inject constructor( //
|
||||
private val moveFilesUseCase: MoveFilesUseCase, //
|
||||
private val moveFoldersUseCase: MoveFoldersUseCase, //
|
||||
private val getCloudListRecursiveUseCase: GetCloudListRecursiveUseCase, //
|
||||
private val getDecryptedCloudForVaultUseCase: GetDecryptedCloudForVaultUseCase, //
|
||||
private val contentResolverUtil: ContentResolverUtil, //
|
||||
private val addExistingVaultWorkflow: AddExistingVaultWorkflow, //
|
||||
private val createNewVaultWorkflow: CreateNewVaultWorkflow, //
|
||||
@ -129,6 +134,8 @@ class BrowseFilesPresenter @Inject constructor( //
|
||||
private lateinit var existingFilesForUpload: MutableMap<String, UploadFile>
|
||||
private lateinit var downloadFiles: MutableList<DownloadFile>
|
||||
|
||||
private var resumedAfterAuthentication = false
|
||||
|
||||
@InjectIntent
|
||||
lateinit var intent: BrowseFilesIntent
|
||||
|
||||
@ -203,6 +210,7 @@ class BrowseFilesPresenter @Inject constructor( //
|
||||
view?.showLoading(false)
|
||||
when {
|
||||
authenticationExceptionHandler.handleAuthenticationException(this@BrowseFilesPresenter, e, ActivityResultCallbacks.getCloudListAfterAuthentication(cloudFolderModel)) -> {
|
||||
resumedAfterAuthentication = true
|
||||
return
|
||||
}
|
||||
e is EmptyDirFileException -> {
|
||||
@ -222,14 +230,45 @@ class BrowseFilesPresenter @Inject constructor( //
|
||||
})
|
||||
}
|
||||
|
||||
@Callback
|
||||
@Callback(dispatchResultOkOnly = false)
|
||||
fun getCloudListAfterAuthentication(result: ActivityResult, cloudFolderModel: CloudFolderModel) {
|
||||
val cloudModel = result.getSingleResult(CloudModel::class.java)
|
||||
cloudFolderModel.toCloudNode().withCloud(cloudModel.toCloud())?.let {
|
||||
getCloudList(cloudFolderModelMapper.toModel(it))
|
||||
if(result.isResultOk) {
|
||||
val cloudModel = result.getSingleResult(CloudModel::class.java) // FIXME update other vaults using this cloud as well
|
||||
val cloudNode = cloudFolderModel.toCloudNode()
|
||||
if (cloudNode is CryptoFolder) {
|
||||
updatedDecryptedCloudFor(Vault.aCopyOf(cloudFolderModel.vault()!!.toVault()).withCloud(cloudModel.toCloud()).build(), cloudFolderModel)
|
||||
} else {
|
||||
updatePlaintextCloud(cloudFolderModel, cloudModel)
|
||||
}
|
||||
} else {
|
||||
Timber.tag("BrowseFilesPresenter").e("Authentication failed")
|
||||
}
|
||||
}
|
||||
|
||||
private fun updatePlaintextCloud(cloudFolderModel: CloudFolderModel, updatedCloud: CloudModel) {
|
||||
cloudFolderModel.toCloudNode().withCloud(updatedCloud.toCloud())?.let {
|
||||
val folder = cloudFolderModelMapper.toModel(it)
|
||||
view?.updateActiveFolderDueToAuthenticationProblem(folder)
|
||||
getCloudList(folder)
|
||||
resumedAfterAuthentication = false
|
||||
} ?: throw FatalBackendException("cloudFolderModel with updated Cloud shouldn't be null")
|
||||
}
|
||||
|
||||
private fun updatedDecryptedCloudFor(vault: Vault, cloudFolderModel: CloudFolderModel) {
|
||||
getDecryptedCloudForVaultUseCase //
|
||||
.withVault(vault) //
|
||||
.run(object : DefaultResultHandler<Cloud>() {
|
||||
override fun onSuccess(cloud: Cloud) {
|
||||
val folder = cloudFolderModelMapper.toModel(cloudFolderModel.toCloudNode().withCloud(cloud)!!)
|
||||
view?.updateActiveFolderDueToAuthenticationProblem(folder)
|
||||
getCloudList(folder)
|
||||
}
|
||||
override fun onFinished() {
|
||||
resumedAfterAuthentication = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun onCreateFolderPressed(cloudFolder: CloudFolderModel, folderName: String?) {
|
||||
createFolderUseCase //
|
||||
.withParent(cloudFolder.toCloudNode()) //
|
||||
@ -245,16 +284,16 @@ class BrowseFilesPresenter @Inject constructor( //
|
||||
private fun copyFile(downloadFiles: List<DownloadFile>) {
|
||||
downloadFiles.forEach { downloadFile ->
|
||||
try {
|
||||
val source = FileInputStream(fileUtil.fileFor(cloudFileModelMapper.toModel(downloadFile.downloadFile)))
|
||||
|
||||
copyDataUseCase //
|
||||
.withSource(source) //
|
||||
.andTarget(downloadFile.dataSink) //
|
||||
.run(object : DefaultResultHandler<Void>() {
|
||||
override fun onFinished() {
|
||||
view?.showMessage(R.string.screen_file_browser_msg_file_exported)
|
||||
}
|
||||
})
|
||||
FileInputStream(fileUtil.fileFor(cloudFileModelMapper.toModel(downloadFile.downloadFile))).use {
|
||||
copyDataUseCase //
|
||||
.withSource(it) //
|
||||
.andTarget(downloadFile.dataSink) //
|
||||
.run(object : DefaultResultHandler<Void>() {
|
||||
override fun onFinished() {
|
||||
view?.showMessage(R.string.screen_file_browser_msg_file_exported)
|
||||
}
|
||||
})
|
||||
}
|
||||
} catch (e: FileNotFoundException) {
|
||||
showError(e)
|
||||
}
|
||||
@ -1115,7 +1154,9 @@ class BrowseFilesPresenter @Inject constructor( //
|
||||
}
|
||||
|
||||
fun onFolderReloadContent(folder: CloudFolderModel) {
|
||||
getCloudList(folder)
|
||||
if(!resumedAfterAuthentication) {
|
||||
getCloudList(folder)
|
||||
}
|
||||
}
|
||||
|
||||
fun onExportFolderClicked(cloudFolder: CloudFolderModel, exportTriggeredByUser: ExportOperation) {
|
||||
@ -1249,6 +1290,7 @@ class BrowseFilesPresenter @Inject constructor( //
|
||||
companion object {
|
||||
|
||||
const val OPEN_FILE_FINISHED = 12
|
||||
|
||||
val EXPORT_AFTER_APP_CHOOSER: ExportOperation = object : ExportOperation {
|
||||
override fun export(presenter: BrowseFilesPresenter, downloadFiles: List<DownloadFile>) {
|
||||
presenter.copyFile(downloadFiles)
|
||||
@ -1272,7 +1314,8 @@ class BrowseFilesPresenter @Inject constructor( //
|
||||
renameFolderUseCase, //
|
||||
copyDataUseCase, //
|
||||
moveFilesUseCase, //
|
||||
moveFoldersUseCase
|
||||
moveFoldersUseCase, //
|
||||
getDecryptedCloudForVaultUseCase
|
||||
)
|
||||
this.authenticationExceptionHandler = authenticationExceptionHandler
|
||||
}
|
||||
|
@ -4,8 +4,16 @@ import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.widget.Toast
|
||||
import com.microsoft.identity.client.AuthenticationCallback
|
||||
import com.microsoft.identity.client.IAccount
|
||||
import com.microsoft.identity.client.IAuthenticationResult
|
||||
import com.microsoft.identity.client.IMultipleAccountPublicClientApplication
|
||||
import com.microsoft.identity.client.IPublicClientApplication
|
||||
import com.microsoft.identity.client.PublicClientApplication
|
||||
import com.microsoft.identity.client.exception.MsalException
|
||||
import org.cryptomator.domain.Cloud
|
||||
import org.cryptomator.domain.LocalStorageCloud
|
||||
import org.cryptomator.domain.OnedriveCloud
|
||||
import org.cryptomator.domain.PCloud
|
||||
import org.cryptomator.domain.Vault
|
||||
import org.cryptomator.domain.di.PerView
|
||||
@ -13,7 +21,7 @@ import org.cryptomator.domain.usecases.cloud.AddOrChangeCloudConnectionUseCase
|
||||
import org.cryptomator.domain.usecases.cloud.GetCloudsUseCase
|
||||
import org.cryptomator.domain.usecases.cloud.GetUsernameUseCase
|
||||
import org.cryptomator.domain.usecases.cloud.RemoveCloudUseCase
|
||||
import org.cryptomator.domain.usecases.vault.DeleteVaultUseCase
|
||||
import org.cryptomator.domain.usecases.vault.DeleteVaultsUseCase
|
||||
import org.cryptomator.domain.usecases.vault.GetVaultListUseCase
|
||||
import org.cryptomator.generator.Callback
|
||||
import org.cryptomator.presentation.R
|
||||
@ -40,7 +48,7 @@ class CloudConnectionListPresenter @Inject constructor( //
|
||||
private val removeCloudUseCase: RemoveCloudUseCase, //
|
||||
private val addOrChangeCloudConnectionUseCase: AddOrChangeCloudConnectionUseCase, //
|
||||
private val getVaultListUseCase: GetVaultListUseCase, //
|
||||
private val deleteVaultUseCase: DeleteVaultUseCase, //
|
||||
private val deleteVaultsUseCase: DeleteVaultsUseCase, //
|
||||
private val cloudModelMapper: CloudModelMapper, //
|
||||
exceptionMappings: ExceptionHandlers
|
||||
) : Presenter<CloudConnectionListView>(exceptionMappings) {
|
||||
@ -85,18 +93,25 @@ class CloudConnectionListPresenter @Inject constructor( //
|
||||
}
|
||||
|
||||
private fun vaultsFor(cloudModel: CloudModel, allVaults: List<Vault>): ArrayList<Vault> {
|
||||
return allVaults.filterTo(ArrayList()) { it.cloud.type() == cloudModel.toCloud().type() }
|
||||
return allVaults.filterTo(ArrayList()) { it.cloud.id() == cloudModel.toCloud().id() }
|
||||
}
|
||||
|
||||
fun onDeleteCloudConnectionAndVaults(cloudModel: CloudModel, vaultsOfCloud: ArrayList<Vault>) {
|
||||
vaultsOfCloud.forEach { vault ->
|
||||
deleteVault(vault)
|
||||
}
|
||||
deleteCloud(cloudModel)
|
||||
}
|
||||
if (vaultsOfCloud.isEmpty()) {
|
||||
deleteCloud(cloudModel)
|
||||
} else {
|
||||
deleteVaultsUseCase
|
||||
.withVaults(vaultsOfCloud)
|
||||
.run(object : DefaultResultHandler<List<Long>>() {
|
||||
override fun onFinished() {
|
||||
deleteCloud(cloudModel)
|
||||
}
|
||||
|
||||
private fun deleteVault(vault: Vault) {
|
||||
deleteVaultUseCase.withVault(vault).run(DefaultResultHandler())
|
||||
override fun onError(e: Throwable) {
|
||||
Timber.tag("CloudConnectionListPresenter").e(e, "Failed to remove all vaults")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun deleteCloud(cloudModel: CloudModel) {
|
||||
@ -124,38 +139,90 @@ class CloudConnectionListPresenter @Inject constructor( //
|
||||
|
||||
fun onAddConnectionClicked() {
|
||||
when (selectedCloudType.get()) {
|
||||
CloudTypeModel.WEBDAV -> requestActivityResult(
|
||||
ActivityResultCallbacks.addChangeMultiCloud(), //
|
||||
Intents.webDavAddOrChangeIntent()
|
||||
)
|
||||
CloudTypeModel.PCLOUD -> {
|
||||
requestActivityResult(
|
||||
ActivityResultCallbacks.pCloudAuthenticationFinished(), //
|
||||
Intents.authenticatePCloudIntent()
|
||||
)
|
||||
}
|
||||
CloudTypeModel.S3 -> requestActivityResult(
|
||||
ActivityResultCallbacks.addChangeMultiCloud(), //
|
||||
Intents.s3AddOrChangeIntent()
|
||||
)
|
||||
CloudTypeModel.ONEDRIVE -> addOnedriveCloud()
|
||||
CloudTypeModel.WEBDAV -> requestActivityResult(ActivityResultCallbacks.addChangeMultiCloud(), Intents.webDavAddOrChangeIntent())
|
||||
CloudTypeModel.PCLOUD -> requestActivityResult(ActivityResultCallbacks.pCloudAuthenticationFinished(), Intents.authenticatePCloudIntent())
|
||||
CloudTypeModel.S3 -> requestActivityResult(ActivityResultCallbacks.addChangeMultiCloud(), Intents.s3AddOrChangeIntent())
|
||||
CloudTypeModel.LOCAL -> openDocumentTree()
|
||||
}
|
||||
}
|
||||
|
||||
private fun addOnedriveCloud() {
|
||||
PublicClientApplication.createMultipleAccountPublicClientApplication(
|
||||
context(),
|
||||
R.raw.auth_config_onedrive,
|
||||
object : IPublicClientApplication.IMultipleAccountApplicationCreatedListener {
|
||||
override fun onCreated(application: IMultipleAccountPublicClientApplication) {
|
||||
application.getAccounts(object : IPublicClientApplication.LoadAccountsCallback {
|
||||
override fun onTaskCompleted(accounts: List<IAccount>) {
|
||||
application.acquireToken(activity(), AuthenticateCloudPresenter.onedriveScopes(), getAuthInteractiveCallback())
|
||||
}
|
||||
|
||||
override fun onError(e: MsalException) {
|
||||
Timber.tag("AuthenticateCloudPresenter").e(e, "Error to get accounts")
|
||||
showError(e);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onError(e: MsalException) {
|
||||
Timber.tag("AuthenticateCloudPresenter").i(e, "Error in configuration")
|
||||
showError(e);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun getAuthInteractiveCallback(): AuthenticationCallback {
|
||||
return object : AuthenticationCallback {
|
||||
|
||||
override fun onSuccess(authenticationResult: IAuthenticationResult) {
|
||||
Timber.tag("AuthenticateCloudPresenter").i("Successfully authenticated")
|
||||
val accessToken = CredentialCryptor.getInstance(context()).encrypt(authenticationResult.accessToken)
|
||||
val onedriveSkeleton = OnedriveCloud.aOnedriveCloud().withAccessToken(accessToken).withUsername(authenticationResult.account.username).build()
|
||||
saveOnedriveCloud(onedriveSkeleton)
|
||||
}
|
||||
|
||||
override fun onError(e: MsalException) {
|
||||
Timber.tag("AuthenticateCloudPresenter").e(e, "Successfully authenticated")
|
||||
showError(e);
|
||||
}
|
||||
|
||||
override fun onCancel() {
|
||||
Timber.tag("AuthenticateCloudPresenter").i("User cancelled login")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveOnedriveCloud(onedriveSkeleton: OnedriveCloud) {
|
||||
getUsernameUseCase //
|
||||
.withCloud(onedriveSkeleton) //
|
||||
.run(object : DefaultResultHandler<String>() {
|
||||
override fun onSuccess(username: String) {
|
||||
prepareForSavingOnedriveCloud(OnedriveCloud.aCopyOf(onedriveSkeleton).withUsername(username).build())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun prepareForSavingOnedriveCloud(cloud: OnedriveCloud) {
|
||||
getCloudsUseCase //
|
||||
.withCloudType(CloudTypeModel.valueOf(selectedCloudType.get())) //
|
||||
.run(object : DefaultResultHandler<List<Cloud>>() {
|
||||
override fun onSuccess(clouds: List<Cloud>) {
|
||||
clouds.firstOrNull {
|
||||
(it as OnedriveCloud).username() == cloud.username()
|
||||
}?.let {
|
||||
saveCloud(OnedriveCloud.aCopyOf(it as OnedriveCloud).withAccessToken(cloud.accessToken()).build())
|
||||
Timber.tag("CloudConnListPresenter").i("OneDrive access token updated")
|
||||
} ?: saveCloud(cloud)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun openDocumentTree() {
|
||||
try {
|
||||
requestActivityResult( //
|
||||
ActivityResultCallbacks.pickedLocalStorageLocation(), //
|
||||
Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
|
||||
)
|
||||
requestActivityResult(ActivityResultCallbacks.pickedLocalStorageLocation(), Intent(Intent.ACTION_OPEN_DOCUMENT_TREE))
|
||||
} catch (exception: ActivityNotFoundException) {
|
||||
Toast //
|
||||
.makeText( //
|
||||
activity().applicationContext, //
|
||||
context().getText(R.string.screen_cloud_local_error_no_content_provider), //
|
||||
Toast.LENGTH_SHORT
|
||||
) //
|
||||
.show()
|
||||
Toast.makeText(activity().applicationContext, context().getText(R.string.screen_cloud_local_error_no_content_provider), Toast.LENGTH_SHORT).show()
|
||||
Timber.tag("CloudConnListPresenter").e(exception, "No ContentProvider on system")
|
||||
}
|
||||
}
|
||||
@ -198,14 +265,8 @@ class CloudConnectionListPresenter @Inject constructor( //
|
||||
|
||||
if (!code.isNullOrEmpty() && !hostname.isNullOrEmpty()) {
|
||||
Timber.tag("CloudConnectionListPresenter").i("PCloud OAuth code successfully retrieved")
|
||||
|
||||
val accessToken = CredentialCryptor //
|
||||
.getInstance(this.context()) //
|
||||
.encrypt(code)
|
||||
val pCloudSkeleton = PCloud.aPCloud() //
|
||||
.withAccessToken(accessToken)
|
||||
.withUrl(hostname)
|
||||
.build();
|
||||
val accessToken = CredentialCryptor.getInstance(this.context()).encrypt(code)
|
||||
val pCloudSkeleton = PCloud.aPCloud().withAccessToken(accessToken).withUrl(hostname).build();
|
||||
getUsernameUseCase //
|
||||
.withCloud(pCloudSkeleton) //
|
||||
.run(object : DefaultResultHandler<String>() {
|
||||
@ -226,19 +287,14 @@ class CloudConnectionListPresenter @Inject constructor( //
|
||||
clouds.firstOrNull {
|
||||
(it as PCloud).username() == cloud.username()
|
||||
}?.let {
|
||||
saveCloud(
|
||||
PCloud.aCopyOf(it as PCloud) //
|
||||
.withUrl(cloud.url())
|
||||
.withAccessToken(cloud.accessToken())
|
||||
.build()
|
||||
)
|
||||
saveCloud(PCloud.aCopyOf(it as PCloud).withUrl(cloud.url()).withAccessToken(cloud.accessToken()).build())
|
||||
view?.showDialog(PCloudCredentialsUpdatedDialog.newInstance(it.username()))
|
||||
} ?: saveCloud(cloud)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun saveCloud(cloud: PCloud) {
|
||||
fun saveCloud(cloud: Cloud) {
|
||||
addOrChangeCloudConnectionUseCase //
|
||||
.withCloud(cloud) //
|
||||
.run(object : DefaultResultHandler<Void?>() {
|
||||
@ -252,15 +308,13 @@ class CloudConnectionListPresenter @Inject constructor( //
|
||||
fun pickedLocalStorageLocation(result: ActivityResult) {
|
||||
val rootTreeUriOfLocalStorage = result.intent().data
|
||||
persistUriPermission(rootTreeUriOfLocalStorage)
|
||||
addOrChangeCloudConnectionUseCase.withCloud(
|
||||
LocalStorageCloud.aLocalStorage() //
|
||||
.withRootUri(rootTreeUriOfLocalStorage.toString()) //
|
||||
.build()
|
||||
).run(object : DefaultResultHandler<Void?>() {
|
||||
override fun onSuccess(void: Void?) {
|
||||
loadCloudList()
|
||||
}
|
||||
})
|
||||
addOrChangeCloudConnectionUseCase
|
||||
.withCloud(LocalStorageCloud.aLocalStorage().withRootUri(rootTreeUriOfLocalStorage.toString()).build())
|
||||
.run(object : DefaultResultHandler<Void?>() {
|
||||
override fun onSuccess(void: Void?) {
|
||||
loadCloudList()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun persistUriPermission(rootTreeUriOfLocalStorage: Uri?) {
|
||||
@ -298,6 +352,6 @@ class CloudConnectionListPresenter @Inject constructor( //
|
||||
}
|
||||
|
||||
init {
|
||||
unsubscribeOnDestroy(getCloudsUseCase, removeCloudUseCase, addOrChangeCloudConnectionUseCase, getVaultListUseCase, deleteVaultUseCase)
|
||||
unsubscribeOnDestroy(getCloudsUseCase, removeCloudUseCase, addOrChangeCloudConnectionUseCase, getVaultListUseCase, deleteVaultsUseCase)
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package org.cryptomator.presentation.presenter
|
||||
|
||||
import org.cryptomator.domain.Cloud
|
||||
import org.cryptomator.domain.LocalStorageCloud
|
||||
import org.cryptomator.domain.OnedriveCloud
|
||||
import org.cryptomator.domain.PCloud
|
||||
import org.cryptomator.domain.S3Cloud
|
||||
import org.cryptomator.domain.WebDavCloud
|
||||
@ -18,6 +19,7 @@ import org.cryptomator.presentation.intent.Intents
|
||||
import org.cryptomator.presentation.model.CloudModel
|
||||
import org.cryptomator.presentation.model.CloudTypeModel
|
||||
import org.cryptomator.presentation.model.LocalStorageModel
|
||||
import org.cryptomator.presentation.model.OnedriveCloudModel
|
||||
import org.cryptomator.presentation.model.PCloudModel
|
||||
import org.cryptomator.presentation.model.S3CloudModel
|
||||
import org.cryptomator.presentation.model.WebDavCloudModel
|
||||
@ -39,6 +41,7 @@ class CloudSettingsPresenter @Inject constructor( //
|
||||
private val nonSingleLoginClouds: Set<CloudTypeModel> = EnumSet.of( //
|
||||
CloudTypeModel.CRYPTO, //
|
||||
CloudTypeModel.LOCAL, //
|
||||
CloudTypeModel.ONEDRIVE, //
|
||||
CloudTypeModel.PCLOUD, //
|
||||
CloudTypeModel.S3, //
|
||||
CloudTypeModel.WEBDAV
|
||||
@ -95,6 +98,7 @@ class CloudSettingsPresenter @Inject constructor( //
|
||||
|
||||
private fun effectiveTitle(cloudTypeModel: CloudTypeModel): String {
|
||||
when (cloudTypeModel) {
|
||||
CloudTypeModel.ONEDRIVE -> return context().getString(R.string.screen_cloud_settings_onedrive_connections)
|
||||
CloudTypeModel.PCLOUD -> return context().getString(R.string.screen_cloud_settings_pcloud_connections)
|
||||
CloudTypeModel.WEBDAV -> return context().getString(R.string.screen_cloud_settings_webdav_connections)
|
||||
CloudTypeModel.S3 -> return context().getString(R.string.screen_cloud_settings_s3_connections)
|
||||
@ -130,6 +134,7 @@ class CloudSettingsPresenter @Inject constructor( //
|
||||
.filter { cloud -> !(BuildConfig.FLAVOR == "fdroid" && cloud.cloudType() == CloudTypeModel.GOOGLE_DRIVE) } //
|
||||
.toMutableList() //
|
||||
.also {
|
||||
it.add(aOnedriveCloud())
|
||||
it.add(aPCloud())
|
||||
it.add(aWebdavCloud())
|
||||
it.add(aS3Cloud())
|
||||
@ -138,6 +143,10 @@ class CloudSettingsPresenter @Inject constructor( //
|
||||
view?.render(cloudModel)
|
||||
}
|
||||
|
||||
private fun aOnedriveCloud(): OnedriveCloudModel {
|
||||
return OnedriveCloudModel(OnedriveCloud.aOnedriveCloud().build())
|
||||
}
|
||||
|
||||
private fun aPCloud(): PCloudModel {
|
||||
return PCloudModel(PCloud.aPCloud().build())
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ import org.cryptomator.domain.usecases.vault.LockVaultUseCase
|
||||
import org.cryptomator.domain.usecases.vault.MoveVaultPositionUseCase
|
||||
import org.cryptomator.domain.usecases.vault.RenameVaultUseCase
|
||||
import org.cryptomator.domain.usecases.vault.SaveVaultUseCase
|
||||
import org.cryptomator.domain.usecases.vault.UpdateVaultParameterIfChangedRemotelyUseCase
|
||||
import org.cryptomator.generator.Callback
|
||||
import org.cryptomator.presentation.BuildConfig
|
||||
import org.cryptomator.presentation.CryptomatorApp
|
||||
@ -78,6 +79,7 @@ class VaultListPresenter @Inject constructor( //
|
||||
private val licenseCheckUseCase: DoLicenseCheckUseCase, //
|
||||
private val updateCheckUseCase: DoUpdateCheckUseCase, //
|
||||
private val updateUseCase: DoUpdateUseCase, //
|
||||
private val updateVaultParameterIfChangedRemotelyUseCase: UpdateVaultParameterIfChangedRemotelyUseCase, //
|
||||
private val networkConnectionCheck: NetworkConnectionCheck, //
|
||||
private val fileUtil: FileUtil, //
|
||||
private val authenticationExceptionHandler: AuthenticationExceptionHandler, //
|
||||
@ -115,7 +117,7 @@ class VaultListPresenter @Inject constructor( //
|
||||
|
||||
checkLicense()
|
||||
|
||||
if(sharedPreferencesHandler.usePhotoUpload()) {
|
||||
if (sharedPreferencesHandler.usePhotoUpload()) {
|
||||
checkLocalStoragePermissionRegardingAutoUpload()
|
||||
}
|
||||
}
|
||||
@ -399,16 +401,25 @@ class VaultListPresenter @Inject constructor( //
|
||||
@Callback
|
||||
fun vaultUnlockedVaultList(result: ActivityResult) {
|
||||
val cloud = result.intent().getSerializableExtra(SINGLE_RESULT) as Cloud
|
||||
navigateToVaultContent(cloud)
|
||||
getRootFolderOf(cloud)
|
||||
}
|
||||
|
||||
private fun navigateToVaultContent(cloud: Cloud) {
|
||||
private fun getRootFolderOf(cloud: Cloud) {
|
||||
getRootFolderUseCase //
|
||||
.withCloud(cloud) //
|
||||
.run(object : DefaultResultHandler<CloudFolder>() {
|
||||
override fun onSuccess(folder: CloudFolder) {
|
||||
val cryptoCloud = (folder.cloud as CryptoCloud)
|
||||
val vault = cryptoCloud.vault
|
||||
navigateToVaultContent(folder)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun navigateToVaultContent(folder: CloudFolder) {
|
||||
val cryptoCloud = (folder.cloud as CryptoCloud)
|
||||
updateVaultParameterIfChangedRemotelyUseCase //
|
||||
.withVault(cryptoCloud.vault) //
|
||||
.run(object : DefaultResultHandler<Vault>() {
|
||||
override fun onSuccess(vault: Vault) {
|
||||
view?.addOrUpdateVault(VaultModel(vault))
|
||||
navigateToVaultContent(vault, folder)
|
||||
view?.showProgress(ProgressModel.COMPLETED)
|
||||
@ -530,7 +541,8 @@ class VaultListPresenter @Inject constructor( //
|
||||
moveVaultPositionUseCase, //
|
||||
licenseCheckUseCase, //
|
||||
updateCheckUseCase, //
|
||||
updateUseCase
|
||||
updateUseCase, //
|
||||
updateVaultParameterIfChangedRemotelyUseCase
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +50,6 @@ import org.cryptomator.presentation.ui.dialog.ReplaceDialog
|
||||
import org.cryptomator.presentation.ui.dialog.SymLinkDialog
|
||||
import org.cryptomator.presentation.ui.dialog.UploadCloudFileDialog
|
||||
import org.cryptomator.presentation.ui.fragment.BrowseFilesFragment
|
||||
import java.util.ArrayList
|
||||
import java.util.regex.Pattern
|
||||
import javax.inject.Inject
|
||||
import kotlinx.android.synthetic.main.toolbar_layout.toolbar
|
||||
@ -125,11 +124,7 @@ class BrowseFilesActivity : BaseActivity(), //
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
finishActivityDueToScreenLockEventReceiver?.let {
|
||||
LocalBroadcastManager.getInstance(this).registerReceiver(it, IntentFilter(CryptorsService.SCREEN_AND_VAULT_LOCKED))
|
||||
}
|
||||
}.also { LocalBroadcastManager.getInstance(this).registerReceiver(it, IntentFilter(CryptorsService.SCREEN_AND_VAULT_LOCKED)) }
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
@ -615,6 +610,10 @@ class BrowseFilesActivity : BaseActivity(), //
|
||||
showDialog(NoDirFileDialog.newInstance(cryptoFolderName, cloudFolderPath))
|
||||
}
|
||||
|
||||
override fun updateActiveFolderDueToAuthenticationProblem(folder: CloudFolderModel) {
|
||||
browseFilesFragment().folder = folder
|
||||
}
|
||||
|
||||
override fun navigateFolderBackBecauseSymlink() {
|
||||
onBackPressed()
|
||||
}
|
||||
|
@ -35,5 +35,6 @@ interface BrowseFilesView : View {
|
||||
fun disableSelectionMode()
|
||||
fun showSymLinkDialog()
|
||||
fun showNoDirFileDialog(cryptoFolderName: String, cloudFolderPath: String)
|
||||
fun updateActiveFolderDueToAuthenticationProblem(folder: CloudFolderModel)
|
||||
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ constructor(
|
||||
if (sharedPreferencesHandler.useGlobSearch()) {
|
||||
nodes?.filter { cloudNode -> PatternMatcher(filterText, PatternMatcher.PATTERN_SIMPLE_GLOB).match(cloudNode.name) }
|
||||
} else {
|
||||
nodes?.filter { cloudNode -> cloudNode.name.lowercase().startsWith(filterText.lowercase()) }
|
||||
nodes?.filter { cloudNode -> cloudNode.name.contains(filterText, true) }
|
||||
}
|
||||
} else {
|
||||
nodes
|
||||
|
@ -7,6 +7,7 @@ import org.cryptomator.domain.exception.FatalBackendException
|
||||
import org.cryptomator.presentation.R
|
||||
import org.cryptomator.presentation.model.CloudModel
|
||||
import org.cryptomator.presentation.model.LocalStorageModel
|
||||
import org.cryptomator.presentation.model.OnedriveCloudModel
|
||||
import org.cryptomator.presentation.model.PCloudModel
|
||||
import org.cryptomator.presentation.model.S3CloudModel
|
||||
import org.cryptomator.presentation.model.WebDavCloudModel
|
||||
@ -55,6 +56,9 @@ internal constructor(context: Context) : RecyclerViewBaseAdapter<CloudModel, Clo
|
||||
itemView.setOnClickListener { callback.onCloudConnectionClicked(cloudModel) }
|
||||
|
||||
when (cloudModel) {
|
||||
is OnedriveCloudModel -> {
|
||||
bindOnedriveCloudModel(cloudModel)
|
||||
}
|
||||
is WebDavCloudModel -> {
|
||||
bindWebDavCloudModel(cloudModel)
|
||||
}
|
||||
@ -70,6 +74,12 @@ internal constructor(context: Context) : RecyclerViewBaseAdapter<CloudModel, Clo
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun bindOnedriveCloudModel(cloudModel: OnedriveCloudModel) {
|
||||
itemView.cloudText.text = cloudModel.username()
|
||||
itemView.cloudSubText.visibility = View.GONE
|
||||
}
|
||||
|
||||
private fun bindWebDavCloudModel(cloudModel: WebDavCloudModel) {
|
||||
try {
|
||||
val uri = Uri.parse(cloudModel.url())
|
||||
|
@ -42,6 +42,7 @@ constructor(private val context: Context) : RecyclerViewBaseAdapter<CloudModel,
|
||||
itemView.cloudImage.setImageResource(cloudModel.cloudType().cloudImageResource)
|
||||
|
||||
when (cloudModel.cloudType()) {
|
||||
CloudTypeModel.ONEDRIVE -> itemView.cloudName.text = context.getString(R.string.screen_cloud_settings_onedrive_connections)
|
||||
CloudTypeModel.PCLOUD -> itemView.cloudName.text = context.getString(R.string.screen_cloud_settings_pcloud_connections)
|
||||
CloudTypeModel.S3 -> itemView.cloudName.text = context.getString(R.string.screen_cloud_settings_s3_connections)
|
||||
CloudTypeModel.WEBDAV -> itemView.cloudName.text = context.getString(R.string.screen_cloud_settings_webdav_connections)
|
||||
|
@ -7,6 +7,7 @@ import org.cryptomator.presentation.R
|
||||
import org.cryptomator.presentation.model.CloudModel
|
||||
import org.cryptomator.presentation.model.CloudTypeModel
|
||||
import org.cryptomator.presentation.model.LocalStorageModel
|
||||
import org.cryptomator.presentation.model.OnedriveCloudModel
|
||||
import org.cryptomator.presentation.model.PCloudModel
|
||||
import org.cryptomator.presentation.model.S3CloudModel
|
||||
import org.cryptomator.presentation.model.WebDavCloudModel
|
||||
@ -29,6 +30,7 @@ class CloudConnectionSettingsBottomSheet : BaseBottomSheet<CloudConnectionSettin
|
||||
val cloudModel = requireArguments().getSerializable(CLOUD_NODE_ARG) as CloudModel
|
||||
|
||||
when (cloudModel.cloudType()) {
|
||||
CloudTypeModel.ONEDRIVE -> bindViewForOnedrive(cloudModel as OnedriveCloudModel)
|
||||
CloudTypeModel.WEBDAV -> bindViewForWebDAV(cloudModel as WebDavCloudModel)
|
||||
CloudTypeModel.PCLOUD -> bindViewForPCloud(cloudModel as PCloudModel)
|
||||
CloudTypeModel.S3 -> bindViewForS3(cloudModel as S3CloudModel)
|
||||
@ -57,6 +59,11 @@ class CloudConnectionSettingsBottomSheet : BaseBottomSheet<CloudConnectionSettin
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindViewForOnedrive(cloudModel: OnedriveCloudModel) {
|
||||
change_cloud.visibility = View.GONE
|
||||
tv_cloud_subtext.text = cloudModel.username()
|
||||
}
|
||||
|
||||
private fun bindViewForWebDAV(cloudModel: WebDavCloudModel) {
|
||||
change_cloud.visibility = View.VISIBLE
|
||||
tv_cloud_name.text = cloudModel.url()
|
||||
|
@ -20,7 +20,7 @@ class PCloudCredentialsUpdatedDialog : BaseDialog<PCloudCredentialsUpdatedDialog
|
||||
fun onNotifyForPCloudCredentialsUpdateFinished()
|
||||
}
|
||||
|
||||
val someActivityResultLauncher = registerForActivityResult(StartActivityForResult()) {
|
||||
private val someActivityResultLauncher = registerForActivityResult(StartActivityForResult()) {
|
||||
dismiss()
|
||||
callback?.onNotifyForPCloudCredentialsUpdateFinished()
|
||||
}
|
||||
|
@ -26,7 +26,6 @@ import org.cryptomator.presentation.model.ProgressModel
|
||||
import org.cryptomator.presentation.presenter.BrowseFilesPresenter
|
||||
import org.cryptomator.presentation.ui.adapter.BrowseFilesAdapter
|
||||
import org.cryptomator.presentation.util.ResourceHelper.Companion.getPixelOffset
|
||||
import java.util.Comparator
|
||||
import java.util.Optional
|
||||
import javax.inject.Inject
|
||||
import kotlinx.android.synthetic.main.floating_action_button_layout.floatingActionButton
|
||||
@ -51,8 +50,11 @@ class BrowseFilesFragment : BaseFragment() {
|
||||
|
||||
private var filterText: String = ""
|
||||
|
||||
val folder: CloudFolderModel
|
||||
var folder: CloudFolderModel
|
||||
get() = requireArguments().getSerializable(ARG_FOLDER) as CloudFolderModel
|
||||
set(updatedFolder) {
|
||||
arguments?.putSerializable(ARG_FOLDER, updatedFolder)
|
||||
}
|
||||
|
||||
private val chooseCloudNodeSettings: ChooseCloudNodeSettings?
|
||||
get() = requireArguments().getSerializable(ARG_CHOOSE_CLOUD_NODE_SETTINGS) as ChooseCloudNodeSettings?
|
||||
|
@ -1,9 +1,7 @@
|
||||
package org.cryptomator.presentation.util
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.security.keystore.KeyPermanentlyInvalidatedException
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.biometric.BiometricPrompt
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
@ -12,8 +10,9 @@ import org.cryptomator.presentation.R
|
||||
import org.cryptomator.presentation.model.VaultModel
|
||||
import org.cryptomator.util.crypto.BiometricAuthCryptor
|
||||
import org.cryptomator.util.crypto.UnrecoverableStorageKeyException
|
||||
import java.util.concurrent.Executor
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.Executor
|
||||
import javax.crypto.BadPaddingException
|
||||
|
||||
class BiometricAuthentication(val callback: Callback, val context: Context, val cryptoMode: CryptoMode, private val useConfirmationInFaceUnlockAuth: Boolean) {
|
||||
|
||||
@ -72,20 +71,28 @@ class BiometricAuthentication(val callback: Callback, val context: Context, val
|
||||
|
||||
val cipher = result.cryptoObject?.cipher
|
||||
|
||||
val transformedPassword = if (cryptoMode == CryptoMode.ENCRYPT) {
|
||||
biometricAuthCryptor.encrypt(cipher, vaultModel.password)
|
||||
} else {
|
||||
biometricAuthCryptor.decrypt(cipher, vaultModel.password)
|
||||
try {
|
||||
val transformedPassword = if (cryptoMode == CryptoMode.ENCRYPT) {
|
||||
biometricAuthCryptor.encrypt(cipher, vaultModel.password)
|
||||
} else {
|
||||
biometricAuthCryptor.decrypt(cipher, vaultModel.password)
|
||||
}
|
||||
|
||||
val vaultModelPasswordAware = VaultModel(
|
||||
Vault //
|
||||
.aCopyOf(vaultModel.toVault()) //
|
||||
.withSavedPassword(transformedPassword) //
|
||||
.build()
|
||||
)
|
||||
|
||||
callback.onBiometricAuthenticated(vaultModelPasswordAware)
|
||||
} catch (e: BadPaddingException) {
|
||||
Timber.tag("BiometricAuthentication").i(
|
||||
e,
|
||||
"Recover from BadPaddingException which can be thrown on some devices if the key in the keystore is invalidated e.g. due to a fingerprint added because of an upstream error in Android, see #400 for more info"
|
||||
)
|
||||
callback.onBiometricKeyInvalidated(vaultModel)
|
||||
}
|
||||
|
||||
val vaultModelPasswordAware = VaultModel(
|
||||
Vault //
|
||||
.aCopyOf(vaultModel.toVault()) //
|
||||
.withSavedPassword(transformedPassword) //
|
||||
.build()
|
||||
)
|
||||
|
||||
callback.onBiometricAuthenticated(vaultModelPasswordAware)
|
||||
}
|
||||
|
||||
override fun onAuthenticationFailed() {
|
||||
|
1
presentation/src/main/res/raw/.gitignore
vendored
Normal file
1
presentation/src/main/res/raw/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
auth_config_onedrive.json
|
@ -4,28 +4,34 @@
|
||||
<string name="share_with_label">تشفير</string>
|
||||
<!-- # error messages -->
|
||||
<string name="error_generic">حدث خطأ ما</string>
|
||||
<string name="error_authentication_failed">المصادقة فشلت</string>
|
||||
<string name="error_authentication_failed">فشلت المصادقة</string>
|
||||
<string name="error_authentication_failed_re_authenticate">فشلت عملية المصادقة، الرجاء تسجيل الدخول باستخدام %1$s</string>
|
||||
<string name="error_no_network_connection">لا يوجد اتصال بالشبكة</string>
|
||||
<string name="error_invalid_passphrase">كلمة المرور غير صحيحة</string>
|
||||
<string name="error_invalid_passphrase">كلمة المرور خاطئة</string>
|
||||
<string name="error_file_or_folder_exists">المجلد أو الملف موجود مسبقاُ.</string>
|
||||
<string name="error_vault_version_not_supported">نوع المخزن غير مدعوم. تم إنشاء هذا المخزن باستخدام نسخة أخري من Cryptomator.</string>
|
||||
<string name="error_vault_version_not_supported">نوع المخزن غير مدعوم. تم إنشاء هذا المخزن باستخدام إصدار آخر من Cryptomator.</string>
|
||||
<string name="error_vault_already_exists">المخزن موجود مسبقاً.</string>
|
||||
<string name="error_no_such_file">ملف غير موجود.</string>
|
||||
<string name="error_vault_has_been_locked">تم قفل المخزن.</string>
|
||||
<string name="error_cloud_already_exists">موفر الخدمة السحابية موجود مسبقاً.</string>
|
||||
<string name="error_activity_not_found">الرجاء تحميل تطبيق يمكنه فتح هذا الملف.</string>
|
||||
<string name="error_server_not_found">لم يتم العثور على الخادم.</string>
|
||||
<string name="error_export_illegal_file_name">فشل التصدير. حاول إزالة الأحرف الخاصة من أسماء الملفات وتصديرها مرة أخرى.</string>
|
||||
<string name="error_name_contains_invalid_characters">لا يمكن أن يحتوي علي رموز خاصة.</string>
|
||||
<string name="error_names_contains_invalid_characters">اسم الملف لا يمكن أن يحتوي علي رموز خاصة.</string>
|
||||
<string name="error_vault_name_contains_invalid_characters">اسم المخزن لا يمكن أن يحتوي علي رموز خاصة.</string>
|
||||
<string name="error_server_not_found">لم يتم العثور على المخدم.</string>
|
||||
<string name="error_device_policy_manager_not_found">الرجاء فتح إعدادات جهازك وتعيين قفل الشاشة يدوياً</string>
|
||||
<string name="error_export_illegal_file_name">فشل التصدير. حاول إزالة المحارف الخاصة من أسماء الملفات وتصديرها مرة أخرى.</string>
|
||||
<string name="error_name_contains_invalid_characters">لا يمكن أن يحتوي على محارف خاصة.</string>
|
||||
<string name="error_names_contains_invalid_characters">اسم الملف لا يمكن أن يحتوي على محارف خاصة.</string>
|
||||
<string name="error_vault_name_contains_invalid_characters">اسم المخزن لا يمكن أن يحتوي على محارف خاصة.</string>
|
||||
<string name="error_general_update">فشل التحقق من التحديث. حدث خطأ عام.</string>
|
||||
<string name="error_hash_mismatch_update">فشل التحقق من التحديث. التجزئة المحسوبة لا تتطابق مع الملف المرفوع</string>
|
||||
<string name="error_update_no_internet">فشل التحقق من التحديث. لا يوجد اتصال بالإنترنت.</string>
|
||||
<string name="error_failed_to_decrypt_webdav_password">فشل فك تشفير كلمة المرور WebDAV، الرجاء إضافتها في الإعدادات</string>
|
||||
<string name="error_play_services_not_available">خدمات متجر Google غير مثبتة!</string>
|
||||
<string name="error_biometric_auth_aborted">تم إلغاء استخدام البصمة في عملية التوثيق</string>
|
||||
<string name="error_vault_version_mismatch">الإصدار المحدد في %1$s يختلف عن %2$s</string>
|
||||
<string name="error_vault_key_invalid">%1$s لا يتطابق مع هذا %2$s</string>
|
||||
<string name="error_vault_config_loading">خطأ عام أثناء تحميل إعدادات المخزن</string>
|
||||
<string name="error_file_not_found_after_opening_using_3party">بعد إعادة التبديل الي Cryptomator لم يعد الملف المحلي موجود. لايمكن نشر التعديلات الي موفر الخدمة السحابية.</string>
|
||||
<string name="error_no_such_bucket">لا يوجد مخزن بهذه المعلومات في S3</string>
|
||||
<string name="error_masterkey_location_not_supported">موقع المفتاح الرئيسي غير مدعوم بعد</string>
|
||||
<!-- # clouds -->
|
||||
<!-- ## cloud names -->
|
||||
@ -35,6 +41,7 @@
|
||||
<string name="permission_message_export_file">Cryptomator يحتاج صلاحية الوصول الي التخزين لكي يستطيع تصدير الملفات</string>
|
||||
<string name="permission_message_upload_file">Cryptomator يحتاج صلاحية الوصول الي التخزين لكي يستطيع رفع الملفات</string>
|
||||
<string name="permission_message_share_file">Cryptomator يحتاج صلاحية الوصول الي التخزين لكي تستطيع مشاركة الملفات</string>
|
||||
<string name="permission_revoked_re_request_permission">لقد فقد Cryptomator الإذن للوصول إلى هذا الموقع. الرجاء تحديد هذا المجلد مرة أخرى لاستعادة الإذن.</string>
|
||||
<string name="snack_bar_action_title_settings">الإعدادات</string>
|
||||
<string name="snack_bar_action_title_search">بحث</string>
|
||||
<string name="snack_bar_action_title_search_previous">السابق</string>
|
||||
@ -119,11 +126,13 @@
|
||||
<string name="screen_s3_settings_display_name_label">الاسم المعروض</string>
|
||||
<string name="screen_s3_settings_access_key_label">مفتاح الوصول</string>
|
||||
<string name="screen_s3_settings_secret_key_label">المفتاح السري</string>
|
||||
<string name="screen_s3_settings_bucket_label">مخزن موجود مسبقاً</string>
|
||||
<string name="screen_s3_settings_endpoint_label">نقطة الوصول</string>
|
||||
<string name="screen_s3_settings_region_label">المنطقة</string>
|
||||
<string name="screen_s3_settings_msg_display_name_not_empty">إسم العرض لا يمكن أن يكون فارغاً</string>
|
||||
<string name="screen_s3_settings_msg_access_key_not_empty">مفتاح الوصول لا يمكن أن يكون فارغاً</string>
|
||||
<string name="screen_s3_settings_msg_secret_key_not_empty">المفتاح السري لا يمكن أن يكون فارغاً</string>
|
||||
<string name="screen_s3_settings_msg_bucket_not_empty">المخزن لايمكن ان يكون فارغاً</string>
|
||||
<string name="screen_s3_settings_msg_endpoint_and_region_not_empty">نقطة الوصول أو المنطقة لا يمكن أن تكون فارغة</string>
|
||||
<!-- ## screen: enter vault name -->
|
||||
<string name="screen_enter_vault_name_msg_name_empty">إسم المخزن لايمكن ان يكون فارغاً.</string>
|
||||
@ -146,11 +155,15 @@
|
||||
<string name="screen_settings_biometric_auth">إستخدام البصمة</string>
|
||||
<string name="screen_settings_activate_biometric_auth">تنشيط توثيق البصمة</string>
|
||||
<string name="screen_settings_confirm_face_unlock">تأكيد فتح الوجه (إذا كان متوفراً)</string>
|
||||
<string name="screen_settings_block_app_when_obscured">حظر التطبيق عندما يحجب</string>
|
||||
<string name="screen_settings_block_app_when_obscured_summary">حظر اعتراض الإدخال وعرض واجهة مستخدم خاطئة</string>
|
||||
<string name="screen_settings_secure_screen">حظر لقطات الشاشة</string>
|
||||
<string name="screen_settings_secure_screen_summary">حظر لقطات الشاشة في قائمة التطبيقات الحديثة وداخل التطبيق</string>
|
||||
<string name="screen_settings_section_search">بحث</string>
|
||||
<string name="screen_settings_live_search">بحث مباشر</string>
|
||||
<string name="screen_settings_live_search_summary">تحديث نتائج البحث أثناء إدخال الإستعلام</string>
|
||||
<string name="screen_settings_glob_search">البحث باستخدام نمط المطابقة glob</string>
|
||||
<string name="screen_settings_glob_search_summary">استخدام نمط المطابقة glob مثل alice.*.jpg</string>
|
||||
<string name="screen_settings_section_auto_lock">قفل تلقائي</string>
|
||||
<string name="screen_settings_auto_lock_timeout">أقفال بعد</string>
|
||||
<string name="screen_settings_auto_lock_on_screen_lock">عند تعطيل الشاشة</string>
|
||||
@ -174,6 +187,7 @@
|
||||
<string name="screen_settings_debug_mode_label">وضع التصحيح</string>
|
||||
<string name="screen_settings_error_report_label">ارسال ملفات السجل</string>
|
||||
<string name="screen_settings_error_report_failed">فشل الإرسال</string>
|
||||
<string name="screen_settings_security_label">تلميحات الأمان</string>
|
||||
<string name="screen_settings_section_version">الإصدارة</string>
|
||||
<string name="screen_settings_advanced_settings">إعدادات متقدمة</string>
|
||||
<string name="screen_settings_background_unlock_preparation_label">تسريع فتح القفل</string>
|
||||
@ -212,6 +226,7 @@
|
||||
<string name="dialog_replace_positive_button_single_file_exists">إستبدال</string>
|
||||
<string name="dialog_replace_msg_single_file_exists">الملف \'%1$s\' موجود مسبقاً. هل تريد استبداله؟</string>
|
||||
<string name="dialog_replace_msg_all_files_exists">الملفات موجودة مسبقاً. هل تريد استبدالها؟</string>
|
||||
<string name="dialog_replace_msg_some_files_exists">%1$d ملفات موجودة مسبقاً. هل تريد استبدالها؟</string>
|
||||
<string name="dialog_replace_title_single_file_exists">استبدال الملف؟</string>
|
||||
<string name="dialog_replace_title_multiple_files_exist">استبدال الملفات؟</string>
|
||||
<string name="dialog_unable_to_share_title">غير قادر علي مشاركة الملفات</string>
|
||||
@ -266,6 +281,7 @@
|
||||
<string name="dialog_disable_app_obscured_disclaimer_title">انتباه</string>
|
||||
<string name="dialog_disable_app_obscured_positive_button">تعطيل</string>
|
||||
<string name="dialog_app_is_obscured_info_title">التطبيق غامض</string>
|
||||
<string name="dialog_app_is_obscured_info_hint">يقوم تطبيق آخر بعرض شيء فوق Cryptomator (على سبيل المثال فلتر الضوء الأزرق أو تطبيق الوضع الليلي). تم تعطيل Cryptomator لأسباب أمنية.\n\n<a href="https://docs.cryptomator.org/en/1.5/android/settings/#block-app-when-obscured">كيفية تمكين Cryptomator</a></string>
|
||||
<string name="dialog_app_is_obscured_info_neutral_button">إغلاق</string>
|
||||
<string name="dialog_disable_secure_screen_disclaimer_hint">هذا الإعداد هو ميزة أمنية ويمنع التطبيقات الأخرى من خداع المستخدمين لفعل أشياء لا يريدون القيام بها.\n\nبتعطيليها, أنت تؤكد أنك <a href="https://docs.cryptomator.org/en/1.5/android/settings/#screen-security">على علم بالمخاطر</a>.</string>
|
||||
<string name="dialog_delete_cloud_connection_with_vaults_message">هل ترغب حقّا بحذف هذا الإتصال الخاص بمقدم الخدمة السحابية؟</string>
|
||||
@ -328,6 +344,7 @@
|
||||
<string name="notification_auto_upload_failed_general_error">حدث خطأ عام أثناء الرفع.</string>
|
||||
<string name="notification_auto_upload_failed_due_to_folder_not_exists">المجلد المحدد للرفع لم يعد متوفرًا. اذهب إلى الإعدادات واختيار واحد جديد</string>
|
||||
<string name="notification_auto_upload_failed_due_to_vault_locked">تم قفل المخزن اثناء الرفع، الرجاء قم بفتحه لمتابعة الرفع</string>
|
||||
<string name="notification_auto_upload_failed_due_to_vault_not_found">المخزن المحدد للتحميل التلقائي لم يعد موجودا.</string>
|
||||
<string name="notification_open_writable_file_title">فتح ملف قابل للكتابة</string>
|
||||
<string name="notification_open_writable_file_message">سوف يبقى المخزن مفتوحاً حتي يتم التعديل</string>
|
||||
<string name="notification_update_check_finished_latest">تم تثبيت آخر إصدارة</string>
|
||||
|
219
presentation/src/main/res/values-bn-rBD/strings.xml
Normal file
219
presentation/src/main/res/values-bn-rBD/strings.xml
Normal file
@ -0,0 +1,219 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- # app -->
|
||||
<string name="share_with_label">এনক্রিপ্ট করুন</string>
|
||||
<!-- # error messages -->
|
||||
<string name="error_generic">একটি ত্রুটি ঘটেছে</string>
|
||||
<string name="error_authentication_failed">সনাক্তকরণ ব্যর্থ হয়েছে</string>
|
||||
<string name="error_authentication_failed_re_authenticate">সনাক্তকরণ ব্যর্থ হয়েছে, অনুগ্রহ করে %1$s ব্যবহার করে লগইন করুন</string>
|
||||
<string name="error_no_network_connection">কোনো নেটওয়ার্ক সংযোগ নেই</string>
|
||||
<string name="error_invalid_passphrase">ভুল পাসওয়ার্ড</string>
|
||||
<string name="error_file_or_folder_exists">একটি ফাইল অথবা ফোল্ডার ইতিমধ্যে রয়েছে।</string>
|
||||
<string name="error_vault_version_not_supported">অসমর্থিত ভোল্ট। এই ভোল্টটি ক্রিপ্টোমেটরের অন্য একটি ভার্সন দিয়ে তৈরি হয়েছিল।</string>
|
||||
<string name="error_vault_already_exists">ভোল্টটি ইতিমধ্যে রয়েছে।</string>
|
||||
<string name="error_no_such_file">ফাইলটি নেই।</string>
|
||||
<string name="error_vault_has_been_locked">ভোল্টটি লক করা হয়েছে।</string>
|
||||
<string name="error_activity_not_found">ফাইলটি খুলতে পারে এমন একটি আ্যপ ডাউনলোড করুন।</string>
|
||||
<string name="error_server_not_found">সার্ভার খুজে পাওয়া যায়নি।</string>
|
||||
<string name="error_device_policy_manager_not_found">অনুগ্রহ করে ডিভাইস সেটিংস খুলে নিজে স্ক্রিন লক লাগিয়ে নিন। </string>
|
||||
<string name="error_export_illegal_file_name">এক্সপোর্ট ব্যর্থ হয়েছে। ফাইলের নাম থেকে বিশেষ অক্ষরগুলো সরিয়ে আবার এক্সপোর্ট করুন।</string>
|
||||
<string name="error_name_contains_invalid_characters">বিশেষ অক্ষর থাকা যাবে।</string>
|
||||
<string name="error_names_contains_invalid_characters">ফাইলের নামে বিশেষ অক্ষর থাকা যাবে না।</string>
|
||||
<string name="error_vault_name_contains_invalid_characters">ভোল্ট নামে বিশেষ অক্ষর থাকা যাবে না।</string>
|
||||
<string name="error_general_update">সাধারণ ত্রুটি, হালনাগাদ পরীক্ষায় ব্যর্থ হয়েছে।</string>
|
||||
<string name="error_hash_mismatch_update">হালনাগাদ পরীক্ষায় ব্যর্থ হয়েছে। গণনা করা হ্যাশ আপলোড করা ফাইলের সাথে মেলে না</string>
|
||||
<string name="error_update_no_internet">ইন্টারনেট সংযোগ নেই। হালনাগাদ পরীক্ষায় ব্যর্থ হয়েছে।</string>
|
||||
<string name="error_failed_to_decrypt_webdav_password">WebDev পাসওয়ার্ড ডিক্রিপশন ব্যর্থ হয়েছে, অনুগ্রহ করে সেটিংসে পুনরায় যোগ করুন</string>
|
||||
<string name="error_play_services_not_available">গুগল প্লে সার্ভিস ইন্সটল করা নয়।</string>
|
||||
<string name="error_biometric_auth_aborted">বায়োমেট্রিক সনাক্তকরণ ব্যর্থ হয়েছে</string>
|
||||
<string name="error_vault_version_mismatch">%1$s এ ভার্সনটি %2$s এর ভার্সন থেকে ভিন্ন</string>
|
||||
<string name="error_vault_key_invalid">%1$s এই %2$s এর সাথে মেলে না</string>
|
||||
<string name="error_vault_config_loading">ভোল্ট কোনফিগারেশন লোডিংএ সাধারণ ত্রুটি দেখা দিয়েছে</string>
|
||||
<string name="error_file_not_found_after_opening_using_3party">ক্রিপ্টোমেটরে ফিরে যাওয়ার পর লোকাল ফাইলটি বিদ্যমান নয়। তাই সম্ভাব্য পরিবর্তনগুলো আবার ক্লাউডে স্থানান্তর করা হবে না।</string>
|
||||
<!-- # clouds -->
|
||||
<!-- ## cloud names -->
|
||||
<string name="cloud_names_local_storage">সিস্টেম স্টোরেজ</string>
|
||||
<!-- # permission -->
|
||||
<!-- ## permission messages -->
|
||||
<string name="snack_bar_action_title_settings">সেটিংস</string>
|
||||
<string name="snack_bar_action_title_search">খুঁজুন</string>
|
||||
<string name="snack_bar_action_title_search_previous">পিছনে</string>
|
||||
<string name="snack_bar_action_title_search_next">পরবর্তী</string>
|
||||
<string name="snack_bar_action_title_sort">সাজান</string>
|
||||
<string name="snack_bar_action_title_sort_az">A - Z</string>
|
||||
<string name="snack_bar_action_title_sort_za">Z - A</string>
|
||||
<string name="snack_bar_action_title_sort_newest">নতুন থেকে</string>
|
||||
<string name="snack_bar_action_title_sort_oldest">পুরনো থেকে</string>
|
||||
<string name="snack_bar_action_title_sort_biggest">বড় আকারের থেকে</string>
|
||||
<string name="snack_bar_action_title_sort_smallest">ছোট আকারের থেকে</string>
|
||||
<!-- # screens -->
|
||||
<!-- # screen: vault list -->
|
||||
<string name="screen_vault_list_actions_title">ক্রিপ্টোমেটরে যুক্ত করুন</string>
|
||||
<string name="screen_vault_list_action_create_new_vault">নতুন ভোল্ট তৈরি করুন</string>
|
||||
<string name="screen_vault_list_action_add_existing_vault">বিদ্যমান কোনো ভোল্ট যুক্ত করুন</string>
|
||||
<string name="screen_vault_list_vault_action_delete">বাতিল</string>
|
||||
<string name="screen_vault_list_vault_creation_hint">নতুন ভোল্ট তৈরি করতে এখানে ক্লিক করুন</string>
|
||||
<string name="screen_vault_list_change_password_successful">পাসওয়ার্ড সফলভাবে পরিবর্তিত হয়েছে</string>
|
||||
<!-- # screen: file browser -->
|
||||
<string name="screen_file_browser_default_title">ভোল্ট</string>
|
||||
<string name="screen_file_browser_add_existing_vault_extra_text">মাস্টার কী ফাইলটি নির্বাচন করুন</string>
|
||||
<string name="screen_file_browser_create_new_vault_button_text">এখানে রাখুন</string>
|
||||
<string name="screen_file_browser_create_new_vault_extra_text">ভোল্টের নাম: %1$s</string>
|
||||
<plurals name="screen_file_browser_subtitle_move">
|
||||
<item quantity="one">%1$s স্থানান্তর করুন</item>
|
||||
<item quantity="other">%2$d আইটেম স্থানান্তর করুন</item>
|
||||
</plurals>
|
||||
<string name="screen_file_browser_move_button_text">স্থানান্তর করুন</string>
|
||||
<string name="screen_file_browser_msg_empty_folder">খালি ফোল্ডার</string>
|
||||
<string name="screen_file_browser_file_info_label_date">%1$s আগে পরিবর্তিত হয়েছে</string>
|
||||
<string name="screen_file_browser_share_intent_chooser_title">শেয়ার করুন</string>
|
||||
<string name="screen_file_browser_share_destination_title">একটি গন্তব্য নির্বাচন করুন</string>
|
||||
<string name="screen_file_browser_share_button_text">বাছুন</string>
|
||||
<string name="screen_file_browser_nothing_to_share">শেয়ার করার মত কিছু নেই</string>
|
||||
<string name="screen_file_browser_actions_title">%1$s এ যুক্ত করুন</string>
|
||||
<string name="screen_file_browser_action_create_folder">ফোল্ডার তৈরি করুন</string>
|
||||
<string name="screen_file_browser_action_create_new_text_file">টেক্সট ফাইল তৈরি করুন</string>
|
||||
<string name="screen_file_browser_action_upload_files">ফাইল আপলোড করুন</string>
|
||||
<string name="screen_file_browser_upload_files_chooser_title">ফাইল</string>
|
||||
<string name="screen_file_browser_msg_file_exported">ফাইল এক্সপোর্ট হয়েছে</string>
|
||||
<string name="screen_file_browser_msg_files_exported">ফাইলগুলো এক্সপোর্ট হয়েছে</string>
|
||||
<string name="screen_file_browser_nothing_to_export">এক্সপোর্ট করার মত কিছু নেই</string>
|
||||
<string name="screen_file_browser_msg_creating_download_dir_failed">ডাউনলোড ডিরেক্টরি তৈরিতে ব্যর্থ হয়েছে</string>
|
||||
<string name="screen_file_browser_node_action_share">শেয়ার</string>
|
||||
<string name="screen_file_browser_node_action_rename">নাম পরিবর্তন করুন</string>
|
||||
<string name="screen_file_browser_node_action_edit_text">এডিট</string>
|
||||
<string name="screen_file_browser_node_action_export">এক্সপোর্ট</string>
|
||||
<string name="screen_file_browser_node_action_delete">মুছুন</string>
|
||||
<string name="screen_file_browser_node_action_open_with_text">খোলা হক…</string>
|
||||
<string name="screen_file_browser_selection_mode_title_zero_elements">আইটেম বাছুন</string>
|
||||
<string name="screen_file_browser_selection_mode_title_one_or_more_elements">%1$dটি নির্বাচিত</string>
|
||||
<string name="screen_file_browser_select_items">নির্বাচন করুন</string>
|
||||
<string name="screen_file_browser_select_all_items">সবগুলো নির্বাচন করুন</string>
|
||||
<string name="screen_file_browser_refresh_items">রিফ্রেশ</string>
|
||||
<string name="screen_file_browser_no_connection_message">কোনো সংযোগ নেই</string>
|
||||
<string name="screen_file_browser_no_connection_button_text">পুনরায় চেষ্টা করুন</string>
|
||||
<!-- ## screen: text editor -->
|
||||
<string name="screen_text_editor_save_success">সফলভাবে সংরক্ষিত হয়েছে</string>
|
||||
<!-- ## screen: share files -->
|
||||
<string name="screen_share_files_title">%1$s … তে সংরক্ষণ করুন</string>
|
||||
<string name="screen_share_files_content_text">টেক্সট</string>
|
||||
<string name="screen_share_files_content_file">ফাইল</string>
|
||||
<string name="screen_share_files_content_files">ফাইল</string>
|
||||
<string name="screen_share_files_msg_filenames_must_be_unique">ফাইলের নাম অনন্য হতে হবে, অনুগ্রহ করে একই নাম পরিবর্তন করুন।</string>
|
||||
<string name="screen_share_files_section_location">সংরক্ষণের ঠিকানা</string>
|
||||
<string name="screen_share_files_save_button_text">সংরক্ষণ করুন</string>
|
||||
<string name="screen_share_files_msg_success">এনক্রিপশন সম্পন্ন হয়েছে</string>
|
||||
<!-- ## screen: choose cloud service -->
|
||||
<string name="screen_choose_cloud_service_title">ক্লাউড পরিষেবা</string>
|
||||
<!-- ## screen: cloud connections -->
|
||||
<string name="screen_cloud_connections_title">একটি ঠিকানা বাছাই করুন</string>
|
||||
<string name="screen_cloud_connections_no_connections">ঠিকানা যোগ করতে এখানে ক্লিক করুন</string>
|
||||
<!-- ## screen: webdav settings -->
|
||||
<string name="screen_webdav_settings_url_port_label">ইউআরএল</string>
|
||||
<string name="screen_webdav_settings_username_label">ব্যবহারকারীর নাম</string>
|
||||
<string name="screen_webdav_settings_password_label">পাসওয়ার্ড</string>
|
||||
<string name="screen_webdav_settings_done_button_text">কানেক্ট</string>
|
||||
<string name="screen_webdav_settings_msg_url_must_not_be_empty">URL খালি রাখা যাবে না।</string>
|
||||
<string name="screen_webdav_settings_msg_url_is_invalid">URL টি ভুল।</string>
|
||||
<string name="screen_webdav_settings_msg_username_must_not_be_empty">ব্যবহারকারীর নাম খালি রাখা যাবে না।</string>
|
||||
<string name="screen_webdav_settings_msg_password_must_not_be_empty">পাসওয়ার্ড খালি থাকতে পারবে না।</string>
|
||||
<!-- ## screen: s3 settings -->
|
||||
<string name="screen_s3_settings_display_name_label">নাম প্রদর্শন করুন</string>
|
||||
<string name="screen_s3_settings_bucket_label">বিদ্যমান বাকেট</string>
|
||||
<string name="screen_s3_settings_endpoint_label">এন্ডপয়েন্ট</string>
|
||||
<string name="screen_s3_settings_region_label">অঞ্চল</string>
|
||||
<string name="screen_s3_settings_msg_display_name_not_empty">নাম খালি হতে পারব না</string>
|
||||
<!-- ## screen: enter vault name -->
|
||||
<string name="screen_enter_vault_name_vault_label">ভোল্ট এর নাম</string>
|
||||
<string name="screen_enter_vault_name_button_text">তৈরি করুন</string>
|
||||
<!-- ## screen: set password -->
|
||||
<string name="screen_set_password_title">পাসওয়ার্ড সেট করুন</string>
|
||||
<string name="screen_set_password_msg_password_mismatch">পাসওয়ার্ড মেলেনা, আবার লিখুন।</string>
|
||||
<string name="screen_set_password_button_text">সম্পন্ন হয়েছে</string>
|
||||
<string name="screen_set_password_hint">সতর্কতা: আপনি যদি আপনার পাসওয়ার্ড ভুলে যান, তাহলে পাসওয়ার্ডটি পুনরুদ্ধার করার কোনো উপায় থাকবে না।</string>
|
||||
<string name="screen_set_password_retype_password_label">পুনরায় পাসওয়ার্ডটি লিখুন</string>
|
||||
<string name="screen_set_password_strength_indicator_0">ভোল্টটি তৈরির জন্য পাসওয়ার্ডটি অত্যন্ত দুর্বল</string>
|
||||
<string name="screen_set_password_strength_indicator_1">দুর্বল</string>
|
||||
<string name="screen_set_password_strength_indicator_2">ভালো</string>
|
||||
<string name="screen_set_password_strength_indicator_3">শক্তিশালী</string>
|
||||
<string name="screen_set_password_strength_indicator_4">খুবই শক্তিশালী</string>
|
||||
<!-- ## screen: settings -->
|
||||
<string name="screen_settings_section_general">সাধারণ বিষয়াদি</string>
|
||||
<string name="screen_settings_cloud_settings_label">ক্লাউড পরিষেবাসমূহ</string>
|
||||
<string name="screen_settings_biometric_auth">বায়োমেট্রিক সনাক্তকরণ</string>
|
||||
<string name="screen_settings_activate_biometric_auth">বায়োমেট্রিক সনাক্তকরণ সক্রিয় করুন</string>
|
||||
<string name="screen_settings_confirm_face_unlock">ফেইস আনলক নিশ্চিত করুন (যদি থাকে)</string>
|
||||
<string name="screen_settings_block_app_when_obscured">অন্যান্য আ্যপকে ক্রিপ্টোমেটর ব্যবহারে বাধা দিন</string>
|
||||
<string name="screen_settings_secure_screen">স্ক্রিনশট নিতে বাধা দিন</string>
|
||||
<string name="screen_settings_section_search">খুঁজুন</string>
|
||||
<string name="screen_settings_auto_lock_on_screen_lock">যখন স্ক্রিন বন্ধ থাকে</string>
|
||||
<string name="screen_settings_section_auto_photo_upload">স্বয়ংক্রিয় ছবি আপলোড</string>
|
||||
<string name="screen_settings_section_auto_photo_upload_vault">আপলোডের জন্য ভোল্ট নির্বাচন করুন</string>
|
||||
<string name="screen_settings_section_auto_photo_upload_toggle">সক্রিয় করুন</string>
|
||||
<string name="screen_settings_section_auto_photo_upload_toggle_summary">ব্যাকগ্রাউন্ডে ছবি তুলুন এবং যখন নির্বাচিত ভোল্টটি আনলক করা হবে, আপলোড করুন</string>
|
||||
<string name="screen_settings_section_auto_photo_upload_toggle_instant_upload_summary">ভোল্ট আনলোক থাকলে সরাসরি আপলোড করুন</string>
|
||||
<string name="screen_settings_section_auto_photo_upload_only_wifi_toggle">শুধু WiFi ব্যবহার করে আপলোড করুন</string>
|
||||
<string name="screen_settings_section_auto_photo_upload_including_videos">ভিডিও আপলোড করুন</string>
|
||||
<string name="screen_settings_auto_photo_upload_title">… তে অটো আপলোড ফাইলগুলি সংরক্ষণ করুন</string>
|
||||
<string name="screen_settings_website_label">ক্রিপ্টোমেটরের ওয়েবসাইট</string>
|
||||
<string name="screen_settings_twitter_label">টুইটারে আমাদের অনুসরণ করুন</string>
|
||||
<string name="screen_settings_facebook_label">ফেসবুকে আমাদের লাইক দিন</string>
|
||||
<string name="screen_settings_section_legal">আইনি</string>
|
||||
<string name="screen_settings_licenses_label">লাইসেন্স</string>
|
||||
<string name="screen_settings_license_terms_label">লাইসেন্স এর শর্তাবলি</string>
|
||||
<string name="screen_settings_section_support">সহায়তা</string>
|
||||
<string name="screen_settings_contact_label">সাহায্য জিজ্ঞাসা</string>
|
||||
<string name="screen_settings_debug_mode_label">ডিবাগ মোড</string>
|
||||
<string name="screen_settings_error_report_label">লগ ফাইলটি পাঠান</string>
|
||||
<string name="screen_settings_error_report_failed">পাঠাতে ব্যর্থ হয়েছে</string>
|
||||
<string name="screen_settings_section_version">সংস্করণ</string>
|
||||
<string name="screen_settings_advanced_settings">অ্যাডভান্স সেটিংস</string>
|
||||
<string name="screen_settings_keep_unlocked_while_editing_files">আনলক রাখুন</string>
|
||||
<string name="screen_settings_keep_unlocked_while_editing_files_summary">ফাইল সম্পাদনার সময় ভোল্ট আনলক রাখুন</string>
|
||||
<!-- ## screen: cloud settings -->
|
||||
<string name="screen_cloud_settings_onedrive_connections">OneDrive সংযোগ</string>
|
||||
<string name="screen_cloud_settings_webdav_connections">WebDAV সংযোগ</string>
|
||||
<string name="screen_cloud_settings_pcloud_connections">pCloud সংযোগ</string>
|
||||
<string name="screen_cloud_settings_s3_connections">S3 সংযোগ</string>
|
||||
<string name="screen_cloud_settings_local_storage_locations">লোকাল স্টোরেজ এর ঠিকানা</string>
|
||||
<string name="screen_cloud_settings_log_in_to">লগ ইন</string>
|
||||
<string name="screen_cloud_settings_sign_out_from_cloud">সাইন আউট</string>
|
||||
<!-- ## screen: licenses -->
|
||||
<!-- ## screen: authenticate cloud -->
|
||||
<!-- ## screen: insecure android version info -->
|
||||
<!-- # dialogs -->
|
||||
<string name="dialog_button_cancel">বাতিল করুন</string>
|
||||
<string name="dialog_enter_password_positive_button">আনলক করুন</string>
|
||||
<string name="dialog_change_password_old_password_label">পুরাতন পাসওয়ার্ড</string>
|
||||
<string name="dialog_change_password_new_password_label">নতুন পাসওয়ার্ড</string>
|
||||
<string name="dialog_change_password">পাসওয়ার্ড পরিবর্তন করুন</string>
|
||||
<string name="dialog_change_password_msg_old_password_empty">পুরনো পাসওয়ার্ড অংশ খালি থাকতে পারবে না।</string>
|
||||
<string name="dialog_change_password_msg_new_password_empty">নতুন পাসওয়ার্ড অংশ খালি থাকতে পারবে না।</string>
|
||||
<string name="dialog_change_password_msg_password_mismatch">নতুন পাসওয়ার্ডটি পুনরায় টাইপ করা পাসওয়ার্ডের সাথে মেলে না।</string>
|
||||
<!-- Vault not found -->
|
||||
<string name="dialog_vault_not_found_title">%1$s ভোল্টটি পাওয়া যায়নি</string>
|
||||
<string name="dialog_vault_not_found_message">ভোল্টটির হয়তো নাম পরিবর্তন, স্থানান্তর বা মুছে ফেলা হয়েছে। তালিকা থেকে ভোল্টটি সরান এবং চালিয়ে যেতে পুনরায় যোগ করুন। ভোল্টটি সরাবেন?</string>
|
||||
<string name="dialog_vault_not_found_positive_button_text">বাতিল</string>
|
||||
<string name="dialog_existing_file_title">ফাইলটি ইতিমধ্যে রয়েছে</string>
|
||||
<string name="dialog_existing_file_positive_button">প্রতিস্থাপন করুন</string>
|
||||
<string name="dialog_existing_file_message">%1$s নামে একটি ফাইল ইতিমধ্যে রয়েছে.</string>
|
||||
<string name="dialog_replace_negative_button_at_least_two_but_not_all_files_exist">বিদ্যমান ফাইলটি এড়িয়ে যান</string>
|
||||
<string name="dialog_replace_positive_button_all_files_exist">সবগুলো প্রতিস্থাপন করুন</string>
|
||||
<string name="dialog_replace_positive_button_some_files_exist">বিদ্যমানটি প্রতিস্থাপন করুন</string>
|
||||
<string name="dialog_replace_positive_button_single_file_exists">প্রতিস্থাপন করুন</string>
|
||||
<string name="dialog_replace_msg_single_file_exists">%1$s নামে একটি ফাইল ইতিমধ্যে রয়েছে। আপনি কি এটা প্রতিস্থাপন করতে চান?</string>
|
||||
<string name="dialog_replace_msg_all_files_exists">সব ফাইলগুলি ইতিমধ্যে রয়েছে। আপনি কি তাদের প্রতিস্থাপন করতে চান?</string>
|
||||
<string name="dialog_lock_vault">লক করুন</string>
|
||||
<string name="dialog_app_is_obscured_info_neutral_button">বন্ধ করুন</string>
|
||||
<string name="dialog_sym_link_back_button">পিছনে</string>
|
||||
<!-- # error reports -->
|
||||
<!-- # misc -->
|
||||
<!-- ## file size helper -->
|
||||
<!-- ## date helper -->
|
||||
<!-- ## biometric authentication -->
|
||||
<!-- notification -->
|
||||
<!-- lock timeout names -->
|
||||
<!-- cache size names -->
|
||||
<!-- screen scheme mode names -->
|
||||
<!-- update interval names -->
|
||||
</resources>
|
@ -16,6 +16,7 @@
|
||||
<string name="error_cloud_already_exists">Cloud již existuje.</string>
|
||||
<string name="error_activity_not_found">Stáhněte si prosím aplikaci, která umí otevřít tento soubor.</string>
|
||||
<string name="error_server_not_found">Server nenalezen.</string>
|
||||
<string name="error_device_policy_manager_not_found">Otevřete prosím nastavení zařízení a nastavte zámek obrazovky ručně</string>
|
||||
<string name="error_export_illegal_file_name">Export se nezdařil. Zkuste odstranit speciální znaky z názvů souborů a znovu exportovat.</string>
|
||||
<string name="error_name_contains_invalid_characters">Nesmí obsahovat speciální znaky.</string>
|
||||
<string name="error_names_contains_invalid_characters">Názvy souborů nesmí obsahovat speciální znaky.</string>
|
||||
@ -23,8 +24,11 @@
|
||||
<string name="error_general_update">Kontrola aktualizací se nezdařila. Došlo k obecné chybě.</string>
|
||||
<string name="error_hash_mismatch_update">Kontrola aktualizace se nezdařila. Hash neodpovídá nahranému souboru</string>
|
||||
<string name="error_update_no_internet">Kontrola aktualizací se nezdařila. Žádné připojení k internetu.</string>
|
||||
<string name="error_failed_to_decrypt_webdav_password">Dešifrování WebDAV hesla se nezdařilo, přidejte jej prosím znovu do nastavení</string>
|
||||
<string name="error_play_services_not_available">Obchod Google Play není nainstalován</string>
|
||||
<string name="error_biometric_auth_aborted">Ověření pomocí otisků prstů selhalo</string>
|
||||
<string name="error_vault_version_mismatch">Verze zadaná v %1$s se liší od verze %2$s</string>
|
||||
<string name="error_vault_key_invalid">%1$s se neshoduje s %2$s</string>
|
||||
<string name="error_vault_config_loading">Obecná chyba při načítání konfigurace trezoru</string>
|
||||
<string name="error_file_not_found_after_opening_using_3party">Místní soubor již není přítomen po přepnutí zpět na Cryptomator. Možné změny nelze promítnout zpět do cloudu.</string>
|
||||
<string name="error_no_such_bucket">Bucket neexistuje</string>
|
||||
@ -37,6 +41,7 @@
|
||||
<string name="permission_message_export_file">Cryptomator potřebuje přístup k úložišti pro export souborů</string>
|
||||
<string name="permission_message_upload_file">Cryptomator potřebuje přístup k úložišti pro nahrávání souborů</string>
|
||||
<string name="permission_message_share_file">Cryptomator potřebuje přístup k úložišti pro sdílení souborů</string>
|
||||
<string name="permission_revoked_re_request_permission">Cryptomator ztratil oprávnění pro přístup k tomuto umístění. Prosím vyberte tuto složku znovu pro obnovení oprávnění.</string>
|
||||
<string name="snack_bar_action_title_settings">Nastavení</string>
|
||||
<string name="snack_bar_action_title_search">Vyhledat</string>
|
||||
<string name="snack_bar_action_title_search_previous">Předchozí</string>
|
||||
@ -196,6 +201,7 @@
|
||||
<string name="screen_settings_keep_unlocked_while_editing_files">Ponechat odemčené</string>
|
||||
<string name="screen_settings_keep_unlocked_while_editing_files_summary">Ponechat trezory odemčené při úpravách souborů</string>
|
||||
<!-- ## screen: cloud settings -->
|
||||
<string name="screen_cloud_settings_onedrive_connections">Spojení OneDrive</string>
|
||||
<string name="screen_cloud_settings_webdav_connections">Připojení WebDAV</string>
|
||||
<string name="screen_cloud_settings_pcloud_connections">pCloud připojení</string>
|
||||
<string name="screen_cloud_settings_s3_connections">S3 připojení</string>
|
||||
@ -218,6 +224,7 @@
|
||||
<string name="dialog_change_password_msg_password_mismatch">Hesla se neshodují.</string>
|
||||
<!-- Vault not found -->
|
||||
<string name="dialog_vault_not_found_title">Trezor %1$s nebyl nalezen</string>
|
||||
<string name="dialog_vault_not_found_message">Trezor byl přejmenován, přesunut nebo odstraněn. Odstraňte tento trezor ze seznamu a znovu jej přidejte pro pokračování. Odstranit nyní?</string>
|
||||
<string name="dialog_vault_not_found_positive_button_text">Odstranit</string>
|
||||
<string name="dialog_existing_file_title">Soubor již existuje</string>
|
||||
<string name="dialog_existing_file_positive_button">Nahradit</string>
|
||||
@ -286,6 +293,9 @@
|
||||
<string name="dialog_app_is_obscured_info_hint">Jiná aplikace zobrazuje něco nad Cryptomatorem (např. filtr modrého světla nebo aplikace pro noční režim). Z bezpečnostních důvodů je Cryptomator zakázán.\n\n<a href="https://docs.cryptomator.org/en/1.5/android/settings/#block-app-when-obscured">Jak povolit Cryptomator</a></string>
|
||||
<string name="dialog_app_is_obscured_info_neutral_button">Zavřít</string>
|
||||
<string name="dialog_vaults_removed_during_migration_title">Prosím znovu přidejte trezory pro %1s cloud</string>
|
||||
<string name="dialog_vaults_removed_during_migration_hint">Při migraci na tuto verzi aplikace musíme z aplikace odstranit následující trezory:\n%2s \n\nTyto trezory nejsou odebrány z cloudu, ale pouze z této aplikace. Omlouváme se za nepříjemnosti a znovu přidejte tyto trezory, abyste s nimi mohli dále pracovat.</string>
|
||||
<string name="dialog_vault_is_root_folder_of_cloud_title">Trezor je kořenovou složkou cloudového připojení</string>
|
||||
<string name="dialog_vault_is_root_folder_of_cloud_hint">Vytvořte nové připojení do cloudu, kde jako kořenový adresář zvolíte alespoň nadřazenou složku tohoto trezoru.</string>
|
||||
<string name="dialog_disable_secure_screen_disclaimer_hint">Toto nastavení je bezpečnostní funkce a brání ostatním aplikacím v tom, aby oklamaly uživatele dělat věci, se kterými nesouhlasí.\n\nZakázáním potvrzujete, že jste <a href="https://docs.cryptomator.org/en/1.5/android/settings/#screen-security">si vědomi rizik</a>.</string>
|
||||
<string name="dialog_delete_cloud_connection_with_vaults_message">Opravdu chcete odstranit toto cloudové připojení?</string>
|
||||
<string name="dialog_delete_cloud_connection_with_vaults_hint">Tato akce odstraní cloudové připojení a všechny trezory tohoto cloudu.</string>
|
||||
@ -365,6 +375,7 @@
|
||||
<string name="notification_open_writable_file_title">Otevřít zapisovatelný soubor</string>
|
||||
<string name="notification_open_writable_file_message">Trezor zůstane odemknutý dokud nejsou dokončené úpravy</string>
|
||||
<string name="notification_update_check_finished_latest">Nainstalována aktuální verze</string>
|
||||
<string name="notification_authenticating">Ověřování…</string>
|
||||
<string name="screen_settings_lru_cache">Mezipaměť</string>
|
||||
<string name="screen_settings_lru_cache_toggle_summary">Uložit nedávno otevřené soubory šifrovaně do lokální mezipaměti pro pozdější použití</string>
|
||||
<string name="screen_settings_lru_cache_size">Velikost mezipaměti</string>
|
||||
|
@ -28,7 +28,7 @@
|
||||
<string name="error_play_services_not_available">Die Google Play Services sind nicht installiert</string>
|
||||
<string name="error_biometric_auth_aborted">Biometrischer Login abgebrochen</string>
|
||||
<string name="error_vault_version_mismatch">Die in %1$s angegebene Version ist nicht identisch mit der Version in %2$s</string>
|
||||
<string name="error_vault_key_invalid">%1$s-Datei stimmt nicht mit der %2$s-Datei überein</string>
|
||||
<string name="error_vault_key_invalid">%1$s stimmt nicht mit %2$s überein</string>
|
||||
<string name="error_vault_config_loading">Allgemeiner Fehler beim Laden der Tresorkonfiguration</string>
|
||||
<string name="error_file_not_found_after_opening_using_3party">Lokale Datei ist nach dem Zurückwechseln zu Cryptomator nicht mehr vorhanden. Mögliche Änderungen können nicht in die Cloud übertragen werden.</string>
|
||||
<string name="error_no_such_bucket">Bucket existiert nicht</string>
|
||||
@ -199,6 +199,7 @@
|
||||
<string name="screen_settings_keep_unlocked_while_editing_files">Entsperrt bleiben</string>
|
||||
<string name="screen_settings_keep_unlocked_while_editing_files_summary">Halte Tresore geöffnet während dem Editieren einer Datei</string>
|
||||
<!-- ## screen: cloud settings -->
|
||||
<string name="screen_cloud_settings_onedrive_connections">OneDrive Verbindungen</string>
|
||||
<string name="screen_cloud_settings_webdav_connections">WebDAV-Verbindungen</string>
|
||||
<string name="screen_cloud_settings_pcloud_connections">pCloud-Verbindungen</string>
|
||||
<string name="screen_cloud_settings_s3_connections">S3-Verbindungen</string>
|
||||
@ -236,15 +237,15 @@
|
||||
<string name="dialog_replace_title_single_file_exists">Datei ersetzen?</string>
|
||||
<string name="dialog_replace_title_multiple_files_exist">Dateien ersetzen?</string>
|
||||
<string name="dialog_unable_to_share_title">Teilen nicht möglich</string>
|
||||
<string name="dialog_unable_to_share_message">Sie haben keinen Tresor eingerichtet. Bitte legen Sie zuerst einen Tresor mit der Cryptomator-App an.</string>
|
||||
<string name="dialog_unable_to_share_message">Du hast keinen Tresor eingerichtet. Bitte lege zuerst einen Tresor mit der Cryptomator-App an.</string>
|
||||
<string name="dialog_unable_to_share_positive_button">OK</string>
|
||||
<string name="dialog_unable_to_share_negative_button">Tresor erstellen</string>
|
||||
<string name="dialog_filetype_not_supported_title">%1$s kann nicht geöffnet werden</string>
|
||||
<string name="dialog_filetype_not_supported_message">Bitte installieren Sie eine App, die diese Datei öffnen kann. Möchten Sie die Datei stattdessen auf dem Gerät speichern?</string>
|
||||
<string name="dialog_filetype_not_supported_message">Bitte installiere eine App, die diese Datei öffnen kann. Möchtest du die Datei stattdessen auf dem Gerät speichern?</string>
|
||||
<string name="dialog_rename_vault_title">Tresor umbenennen</string>
|
||||
<string name="dialog_rename_node_folder_title">Ordner umbenennen</string>
|
||||
<string name="dialog_rename_node_file_title">Datei umbenennen</string>
|
||||
<string name="dialog_unsaved_changes_title">Sie haben ungespeicherte Änderungen</string>
|
||||
<string name="dialog_unsaved_changes_title">Du hast nicht gespeicherte Änderungen</string>
|
||||
<string name="dialog_unsaved_changes_message">Möchtest du wirklich beenden, ohne zu speichern?</string>
|
||||
<string name="dialog_unsaved_changes_discard">Verwerfen</string>
|
||||
<string name="dialog_file_name_placeholder">text.txt</string>
|
||||
@ -301,7 +302,7 @@
|
||||
<string name="dialog_confirm_delete_file_message">Möchtest du dieses Element wirklich löschen?</string>
|
||||
<string name="dialog_confirm_delete_folder_message">Dieser Vorgang wird den gesamten Ordnerinhalt löschen. Bist du sicher, dass du diesen Ordner löschen möchtest?</string>
|
||||
<string name="dialog_biometric_auth_key_invalidated_title">Biometrischer Login deaktiviert</string>
|
||||
<string name="dialog_biometric_auth_key_invalidated_message">Da der Schlüssel nicht mehr zur Verfügung steht, wurde der Biometrischer Login deaktiviert. Zur Reaktivierung öffnen Sie die Cryptomator-Einstellungen.</string>
|
||||
<string name="dialog_biometric_auth_key_invalidated_message">Da der Schlüssel nicht mehr zur Verfügung steht, wurde der Biometrischer Login deaktiviert. Zur Reaktivierung öffne die Cryptomator-Einstellungen.</string>
|
||||
<string name="dialog_enter_license_title">APK-Store Lizenz</string>
|
||||
<string name="dialog_enter_license_content">Cryptomator wurde nicht über den Google Play Store installiert; gib daher eine gültige Lizenz ein. Diese kann auf <a href="https://cryptomator.org/android/">https://cryptomator.org/android/</a> erworben werden.</string>
|
||||
<string name="dialog_enter_license_not_valid_content">Die eingegebene Lizenz ist ungültig. Stelle sicher, dass sie korrekt eingegeben wurde.</string>
|
||||
@ -317,7 +318,7 @@
|
||||
<string name="dialog_download_update_title">Download in Ausführung</string>
|
||||
<string name="dialog_download_update_message">Lade die aktuelle Version von Cryptomator herunter</string>
|
||||
<string name="dialog_sym_link_title">Dieser Ordner ist ein symbolischer Link</string>
|
||||
<string name="dialog_sym_link_message">Sie können nicht in diesen symbolischen Link navigieren</string>
|
||||
<string name="dialog_sym_link_message">Du kannst nicht in diesen symbolischen Link navigieren</string>
|
||||
<string name="dialog_sym_link_back_button">Zurück</string>
|
||||
<string name="dialog_no_dir_file_title">Verzeichnisinhalt kann nicht geladen werden</string>
|
||||
<string name="dialog_no_dir_file_message">Der Ordner \'%1$s\' in der Cloud hat keine gültige Verzeichnis-Datei. Es könnte sein, dass der Ordner auf einem anderen Gerät erstellt und noch nicht vollständig mit der Cloud synchronisiert wurde. Bitte überprüfe, ob die folgende Datei in der Cloud existiert und nicht leer ist:\n%2$s</string>
|
||||
@ -372,6 +373,7 @@
|
||||
<string name="notification_open_writable_file_title">Datei mit Schreibrechten geöffnet</string>
|
||||
<string name="notification_open_writable_file_message">Tresor bleibt entsperrt bis die Datei nicht mehr editiert wird</string>
|
||||
<string name="notification_update_check_finished_latest">Neueste Version installiert</string>
|
||||
<string name="notification_authenticating">Melde an…</string>
|
||||
<string name="screen_settings_lru_cache">Zwischenspeicher</string>
|
||||
<string name="screen_settings_lru_cache_toggle_summary">Speichere kürzlich geöffnete Dateien lokal und verschlüsselt auf dem Gerät für eine spätere Wiederverwendung beim erneuten öffnen</string>
|
||||
<string name="screen_settings_lru_cache_size">Zwischenspeichergröße insgesamt</string>
|
||||
|
@ -199,6 +199,7 @@
|
||||
<string name="screen_settings_keep_unlocked_while_editing_files">Κρατήστε ξεκλείδωτο</string>
|
||||
<string name="screen_settings_keep_unlocked_while_editing_files_summary">Κρατήστε τις κρύπτες ξεκλειδωμένες κατά την επεξεργασία αρχείων</string>
|
||||
<!-- ## screen: cloud settings -->
|
||||
<string name="screen_cloud_settings_onedrive_connections">Συνδέσεις OneDrive</string>
|
||||
<string name="screen_cloud_settings_webdav_connections">Συνδέσεις WebDAV</string>
|
||||
<string name="screen_cloud_settings_pcloud_connections">Συνδέσεις pCloud</string>
|
||||
<string name="screen_cloud_settings_s3_connections">Συνδέσεις S3</string>
|
||||
@ -372,6 +373,7 @@
|
||||
<string name="notification_open_writable_file_title">Άνοιγμα εγγράψιμου αρχείου</string>
|
||||
<string name="notification_open_writable_file_message">Η κρύπτη παραμένει ξεκλείδωτη μέχρι να τελειώσει η επεξεργασία</string>
|
||||
<string name="notification_update_check_finished_latest">Τελευταία έκδοση εγκατεστημένη</string>
|
||||
<string name="notification_authenticating">Ταυτοποίηση…</string>
|
||||
<string name="screen_settings_lru_cache">Προσωρινή μνήμη</string>
|
||||
<string name="screen_settings_lru_cache_toggle_summary">Η προσωρινή μνήμη απέκτησε πρόσφατα πρόσβαση σε αρχεία που έχουν κρυπτογραφηθεί τοπικά στη συσκευή για μελλοντική επαναχρησιμοποίηση όταν ανοίξουν ξανά</string>
|
||||
<string name="screen_settings_lru_cache_size">Συνολικό μέγεθος προσωρινής μνήμης</string>
|
||||
|
@ -5,16 +5,16 @@
|
||||
<!-- # error messages -->
|
||||
<string name="error_generic">Se ha producido un error</string>
|
||||
<string name="error_authentication_failed">Ha fallado la autenticación</string>
|
||||
<string name="error_authentication_failed_re_authenticate">Autenticación fallida. Por favor, inicie sesión usando %1$s</string>
|
||||
<string name="error_authentication_failed_re_authenticate">Error de autenticación. Por favor, inicie sesión usando %1$s</string>
|
||||
<string name="error_no_network_connection">No hay conexión de red</string>
|
||||
<string name="error_invalid_passphrase">Contraseña incorrecta</string>
|
||||
<string name="error_file_or_folder_exists">Ya existe un archivo o carpeta.</string>
|
||||
<string name="error_vault_version_not_supported">Caja fuerte no soportada. Se ha creado con otra versión de Cryptomator.</string>
|
||||
<string name="error_vault_already_exists">La caja fuerte ya existe.</string>
|
||||
<string name="error_vault_version_not_supported">Bóveda no soportada. Se ha creado con otra versión de Cryptomator.</string>
|
||||
<string name="error_vault_already_exists">La bóveda ya existe.</string>
|
||||
<string name="error_no_such_file">El archivo no existe.</string>
|
||||
<string name="error_vault_has_been_locked">Se ha bloqueado la caja fuerte.</string>
|
||||
<string name="error_vault_has_been_locked">Se ha bloqueado la bóveda.</string>
|
||||
<string name="error_cloud_already_exists">La nube ya existe.</string>
|
||||
<string name="error_activity_not_found">Por favor, descarga una aplicación que pueda abrir este archivo.</string>
|
||||
<string name="error_activity_not_found">Descargue una aplicación que pueda abrir este archivo.</string>
|
||||
<string name="error_server_not_found">Servidor no encontrado.</string>
|
||||
<string name="error_device_policy_manager_not_found">Abra los ajustes de su dispositivo y establezca el bloqueo de pantalla manualmente</string>
|
||||
<string name="error_export_illegal_file_name">Exportación fallida. Intente eliminar los caracteres especiales de los nombres de archivo y vuelva a exportar.</string>
|
||||
@ -38,9 +38,9 @@
|
||||
<string name="cloud_names_local_storage">Almacenamiento local</string>
|
||||
<!-- # permission -->
|
||||
<!-- ## permission messages -->
|
||||
<string name="permission_message_export_file">Cryptomator necesita acceso al almacenamiento para exportar archivos.</string>
|
||||
<string name="permission_message_upload_file">Cryptomator necesita acceso al almacenamiento para subir archivos.</string>
|
||||
<string name="permission_message_share_file">Cryptomator necesita acceso al almacenamiento para compartir archivos.</string>
|
||||
<string name="permission_message_export_file">Cryptomator necesita acceso al almacenamiento para exportar archivos</string>
|
||||
<string name="permission_message_upload_file">Cryptomator necesita acceso al almacenamiento para subir archivos</string>
|
||||
<string name="permission_message_share_file">Cryptomator necesita acceso al almacenamiento para compartir archivos</string>
|
||||
<string name="permission_revoked_re_request_permission">Cryptomator ha perdido permiso para acceder a esta ubicación. Seleccione esta carpeta de nuevo para restaurar el permiso.</string>
|
||||
<string name="snack_bar_action_title_settings">Configuración</string>
|
||||
<string name="snack_bar_action_title_search">Buscar</string>
|
||||
@ -56,16 +56,16 @@
|
||||
<!-- # screens -->
|
||||
<!-- # screen: vault list -->
|
||||
<string name="screen_vault_list_actions_title">Añadir a Cryptomator</string>
|
||||
<string name="screen_vault_list_action_create_new_vault">Crear nueva caja fuerte</string>
|
||||
<string name="screen_vault_list_action_add_existing_vault">Añadir caja fuerte existente</string>
|
||||
<string name="screen_vault_list_action_create_new_vault">Crear bóveda nueva</string>
|
||||
<string name="screen_vault_list_action_add_existing_vault">Añadir bóveda existente</string>
|
||||
<string name="screen_vault_list_vault_action_delete">Eliminar</string>
|
||||
<string name="screen_vault_list_vault_creation_hint">Haz clic aquí para crear una caja fuerte</string>
|
||||
<string name="screen_vault_list_vault_creation_hint">Haga clic aquí para crear una bóveda</string>
|
||||
<string name="screen_vault_list_change_password_successful">Contraseña cambiada con éxito</string>
|
||||
<!-- # screen: file browser -->
|
||||
<string name="screen_file_browser_default_title">Caja fuerte</string>
|
||||
<string name="screen_file_browser_default_title">Bóveda</string>
|
||||
<string name="screen_file_browser_add_existing_vault_extra_text">Seleccionar archivo masterkey</string>
|
||||
<string name="screen_file_browser_create_new_vault_button_text">Dejar aquí</string>
|
||||
<string name="screen_file_browser_create_new_vault_extra_text">Nombre de caja fuerte: %1$s</string>
|
||||
<string name="screen_file_browser_create_new_vault_extra_text">Nombre de la bóveda: %1$s</string>
|
||||
<plurals name="screen_file_browser_subtitle_move">
|
||||
<item quantity="one">Mover %1$s a</item>
|
||||
<item quantity="other">Mover %2$d elementos a</item>
|
||||
@ -106,7 +106,7 @@
|
||||
<string name="screen_share_files_content_text">texto</string>
|
||||
<string name="screen_share_files_content_file">archivo</string>
|
||||
<string name="screen_share_files_content_files">archivos</string>
|
||||
<string name="screen_share_files_msg_filenames_must_be_unique">Los nombres de archivo deben ser únicos. Renombra los duplicados.</string>
|
||||
<string name="screen_share_files_msg_filenames_must_be_unique">Los nombres de archivo deben ser únicos. Renombre los duplicados.</string>
|
||||
<string name="screen_share_files_section_location">Guardar ubicación</string>
|
||||
<string name="screen_share_files_save_button_text">Guardar</string>
|
||||
<string name="screen_share_files_msg_success">Cifrado completado</string>
|
||||
@ -114,7 +114,7 @@
|
||||
<string name="screen_choose_cloud_service_title">Servicio de nube</string>
|
||||
<!-- ## screen: cloud connections -->
|
||||
<string name="screen_cloud_connections_title">Elegir ubicación</string>
|
||||
<string name="screen_cloud_connections_no_connections">Haz clic aquí para añadir ubicaciones</string>
|
||||
<string name="screen_cloud_connections_no_connections">Haga clic aquí para añadir ubicaciones</string>
|
||||
<string name="screen_cloud_error_webdav_not_supported">El servidor no parece ser compatible con WebDAV</string>
|
||||
<string name="screen_cloud_local_error_no_content_provider">No hay ubicaciones extra disponibles.</string>
|
||||
<!-- ## screen: webdav settings -->
|
||||
@ -124,7 +124,7 @@
|
||||
<string name="screen_webdav_settings_done_button_text">Conectar</string>
|
||||
<string name="screen_webdav_settings_msg_url_must_not_be_empty">La URL no puede estar vacía.</string>
|
||||
<string name="screen_webdav_settings_msg_url_is_invalid">La URL no es válida.</string>
|
||||
<string name="screen_webdav_settings_msg_username_must_not_be_empty">El nombre de usuario no puede estar vacio.</string>
|
||||
<string name="screen_webdav_settings_msg_username_must_not_be_empty">El nombre de usuario no puede estar vacío.</string>
|
||||
<string name="screen_webdav_settings_msg_password_must_not_be_empty">La contraseña no puede estar vacía.</string>
|
||||
<!-- ## screen: s3 settings -->
|
||||
<string name="screen_s3_settings_display_name_label">Nombre para mostrar</string>
|
||||
@ -139,15 +139,15 @@
|
||||
<string name="screen_s3_settings_msg_bucket_not_empty">La cubeta no puede estar vacía</string>
|
||||
<string name="screen_s3_settings_msg_endpoint_and_region_not_empty">El punto final o la región no puede estar vacío</string>
|
||||
<!-- ## screen: enter vault name -->
|
||||
<string name="screen_enter_vault_name_msg_name_empty">El nombre de la caja fuerte no puede estar vacío.</string>
|
||||
<string name="screen_enter_vault_name_vault_label">Nombre de la caja fuerte</string>
|
||||
<string name="screen_enter_vault_name_msg_name_empty">El nombre de la bóveda no puede estar vacío.</string>
|
||||
<string name="screen_enter_vault_name_vault_label">Nombre de la bóveda</string>
|
||||
<string name="screen_enter_vault_name_button_text">Crear</string>
|
||||
<!-- ## screen: set password -->
|
||||
<string name="screen_set_password_title">Establecer contraseña</string>
|
||||
<string name="screen_set_password_msg_password_mismatch">Las contraseñas no coinciden.</string>
|
||||
<string name="screen_set_password_button_text">Completado</string>
|
||||
<string name="screen_set_password_hint">IMPORTANTE: si olvida su contraseña no habrá manera de recuperar los datos.</string>
|
||||
<string name="screen_set_password_retype_password_label">Reescriba la contraseña</string>
|
||||
<string name="screen_set_password_retype_password_label">Repita la contraseña</string>
|
||||
<string name="screen_set_password_strength_indicator_0">Demasiado débil para crear una bóveda</string>
|
||||
<string name="screen_set_password_strength_indicator_1">Débil</string>
|
||||
<string name="screen_set_password_strength_indicator_2">Aceptable</string>
|
||||
@ -181,15 +181,15 @@
|
||||
<string name="screen_settings_section_auto_photo_upload_including_videos">Cargar videos</string>
|
||||
<string name="screen_settings_auto_photo_upload_title">Guardar archivos de carga automática en…</string>
|
||||
<string name="screen_settings_website_label">Web de Cryptomator</string>
|
||||
<string name="screen_settings_twitter_label">Síguenos en Twitter</string>
|
||||
<string name="screen_settings_facebook_label">Danos me gusta en Facebook</string>
|
||||
<string name="screen_settings_twitter_label">Síganos en Twitter</string>
|
||||
<string name="screen_settings_facebook_label">Denos me gusta en Facebook</string>
|
||||
<string name="screen_settings_section_legal">Legal</string>
|
||||
<string name="screen_settings_licenses_label">Licencias</string>
|
||||
<string name="screen_settings_license_terms_label">Términos de la licencia</string>
|
||||
<string name="screen_settings_section_support">Soporte</string>
|
||||
<string name="screen_settings_contact_label">Solicitar ayuda</string>
|
||||
<string name="screen_settings_debug_mode_label">Modo de depuración</string>
|
||||
<string name="screen_settings_error_report_label">Enviar archivo de trazas</string>
|
||||
<string name="screen_settings_error_report_label">Enviar archivo de registro</string>
|
||||
<string name="screen_settings_error_report_failed">Error en el envío</string>
|
||||
<string name="screen_settings_security_label">Consejos de seguridad</string>
|
||||
<string name="screen_settings_section_version">Versión</string>
|
||||
@ -199,6 +199,7 @@
|
||||
<string name="screen_settings_keep_unlocked_while_editing_files">Mantener desbloqueado</string>
|
||||
<string name="screen_settings_keep_unlocked_while_editing_files_summary">Mantener las bóvedas desbloqueadas durante la edición de archivos</string>
|
||||
<!-- ## screen: cloud settings -->
|
||||
<string name="screen_cloud_settings_onedrive_connections">Conexiones de OneDrive</string>
|
||||
<string name="screen_cloud_settings_webdav_connections">Conexiones de WebDAV</string>
|
||||
<string name="screen_cloud_settings_pcloud_connections">Conexiones de pCloud</string>
|
||||
<string name="screen_cloud_settings_s3_connections">Conexiones S3</string>
|
||||
@ -213,12 +214,12 @@
|
||||
<!-- # dialogs -->
|
||||
<string name="dialog_button_cancel">Cancelar</string>
|
||||
<string name="dialog_enter_password_positive_button">Desbloquear</string>
|
||||
<string name="dialog_change_password_old_password_label">Antigua contraseña</string>
|
||||
<string name="dialog_change_password_new_password_label">Nueva contraseña</string>
|
||||
<string name="dialog_change_password_old_password_label">Contraseña anterior</string>
|
||||
<string name="dialog_change_password_new_password_label">Contraseña nueva</string>
|
||||
<string name="dialog_change_password">Cambiar contraseña</string>
|
||||
<string name="dialog_change_password_msg_old_password_empty">La antigua contraseña no puede estar vacía.</string>
|
||||
<string name="dialog_change_password_msg_new_password_empty">La nueva contraseña no puede estar vacía.</string>
|
||||
<string name="dialog_change_password_msg_password_mismatch">Las nuevas contraseñas no coinciden.</string>
|
||||
<string name="dialog_change_password_msg_old_password_empty">La contraseña anterior no puede estar vacía.</string>
|
||||
<string name="dialog_change_password_msg_new_password_empty">La contraseña nueva no puede estar vacía.</string>
|
||||
<string name="dialog_change_password_msg_password_mismatch">Las contraseñas nuevas no coinciden.</string>
|
||||
<!-- Vault not found -->
|
||||
<string name="dialog_vault_not_found_title">Bóveda %1$s no encontrada</string>
|
||||
<string name="dialog_vault_not_found_message">La bóveda ha sido renombrada, movida o eliminada. Eliminar esta bóveda de la lista y añadirla de nuevo para continuar. ¿Eliminar ahora?</string>
|
||||
@ -230,53 +231,53 @@
|
||||
<string name="dialog_replace_positive_button_all_files_exist">Reemplazar todos</string>
|
||||
<string name="dialog_replace_positive_button_some_files_exist">Reemplazar existentes</string>
|
||||
<string name="dialog_replace_positive_button_single_file_exists">Reemplazar</string>
|
||||
<string name="dialog_replace_msg_single_file_exists">Ya existe un archivo llamado %1$s. ¿Quieres reemplazarlo?</string>
|
||||
<string name="dialog_replace_msg_all_files_exists">Todos los archivos existen ya. ¿Quieres reemplazarlos?</string>
|
||||
<string name="dialog_replace_msg_some_files_exists">%1$d archivos existen ya. ¿Quieres reemplazarlos?</string>
|
||||
<string name="dialog_replace_title_single_file_exists">\"¿Reemplazar archivo?</string>
|
||||
<string name="dialog_replace_title_multiple_files_exist">\"¿Reemplazar archivos?</string>
|
||||
<string name="dialog_replace_msg_single_file_exists">Ya existe un archivo llamado %1$s. ¿Desea reemplazarlo?</string>
|
||||
<string name="dialog_replace_msg_all_files_exists">Todos los archivos ya existen. ¿Desea reemplazarlos?</string>
|
||||
<string name="dialog_replace_msg_some_files_exists">%1$d archivos ya existen. ¿Desea reemplazarlos?</string>
|
||||
<string name="dialog_replace_title_single_file_exists">¿Reemplazar archivo?</string>
|
||||
<string name="dialog_replace_title_multiple_files_exist">¿Reemplazar archivos?</string>
|
||||
<string name="dialog_unable_to_share_title">No se puede compartir archivos</string>
|
||||
<string name="dialog_unable_to_share_message">No ha configurado ninguna caja fuerte. Cree antes una nueva caja fuerte con la aplicación Cryptomator.</string>
|
||||
<string name="dialog_unable_to_share_message">No ha configurado ninguna bóveda. Cree primero una bóveda nueva con la aplicación Cryptomator.</string>
|
||||
<string name="dialog_unable_to_share_positive_button">Aceptar</string>
|
||||
<string name="dialog_unable_to_share_negative_button">Crear caja fuerte</string>
|
||||
<string name="dialog_unable_to_share_negative_button">Crear bóveda</string>
|
||||
<string name="dialog_filetype_not_supported_title">No se puede abrir %1$s</string>
|
||||
<string name="dialog_filetype_not_supported_message">Descarga una aplicación que pueda abrir el archivo o, ¿quieres guardarlo en el dispositivo?</string>
|
||||
<string name="dialog_rename_vault_title">Renombrar caja fuerte</string>
|
||||
<string name="dialog_filetype_not_supported_message">Descargue una aplicación que pueda abrir el archivo o ¿desea guardarlo en el dispositivo?</string>
|
||||
<string name="dialog_rename_vault_title">Renombrar bóveda</string>
|
||||
<string name="dialog_rename_node_folder_title">Renombrar carpeta</string>
|
||||
<string name="dialog_rename_node_file_title">Renombrar archivo</string>
|
||||
<string name="dialog_unsaved_changes_title">Tienes cambios sin guardar</string>
|
||||
<string name="dialog_unsaved_changes_message">\"¿De verdad quieres salir sin guardar?</string>
|
||||
<string name="dialog_unsaved_changes_title">Tiene cambios sin guardar</string>
|
||||
<string name="dialog_unsaved_changes_message">¿Realmente desea salir sin guardar?</string>
|
||||
<string name="dialog_unsaved_changes_discard">Descartar</string>
|
||||
<string name="dialog_file_name_placeholder">texto.txt</string>
|
||||
<string name="dialog_delete_vault_message">\"¿Estás seguro de que quieres eliminar esta caja fuerte?</string>
|
||||
<string name="dialog_delete_vault_hint">Esta acción solo eliminará la caja fuerte de esta lista y no la borrará físicamente.</string>
|
||||
<string name="dialog_upload_file_title">Sube…</string>
|
||||
<string name="dialog_delete_vault_message">¿Está seguro que desea eliminar esta bóveda?</string>
|
||||
<string name="dialog_delete_vault_hint">Esta acción solo borrará la bóveda de esta lista y no la eliminará físicamente.</string>
|
||||
<string name="dialog_upload_file_title">Subiendo…</string>
|
||||
<string name="dialog_upload_file_remaining">Archivo %1$d de %2$d</string>
|
||||
<string name="dialog_export_file_title">Exportando (%1$d/%2$d)</string>
|
||||
<string name="dialog_progress_please_wait">Espera, por favor…</string>
|
||||
<string name="dialog_progress_please_wait">Por favor espere…</string>
|
||||
<string name="dialog_progress_creating_folder">Creando carpeta…</string>
|
||||
<string name="dialog_progress_creating_text_file">Creando archivo de texto…</string>
|
||||
<string name="action_progress_authentication">Autenticación…</string>
|
||||
<string name="action_progress_renaming">Renombrando…</string>
|
||||
<string name="action_progress_deleting">Borrando…</string>
|
||||
<string name="dialog_progress_unlocking_vault">Desbloqueando caja fuerte…</string>
|
||||
<string name="action_progress_deleting">Eliminando…</string>
|
||||
<string name="dialog_progress_unlocking_vault">Desbloqueando bóveda…</string>
|
||||
<string name="dialog_progress_change_password">Cambiando contraseña…</string>
|
||||
<string name="dialog_progress_creating_vault">Creando caja fuerte…</string>
|
||||
<string name="dialog_progress_creating_vault">Creando bóveda…</string>
|
||||
<string name="dialog_progress_upload_file">Subiendo…</string>
|
||||
<string name="dialog_progress_download_file">Descargando…</string>
|
||||
<string name="dialog_progress_encryption">Cifrando…</string>
|
||||
<string name="dialog_progress_decryption">Descifrando…</string>
|
||||
<string name="action_progress_moving">Moviendo…</string>
|
||||
<string name="dialog_lock_vault">Bloquear</string>
|
||||
<string name="dialog_accept_ssl_certificate_title">Invalidar certificado SSL</string>
|
||||
<string name="dialog_accept_ssl_certificate_hint">El certificado SSL no es válido. ¿Quiere confiar en él de todas formas?</string>
|
||||
<string name="dialog_accept_ssl_certificate_title">Certificado SSL inválido</string>
|
||||
<string name="dialog_accept_ssl_certificate_hint">El certificado SSL es inválido. ¿Desea confiar en él de todas formas?</string>
|
||||
<string name="dialog_accept_ssl_certificate_details">Detalles</string>
|
||||
<string name="dialog_accept_ssl_certificate_security_checkbox">Esto podría ser un riesgo para la seguridad. Sé lo que estoy haciendo.</string>
|
||||
<string name="dialog_http_security_hint">El uso de HTTP no es seguro. Recomendamos usar HTTPS en su lugar. Si conoce los riesgos puede seguir usando HTTP.</string>
|
||||
<string name="dialog_http_security_checkbox">Cambiar a HTTPS</string>
|
||||
<string name="dialog_http_security_title">\"¿Usar HTPPS?</string>
|
||||
<string name="dialog_no_screen_lock_hint">No se ha establecido el bloqueo de pantalla. Para almacenar las credenciales de forma segura, establece con Aceptar un patrón o contraseña.</string>
|
||||
<string name="dialog_no_screen_lock_title">\"¿Establecer bloqueo de pantalla?</string>
|
||||
<string name="dialog_no_screen_lock_hint">No se ha establecido el bloqueo de pantalla. Para almacenar las credenciales de forma segura, defina con OK un patrón o contraseña.</string>
|
||||
<string name="dialog_no_screen_lock_title">¿Establecer bloqueo de pantalla?</string>
|
||||
<string name="dialog_no_screen_lock_checkbox">Establecer bloqueo de pantalla</string>
|
||||
<string name="dialog_no_biometric_auth_set_up_title">No hay autenticación básica configurada en el sistema</string>
|
||||
<string name="dialog_no_biometric_auth_set_up_message">Registre al menos un dedo/rostro para usar este servicio.</string>
|
||||
@ -294,12 +295,12 @@
|
||||
<string name="dialog_vault_is_root_folder_of_cloud_title">La bóveda es la carpeta raíz de la conexión a la nube</string>
|
||||
<string name="dialog_vault_is_root_folder_of_cloud_hint">Crear una conexión nueva en la nube donde seleccione al menos la carpeta padre de esta carpeta de bóveda como directorio raíz para añadir esta bóveda.</string>
|
||||
<string name="dialog_disable_secure_screen_disclaimer_hint">Esta opción es una función de seguridad y evita que otras aplicaciones engañen a los usuarios para que hagan cosas que no quieren hacer.\n\nAl desactivar, confirma que es <a href="https://docs.cryptomator.org/en/1.5/android/settings/#screen-security">consciente de los riesgos</a>.</string>
|
||||
<string name="dialog_delete_cloud_connection_with_vaults_message">\"¿Estás seguro de que quieres eliminar esta conexión de nube?</string>
|
||||
<string name="dialog_delete_cloud_connection_with_vaults_hint">Esta acción eliminará la conexión de nube y todas las cajas fuertes de esta nube.</string>
|
||||
<string name="dialog_delete_cloud_connection_with_vaults_message">\"¿Está seguro que desea eliminar esta conexión a la nube?</string>
|
||||
<string name="dialog_delete_cloud_connection_with_vaults_hint">Esta acción eliminará la conexión a la nube y todas las bóvedas de esta nube.</string>
|
||||
<string name="dialog_confirm_delete_multiple_title">¿Eliminar %1$d elementos?</string>
|
||||
<string name="dialog_confirm_delete_multiple_message">¿Está seguro que desea eliminar estos elementos?</string>
|
||||
<string name="dialog_confirm_delete_file_message">\"¿Estás seguro de que quieres borrar este archivo?</string>
|
||||
<string name="dialog_confirm_delete_folder_message">Esto borrará todo el contenido de la carpeta. ¿Estás seguro de que quiere borrar esta carpeta?</string>
|
||||
<string name="dialog_confirm_delete_file_message">¿Está seguro que desea eliminar este archivo?</string>
|
||||
<string name="dialog_confirm_delete_folder_message">Esto borrará todo el contenido de la carpeta. ¿Está seguro que desea eliminar esta carpeta?</string>
|
||||
<string name="dialog_biometric_auth_key_invalidated_title">Función de autenticación biométrica desactivada</string>
|
||||
<string name="dialog_biometric_auth_key_invalidated_message">Debido a que la clave ha sido invalidada, la función de autenticación biométrica ha sido desactivada. Para reactivarla, abra los ajustes de Cryptomator.</string>
|
||||
<string name="dialog_enter_license_title">Proporcione una licencia válida</string>
|
||||
@ -356,8 +357,8 @@
|
||||
<string name="dialog_biometric_auth_use_password">Usar contraseña de la bóveda</string>
|
||||
<string name="dialog_unable_to_auto_upload_files_title">No se pueden cargar archivos automáticamente</string>
|
||||
<!-- notification -->
|
||||
<string name="notification_unlocked">Cajas fuertes desbloqueadas: %1$d</string>
|
||||
<string name="notification_timeout">Autobloqueo en %1$s</string>
|
||||
<string name="notification_unlocked">Bóvedas desbloqueadas: %1$d</string>
|
||||
<string name="notification_timeout">Autobloquear en %1$s</string>
|
||||
<string name="notification_lock_all">Bloquear todas</string>
|
||||
<string name="notification_cancel_auto_upload">Cancelar carga</string>
|
||||
<string name="notification_auto_upload_title">Carga automática de fotos en ejecución</string>
|
||||
@ -372,13 +373,14 @@
|
||||
<string name="notification_open_writable_file_title">Abrir archivo escribible</string>
|
||||
<string name="notification_open_writable_file_message">La bóveda permanece desbloqueada hasta finalizar la edición</string>
|
||||
<string name="notification_update_check_finished_latest">Última versión instalada</string>
|
||||
<string name="notification_authenticating">Autenticando…</string>
|
||||
<string name="screen_settings_lru_cache">Caché</string>
|
||||
<string name="screen_settings_lru_cache_toggle_summary">Almacene en caché los archivos a los que se accedió recientemente cifrados localmente en el dispositivo para su posterior reutilización cuando se vuelva a abrir</string>
|
||||
<string name="screen_settings_lru_cache_size">Tamaño total de la caché</string>
|
||||
<string name="screen_settings_clear_lru_cache_size">Vaciar la caché</string>
|
||||
<string name="screen_settings_lru_cache_changed__restart_toast">Los cambios se aplicarán en el próximo reinicio de la aplicación</string>
|
||||
<string name="screen_settings_license">Registrado para</string>
|
||||
<string name="screen_settings_license_mail">\"%1$s</string>
|
||||
<string name="screen_settings_license_mail">%1$s</string>
|
||||
<string name="dialog_settings_update_check_interval_title">Actualizar intervalo de verificación</string>
|
||||
<string name="screen_settings_check_updates">Comprobar atualizaciones</string>
|
||||
<string name="screen_settings_last_check_updates">Última ejecución %1$s</string>
|
||||
|
@ -199,6 +199,7 @@
|
||||
<string name="screen_settings_keep_unlocked_while_editing_files">Maintenir deverouillé</string>
|
||||
<string name="screen_settings_keep_unlocked_while_editing_files_summary">Gardez les coffres forts déverrouillées pendant l\'édition des fichiers</string>
|
||||
<!-- ## screen: cloud settings -->
|
||||
<string name="screen_cloud_settings_onedrive_connections">Connexions OneDrive</string>
|
||||
<string name="screen_cloud_settings_webdav_connections">Connexions WebDAV</string>
|
||||
<string name="screen_cloud_settings_pcloud_connections">Connexions pCloud</string>
|
||||
<string name="screen_cloud_settings_s3_connections">Connexions S3</string>
|
||||
@ -372,6 +373,7 @@
|
||||
<string name="notification_open_writable_file_title">Ouvrir un fichier accessible en écriture</string>
|
||||
<string name="notification_open_writable_file_message">Le coffre-fort reste déverrouillé jusqu\'à la fin des modifications</string>
|
||||
<string name="notification_update_check_finished_latest">Dernière version installée</string>
|
||||
<string name="notification_authenticating">Authentification en cours…</string>
|
||||
<string name="screen_settings_lru_cache">Cache</string>
|
||||
<string name="screen_settings_lru_cache_toggle_summary">Mettre en cache les fichiers récemment consultés chiffrés localement sur l\'appareil pour une réutilisation lors d\'une réouverture ultérieure</string>
|
||||
<string name="screen_settings_lru_cache_size">Taille totale du cache</string>
|
||||
|
@ -10,7 +10,10 @@
|
||||
<string name="error_invalid_passphrase">गलत कूटशब्द!</string>
|
||||
<string name="error_file_or_folder_exists">दस्तावेज आथ्वा फ़ोल्डर उपलब्ध नहीं है</string>
|
||||
<string name="error_no_such_file">दस्तावेज उपलब्ध नहीं है</string>
|
||||
<string name="error_activity_not_found">कृपया एक ऐप डाउनलोड करें जो इस फाइल को खोल सकता है।</string>
|
||||
<string name="error_server_not_found">सर्वर नहीं मिला।</string>
|
||||
<string name="error_names_contains_invalid_characters">फ़ाइल नामों में विशेष वर्ण नहीं हो सकते हैं।</string>
|
||||
<string name="error_vault_name_contains_invalid_characters">गुप्त तिजोरी के नाम में विशेष वर्ण नहीं हो सकते हैं।</string>
|
||||
<!-- # clouds -->
|
||||
<!-- ## cloud names -->
|
||||
<!-- # permission -->
|
||||
@ -19,14 +22,19 @@
|
||||
<string name="snack_bar_action_title_search">खोज करें</string>
|
||||
<string name="snack_bar_action_title_search_previous">पिछला</string>
|
||||
<string name="snack_bar_action_title_search_next">अगला</string>
|
||||
<string name="snack_bar_action_title_sort_newest">सबसे नया पहले</string>
|
||||
<string name="snack_bar_action_title_sort_oldest">सबसे पुराना पहले</string>
|
||||
<!-- # screens -->
|
||||
<!-- # screen: vault list -->
|
||||
<string name="screen_vault_list_actions_title">क्रिप्टोमेटर में जोड़ें</string>
|
||||
<string name="screen_vault_list_vault_action_delete">हटाएँ</string>
|
||||
<!-- # screen: file browser -->
|
||||
<string name="screen_file_browser_default_title">गुप्त तिजोरी</string>
|
||||
<string name="screen_file_browser_move_button_text">मूव करें</string>
|
||||
<string name="screen_file_browser_share_button_text">चुनें</string>
|
||||
<string name="screen_file_browser_action_create_folder">फोल्डर बनाएं</string>
|
||||
<string name="screen_file_browser_action_create_new_text_file">नया दस्तावेज बनाए</string>
|
||||
<string name="screen_file_browser_action_upload_files">फ़ाइल अपलोड करें</string>
|
||||
<string name="screen_file_browser_upload_files_chooser_title">दस्तावेज</string>
|
||||
<string name="screen_file_browser_msg_file_exported">दस्तावेज निर्यात हुआ</string>
|
||||
<string name="screen_file_browser_msg_files_exported">दस्तावेज निर्यात हुए</string>
|
||||
@ -62,6 +70,8 @@
|
||||
<!-- ## screen: s3 settings -->
|
||||
<string name="screen_s3_settings_region_label">क्षेत्र</string>
|
||||
<!-- ## screen: enter vault name -->
|
||||
<string name="screen_enter_vault_name_vault_label">गुप्त तिजोरी का नाम</string>
|
||||
<string name="screen_enter_vault_name_button_text">बनाएं</string>
|
||||
<!-- ## screen: set password -->
|
||||
<string name="screen_set_password_button_text">हो गया</string>
|
||||
<string name="screen_set_password_strength_indicator_1">कमजोर</string>
|
||||
@ -79,6 +89,7 @@
|
||||
<string name="dialog_enter_password_positive_button">अनलॉक करें</string>
|
||||
<!-- Vault not found -->
|
||||
<string name="dialog_vault_not_found_positive_button_text">हटाएँ</string>
|
||||
<string name="dialog_unable_to_share_negative_button">गुप्त तिजोरी बनाएं</string>
|
||||
<string name="dialog_lock_vault">लॉक करें</string>
|
||||
<string name="dialog_app_is_obscured_info_neutral_button">बंद करें</string>
|
||||
<string name="dialog_sym_link_back_button">पीछे जाएं</string>
|
||||
|
@ -4,15 +4,15 @@
|
||||
<string name="share_with_label">Enkripsi</string>
|
||||
<!-- # error messages -->
|
||||
<string name="error_generic">Kesalahan terjadi</string>
|
||||
<string name="error_authentication_failed">Otentikasi gagal</string>
|
||||
<string name="error_authentication_failed_re_authenticate">Otentikasi gagal, mohon masuk menggunakan %1$s</string>
|
||||
<string name="error_authentication_failed">Autentikasi gagal</string>
|
||||
<string name="error_authentication_failed_re_authenticate">Autentikasi gagal, mohon masuk menggunakan %1$s</string>
|
||||
<string name="error_no_network_connection">Tidak ada koneksi jaringan</string>
|
||||
<string name="error_invalid_passphrase">Kata sandi salah</string>
|
||||
<string name="error_file_or_folder_exists">Berkas atau folder sudah ada.</string>
|
||||
<string name="error_vault_version_not_supported">Vault tidak didukung. Vault ini telah dibuat dengan Cryptomator versi lain.</string>
|
||||
<string name="error_vault_already_exists">Vault sudah ada.</string>
|
||||
<string name="error_no_such_file">Berkas tidak ada.</string>
|
||||
<string name="error_vault_has_been_locked">Vaukt telah terkunci.</string>
|
||||
<string name="error_vault_has_been_locked">Vault telah dikunci.</string>
|
||||
<string name="error_cloud_already_exists">Cloud sudah ada.</string>
|
||||
<string name="error_activity_not_found">Silakan unduh aplikasi yang dapat membuka file ini.</string>
|
||||
<string name="error_server_not_found">Server tidak ditemukan.</string>
|
||||
@ -26,11 +26,11 @@
|
||||
<string name="error_update_no_internet">Pemeriksaan pembaruan gagal. Tidak ada koneksi internet.</string>
|
||||
<string name="error_failed_to_decrypt_webdav_password">Gagal mendekripsi kata sandi WebDAV, harap tambahkan kembali di pengaturan</string>
|
||||
<string name="error_play_services_not_available">Google Play Services belum terpasang</string>
|
||||
<string name="error_biometric_auth_aborted">Otentikasi biometrik gagal</string>
|
||||
<string name="error_biometric_auth_aborted">Autentikasi biometrik dibatalkan</string>
|
||||
<string name="error_vault_version_mismatch">Versi yang ditentukan di %1$s berbeda dengan %2$s</string>
|
||||
<string name="error_vault_key_invalid">%1$s tidak cocok dengan %2$s ini</string>
|
||||
<string name="error_vault_config_loading">Terjadi kesalahan saat memuat konfigurasi vault</string>
|
||||
<string name="error_file_not_found_after_opening_using_3party">File lokal tidak ada lagi setelah beralih kembali ke Cryptomator. Kemungkinan perubahan tidak dapat disebarkan kembali ke cloud.</string>
|
||||
<string name="error_file_not_found_after_opening_using_3party">File lokal sudah tidak tersedia setelah beralih kembali ke Cryptomator. Perubahan yang ada tidak dapat disimpan kembali ke cloud.</string>
|
||||
<string name="error_no_such_bucket">Bucket tidak ada</string>
|
||||
<string name="error_masterkey_location_not_supported">Lokasi Masterkey khusus belum didukung</string>
|
||||
<!-- # clouds -->
|
||||
@ -57,9 +57,9 @@
|
||||
<!-- # screen: vault list -->
|
||||
<string name="screen_vault_list_actions_title">Tambah ke Cryptomator</string>
|
||||
<string name="screen_vault_list_action_create_new_vault">Buat vault baru</string>
|
||||
<string name="screen_vault_list_action_add_existing_vault">Tambahkan vault yang ada</string>
|
||||
<string name="screen_vault_list_action_add_existing_vault">Tambahkan vault yang sudah ada</string>
|
||||
<string name="screen_vault_list_vault_action_delete">Hapus</string>
|
||||
<string name="screen_vault_list_vault_creation_hint">Klik disini untuk membuat vault baru</string>
|
||||
<string name="screen_vault_list_vault_creation_hint">Ketuk disini untuk membuat vault baru</string>
|
||||
<string name="screen_vault_list_change_password_successful">Kasa Sandi berhasil diubah</string>
|
||||
<!-- # screen: file browser -->
|
||||
<string name="screen_file_browser_default_title">Vault</string>
|
||||
@ -70,7 +70,7 @@
|
||||
<item quantity="other">Pindah %2$d item ke</item>
|
||||
</plurals>
|
||||
<string name="screen_file_browser_move_button_text">Pindah</string>
|
||||
<string name="screen_file_browser_msg_empty_folder">Filder kosong</string>
|
||||
<string name="screen_file_browser_msg_empty_folder">Folder kosong</string>
|
||||
<string name="screen_file_browser_file_info_label_date">dimodifikasi %1$s lalu</string>
|
||||
<string name="screen_file_browser_share_intent_chooser_title">Bagikan dengan</string>
|
||||
<string name="screen_file_browser_share_destination_title">Pilih tujuan</string>
|
||||
@ -113,7 +113,7 @@
|
||||
<string name="screen_choose_cloud_service_title">Layanan Cloud</string>
|
||||
<!-- ## screen: cloud connections -->
|
||||
<string name="screen_cloud_connections_title">Pilih lokasi</string>
|
||||
<string name="screen_cloud_connections_no_connections">Klik disini untuk menambahkan lokasi</string>
|
||||
<string name="screen_cloud_connections_no_connections">Ketuk disini untuk menambahkan lokasi</string>
|
||||
<string name="screen_cloud_error_webdav_not_supported">Server tampaknya tidak kompatibel dengan WebDAV</string>
|
||||
<string name="screen_cloud_local_error_no_content_provider">Tidak ada lokasi tambahan yang tersedia.</string>
|
||||
<!-- ## screen: webdav settings -->
|
||||
@ -155,11 +155,11 @@
|
||||
<!-- ## screen: settings -->
|
||||
<string name="screen_settings_section_general">Umum</string>
|
||||
<string name="screen_settings_cloud_settings_label">Layanan cloud</string>
|
||||
<string name="screen_settings_biometric_auth">Otentikasi biometrik</string>
|
||||
<string name="screen_settings_activate_biometric_auth">Aktifkan otentikas biometrik</string>
|
||||
<string name="screen_settings_biometric_auth">Autentikasi biometrik</string>
|
||||
<string name="screen_settings_activate_biometric_auth">Aktifkan autentikasi biometrik</string>
|
||||
<string name="screen_settings_confirm_face_unlock">Konfirmasi face unlock (jika tersedia)</string>
|
||||
<string name="screen_settings_block_app_when_obscured">Blokir aplikasi saat disamarkan</string>
|
||||
<string name="screen_settings_block_app_when_obscured_summary">Blokir mencegat input dan menampilkan antarmuka pengguna yang salah</string>
|
||||
<string name="screen_settings_block_app_when_obscured">Blokir aplikasi ketika dihalangi</string>
|
||||
<string name="screen_settings_block_app_when_obscured_summary">Memblokir pembajakan input serta tampilan antarmuka pengguna yang menyesatkan</string>
|
||||
<string name="screen_settings_secure_screen">Blokir screenshot</string>
|
||||
<string name="screen_settings_secure_screen_summary">Blokir screenshot di daftar terbaru dan di dalam aplikasi</string>
|
||||
<string name="screen_settings_section_search">Cari</string>
|
||||
@ -173,7 +173,7 @@
|
||||
<string name="screen_settings_section_auto_photo_upload">Unggah foto otomatis</string>
|
||||
<string name="screen_settings_section_auto_photo_upload_vault">Pilih vault untuk unggah</string>
|
||||
<string name="screen_settings_section_auto_photo_upload_toggle">Aktifkan</string>
|
||||
<string name="screen_settings_section_auto_photo_upload_toggle_summary">Ambil gambar di background dan setelah vault yang dipilih dibuka, mulai mengunggah</string>
|
||||
<string name="screen_settings_section_auto_photo_upload_toggle_summary">Ambil gambar di background, kemudian mulai proses unggah setelah vault terpilih dibuka</string>
|
||||
<string name="screen_settings_section_auto_photo_upload_toggle_instant_upload">Unggah instan</string>
|
||||
<string name="screen_settings_section_auto_photo_upload_toggle_instant_upload_summary">Unggah langsung jika vault tidak terkunci</string>
|
||||
<string name="screen_settings_section_auto_photo_upload_only_wifi_toggle">Hanya unggah melalui WIFI</string>
|
||||
@ -198,11 +198,12 @@
|
||||
<string name="screen_settings_keep_unlocked_while_editing_files">Biarkan tidak terkunci</string>
|
||||
<string name="screen_settings_keep_unlocked_while_editing_files_summary">Biarkan vault tidak terkunci saat mengedit file</string>
|
||||
<!-- ## screen: cloud settings -->
|
||||
<string name="screen_cloud_settings_onedrive_connections">Koneksi OneDrive</string>
|
||||
<string name="screen_cloud_settings_webdav_connections">Koneksi WebDAV</string>
|
||||
<string name="screen_cloud_settings_pcloud_connections">koneksi pCloud</string>
|
||||
<string name="screen_cloud_settings_s3_connections">Koneksi S3</string>
|
||||
<string name="screen_cloud_settings_local_storage_locations">Lokasi penyimpanan lokal</string>
|
||||
<string name="screen_cloud_settings_log_in_to">Masuk ke</string>
|
||||
<string name="screen_cloud_settings_log_in_to">Login ke</string>
|
||||
<string name="screen_cloud_settings_sign_out_from_cloud">Keluar dari</string>
|
||||
<!-- ## screen: licenses -->
|
||||
<!-- ## screen: authenticate cloud -->
|
||||
@ -211,7 +212,7 @@
|
||||
<!-- ## screen: insecure android version info -->
|
||||
<!-- # dialogs -->
|
||||
<string name="dialog_button_cancel">Batalkan</string>
|
||||
<string name="dialog_enter_password_positive_button">Buka Gembok</string>
|
||||
<string name="dialog_enter_password_positive_button">Buka Kunci</string>
|
||||
<string name="dialog_change_password_old_password_label">Kata sandi lama</string>
|
||||
<string name="dialog_change_password_new_password_label">Kata sandi baru</string>
|
||||
<string name="dialog_change_password">Ubah kata sandi</string>
|
||||
@ -235,7 +236,7 @@
|
||||
<string name="dialog_replace_title_single_file_exists">Menimpa file?</string>
|
||||
<string name="dialog_replace_title_multiple_files_exist">Menimpa file?</string>
|
||||
<string name="dialog_unable_to_share_title">Tidak dapat membagikan file</string>
|
||||
<string name="dialog_unable_to_share_message">Anda belum menyiapkan vault apa pun. Silakan buat vault baru dengan aplikasi Cryptomator terlebih dahulu.</string>
|
||||
<string name="dialog_unable_to_share_message">Anda belum menyiapkan vault sama sekali. Silahkan buat vault baru terlebih dahulu melalui aplikasi Cryptomator.</string>
|
||||
<string name="dialog_unable_to_share_positive_button">OK</string>
|
||||
<string name="dialog_unable_to_share_negative_button">Buat vault</string>
|
||||
<string name="dialog_filetype_not_supported_title">Tidak dapat membuka %1$s</string>
|
||||
@ -255,7 +256,7 @@
|
||||
<string name="dialog_progress_please_wait">Mohon tunggu…</string>
|
||||
<string name="dialog_progress_creating_folder">Membuat folder…</string>
|
||||
<string name="dialog_progress_creating_text_file">Membuat file teks…</string>
|
||||
<string name="action_progress_authentication">Otentikasi…</string>
|
||||
<string name="action_progress_authentication">Autentikasi…</string>
|
||||
<string name="action_progress_renaming">Mengganti nama…</string>
|
||||
<string name="action_progress_deleting">Menghapus…</string>
|
||||
<string name="dialog_progress_unlocking_vault">Membuka vault…</string>
|
||||
@ -266,7 +267,7 @@
|
||||
<string name="dialog_progress_encryption">Mengenkripsi…</string>
|
||||
<string name="dialog_progress_decryption">Mendekripsi…</string>
|
||||
<string name="action_progress_moving">Memnindahkan…</string>
|
||||
<string name="dialog_lock_vault">Gembok</string>
|
||||
<string name="dialog_lock_vault">Kunci</string>
|
||||
<string name="dialog_accept_ssl_certificate_title">Sertifikat SSL tidak valid</string>
|
||||
<string name="dialog_accept_ssl_certificate_hint">Sertifikat SSL tidak valid. Apakah Anda ingin mempercayainya?</string>
|
||||
<string name="dialog_accept_ssl_certificate_details">Rincian</string>
|
||||
@ -277,16 +278,16 @@
|
||||
<string name="dialog_no_screen_lock_hint">Tidak ada kunci layar yang disetel. Untuk menyimpan kredensial Anda dengan cara yang aman, atur dengan OK pola atau kata sandi.</string>
|
||||
<string name="dialog_no_screen_lock_title">Atur kunci layar?</string>
|
||||
<string name="dialog_no_screen_lock_checkbox">Atur kunci layar</string>
|
||||
<string name="dialog_no_biometric_auth_set_up_title">Tidak ada otentikasi dasar yang diatur dalam sistem</string>
|
||||
<string name="dialog_no_biometric_auth_set_up_title">Tidak ada autentikasi dasar yang disetel dalam sistem</string>
|
||||
<string name="dialog_no_biometric_auth_set_up_message">Daftarkan setidaknya satu jari/wajah untuk menggunakan layanan ini.</string>
|
||||
<string name="dialog_debug_mode_disclaimer_hint">Dalam mode ini, data sensitif dapat ditulis ke file log di perangkat Anda (misalnya, nama file dan jalur). Kata sandi, cookie, dll. secara eksplisit dikecualikan.\n\nIngat untuk menonaktifkan mode debug sesegera mungkin.</string>
|
||||
<string name="dialog_debug_mode_disclaimer_hint">Dalam mode ini, data sensitif dapat ditulis ke file log di perangkat Anda (misalnya, nama dan lokasi file). Kata sandi, cookie, dll secara eksplisit dikecualikan.\n\nJangan lupa untuk menonaktifkan mode debug sesegera mungkin.</string>
|
||||
<string name="dialog_debug_mode_disclaimer_title">Perhatian</string>
|
||||
<string name="dialog_debug_mode_positive_button">Aktif</string>
|
||||
<string name="dialog_disable_app_obscured_disclaimer_hint">Setelan ini adalah fitur keamanan dan mencegah aplikasi lain menipu pengguna agar melakukan hal-hal yang tidak ingin mereka lakukan.\n\nDengan menonaktifkan, Anda mengonfirmasi bahwa Anda <a href="https://docs.cryptomator.org/en/1.5/android/settings/#block-app-when-obscured">sadar akan risiko</a>.</string>
|
||||
<string name="dialog_debug_mode_positive_button">Aktifkan</string>
|
||||
<string name="dialog_disable_app_obscured_disclaimer_hint">Setelan ini adalah fitur keamanan yang dapat mencegah aplikasi lain menipu pengguna agar melakukan hal-hal yang tidak ingin mereka lakukan.\n\nDengan menonaktifkan, artinya mengonfirmasi bahwa Anda <a href="https://docs.cryptomator.org/en/1.5/android/settings/#block-app-when-obscured">paham dengan resikonya</a>.</string>
|
||||
<string name="dialog_disable_app_obscured_disclaimer_title">Perhatian</string>
|
||||
<string name="dialog_disable_app_obscured_positive_button">Matikan</string>
|
||||
<string name="dialog_app_is_obscured_info_title">Aplikasi tidak jelas</string>
|
||||
<string name="dialog_app_is_obscured_info_hint">Aplikasi lain menampilkan sesuatu di atas Cryptomator (misal, Filter cahaya biru atau aplikasi mode malam). Untuk alasan keamanan, Cryptomator dinonaktifkan.\n\n<a href="https://docs.cryptomator.org/en/1.5/android/settings/#block-app-when-obscured">Cara mengaktifkan Cryptomator</a></string>
|
||||
<string name="dialog_app_is_obscured_info_title">Aplikasi terhalang</string>
|
||||
<string name="dialog_app_is_obscured_info_hint">Aplikasi lain menampilkan sesuatu di atas tampilan Cryptomator (misal, Filter cahaya biru atau aplikasi mode malam). Untuk alasan keamanan, Cryptomator dinonaktifkan.\n\n<a href="https://docs.cryptomator.org/en/1.5/android/settings/#block-app-when-obscured">Cara mengaktifkan Cryptomator</a></string>
|
||||
<string name="dialog_app_is_obscured_info_neutral_button">Tutup</string>
|
||||
<string name="dialog_vaults_removed_during_migration_title">Harap tambahkan kembali vault untuk cloud %1s</string>
|
||||
<string name="dialog_vaults_removed_during_migration_hint">Saat bermigrasi ke versi aplikasi ini, kita perlu menghapus vault berikut dari aplikasi:\n%2s \n\nVault tersebut tidak dihapus dari cloud, tetapi hanya dari aplikasi ini. Maaf atas ketidaknyamanan ini dan harap tambahkan kembali vault ini untuk terus bekerja dengannya.</string>
|
||||
@ -299,8 +300,8 @@
|
||||
<string name="dialog_confirm_delete_multiple_message">Anda yakin ingin menghapus item ini?</string>
|
||||
<string name="dialog_confirm_delete_file_message">Anda yakin ingin menghapus berkas ini?</string>
|
||||
<string name="dialog_confirm_delete_folder_message">Ini akan menghapus semua isi folder. Anda yakin ingin menghapus folder ini?</string>
|
||||
<string name="dialog_biometric_auth_key_invalidated_title">Fitur otentikasi biometrik dinonaktifkan</string>
|
||||
<string name="dialog_biometric_auth_key_invalidated_message">Karena kunci telah dibatalkan, fitur otentikasi biometrik telah dinonaktifkan. Untuk mengaktifkan kembali, buka pengaturan Cryptomator.</string>
|
||||
<string name="dialog_biometric_auth_key_invalidated_title">Fitur autentikasi biometrik dinonaktifkan</string>
|
||||
<string name="dialog_biometric_auth_key_invalidated_message">Karena kunci telah dibatalkan, fitur autentikasi biometrik telah dinonaktifkan. Untuk mengaktifkan kembali, buka pengaturan Cryptomator.</string>
|
||||
<string name="dialog_enter_license_title">Berikan lisensi yang valid</string>
|
||||
<string name="dialog_enter_license_content">Kami mendeteksi bahwa Anda menginstal Cryptomator tanpa menggunakan Google Play Store. Berikan lisensi yang valid, yang dapat dibeli di <a href="https://cryptomator.org/android/">https://cryptomator.org/android/</a></string>
|
||||
<string name="dialog_enter_license_not_valid_content">Lisensi yang diberikan tidak valid. Pastikan Anda memasukkannya dengan benar.</string>
|
||||
@ -322,7 +323,7 @@
|
||||
<string name="dialog_no_dir_file_message">Folder cloud \'%1$s\' tidak memiliki file direktori yang valid. Bisa jadi folder tersebut dibuat di perangkat lain dan belum sepenuhnya tersinkronisasi ke cloud. Harap periksa di cloud Anda jika file berikut ada dan tidak kosong:\n%2$s</string>
|
||||
<string name="dialog_no_more_images_to_display">Tidak ada lagi gambar untuk ditampilkan…</string>
|
||||
<string name="dialog_pcloud_credentials_updated_title">Kredensial \'%1$s\' diperbarui</string>
|
||||
<string name="dialog_pcloud_credentials_updated">Jika Anda ingin menambahkan akun pCloud baru, klik url ini <a href="https://www.pcloud.com">www.pcloud.com</a>, keluar dari akun saat ini dan klik lagi \'+\' di aplikasi ini untuk membuat koneksi cloud baru.</string>
|
||||
<string name="dialog_pcloud_credentials_updated">Jika Anda ingin menambahkan akun pCloud baru, ketuk url berikut <a href="https://www.pcloud.com">www.pcloud.com</a>, keluar dari akun saat ini kemudian ketuk tombol \'+\' lagi di aplikasi ini untuk membuat koneksi cloud baru.</string>
|
||||
<string name="permission_snackbar_auth_local_vault">Cryptomator membutuhkan akses penyimpanan untuk menggunakan vault lokal</string>
|
||||
<string name="permission_snackbar_auth_auto_upload">Cryptomator membutuhkan akses penyimpanan untuk menggunakan unggahan foto otomatis</string>
|
||||
<!-- # error reports -->
|
||||
@ -350,10 +351,10 @@
|
||||
<string name="time_unit_years_sg">tahun</string>
|
||||
<string name="time_unit_years_pl">tahun</string>
|
||||
<!-- ## biometric authentication -->
|
||||
<string name="dialog_biometric_auth_title">Masuk dengan biometrik</string>
|
||||
<string name="dialog_biometric_auth_title">Login dengan biometrik</string>
|
||||
<string name="dialog_biometric_auth_message">Buka kunci dengan kredensial biometrik</string>
|
||||
<string name="dialog_biometric_auth_use_password">Gunakan kata sandi vault</string>
|
||||
<string name="dialog_unable_to_auto_upload_files_title">Tidak dapat mengunggah file</string>
|
||||
<string name="dialog_unable_to_auto_upload_files_title">Tidak dapat mengunggah file secara otomatis</string>
|
||||
<!-- notification -->
|
||||
<string name="notification_unlocked">Vaults tidak terkunci: %1$d</string>
|
||||
<string name="notification_timeout">Otomatis terkunci dalam %1$s</string>
|
||||
@ -371,8 +372,9 @@
|
||||
<string name="notification_open_writable_file_title">Buka file yang dapat ditulis</string>
|
||||
<string name="notification_open_writable_file_message">Vault tetap tidak terkunci sampai selesai mengedit</string>
|
||||
<string name="notification_update_check_finished_latest">Versi terbaru sudah terpasang</string>
|
||||
<string name="notification_authenticating">Mengautentikasi…</string>
|
||||
<string name="screen_settings_lru_cache">Cache</string>
|
||||
<string name="screen_settings_lru_cache_toggle_summary">Cache file yang baru saja diakses yang dienkripsi secara lokal di perangkat untuk digunakan kembali nanti saat dibuka kembali</string>
|
||||
<string name="screen_settings_lru_cache_toggle_summary">Gunakan cache yang terenkripsi di perangkat lokal untuk file yang baru-baru ini diakses agar bisa digunakan lagi ketika dibuka kembali</string>
|
||||
<string name="screen_settings_lru_cache_size">Total ukuran cache</string>
|
||||
<string name="screen_settings_clear_lru_cache_size">Bersihkan Cache</string>
|
||||
<string name="screen_settings_lru_cache_changed__restart_toast">Perubahan akan diterapkan pada saat aplikasi dibuka kembali</string>
|
||||
|
@ -1,45 +1,362 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- # app -->
|
||||
<string name="share_with_label">הצפן</string>
|
||||
<!-- # error messages -->
|
||||
<string name="error_generic">ארעה שגיאה</string>
|
||||
<string name="error_authentication_failed">האימות נכשל</string>
|
||||
<string name="error_authentication_failed_re_authenticate">האימות נכשל, אנא התחבר באמצעות %1$s</string>
|
||||
<string name="error_no_network_connection">אין חיבור לרשת</string>
|
||||
<string name="error_invalid_passphrase">סיסמה שגויה</string>
|
||||
<string name="error_file_or_folder_exists">כבר קיימים תיקייה או קובץ בשם זה.</string>
|
||||
<string name="error_vault_version_not_supported">הכספת לא נתמכת. כספת זו נוצרה בגרסה אחרת של Cryptomator.</string>
|
||||
<string name="error_vault_already_exists">כספת כבר קיימת.</string>
|
||||
<string name="error_no_such_file">הקובץ לא קיים.</string>
|
||||
<string name="error_vault_has_been_locked">הכספת ננעלה.</string>
|
||||
<string name="error_cloud_already_exists">אחסון ענן זה כבר בשימוש.</string>
|
||||
<string name="error_activity_not_found">אנא הורד אפליקציה שתומכת בפתיחה של קובץ זה.</string>
|
||||
<string name="error_server_not_found">שרת לא נמצא.</string>
|
||||
<string name="error_device_policy_manager_not_found">אנא הפעל מסך נעילה בהגדרות המכשיר</string>
|
||||
<string name="error_export_illegal_file_name">הייצוא נכשל. מחק תווים מיוחדים משמות הקבצים ונסה שוב.</string>
|
||||
<string name="error_name_contains_invalid_characters">הקלט לא יכול להכיל תווים מיוחדים.</string>
|
||||
<string name="error_names_contains_invalid_characters">שם קובץ לא יכול להכיל תווים מיוחדים.</string>
|
||||
<string name="error_vault_name_contains_invalid_characters">שם הכספת לא יכול להכיל תווים מיוחדים.</string>
|
||||
<string name="error_general_update">עדכון גרסה נכשל. התרחשה שגיאה כללית.</string>
|
||||
<string name="error_hash_mismatch_update">עדכון גרסה נכשל. אין תאימות בין הקובץ שהועלה לערך ה hash שחושב</string>
|
||||
<string name="error_update_no_internet">עדכון גרסה נכשל. אין חיבור לאינטרנט.</string>
|
||||
<string name="error_failed_to_decrypt_webdav_password">פענוח סיסמת WebDAV נכשל, וודא בהגדרות האפליקצייה שהסיסמה שהוכנסה נכונה</string>
|
||||
<string name="error_play_services_not_available">שירותי Google Play אינם מותקנים</string>
|
||||
<string name="error_biometric_auth_aborted">אימות ביומטרי הופסק</string>
|
||||
<string name="error_vault_version_mismatch">הגרסה ב %1$s שונה מזו שב %2$s</string>
|
||||
<string name="error_vault_key_invalid">%1$s ו %2$s אינם תואמים</string>
|
||||
<string name="error_vault_config_loading">שגיאה כללית בזמן טעינת הגדרות הכספת</string>
|
||||
<string name="error_no_such_bucket">דלי לא נמצא</string>
|
||||
<string name="error_masterkey_location_not_supported">שינוי מיקום ה Masterkey עדיין לא נתמך</string>
|
||||
<!-- # clouds -->
|
||||
<!-- ## cloud names -->
|
||||
<string name="cloud_names_local_storage">אחסון מקומי</string>
|
||||
<!-- # permission -->
|
||||
<!-- ## permission messages -->
|
||||
<string name="permission_message_export_file">Cryptomator זקוק להרשאת גישה לזיכרון המכשיר כדי לייצא קבצים</string>
|
||||
<string name="permission_message_upload_file">Cryptomator זקוק להרשאת גישה לזיכרון המכשיר כדי להעלות קבצים</string>
|
||||
<string name="permission_message_share_file">Cryptomator זקוק להרשאת גישה לזיכרון המכשיר כדי לשתף קבצים</string>
|
||||
<string name="permission_revoked_re_request_permission">Cryptomator איבד את הרשאות הגישה לתיקייה זו. אנא בחר תיקייה זו בשנית כדי להחזיר את הרשאה.</string>
|
||||
<string name="snack_bar_action_title_settings">הגדרות</string>
|
||||
<string name="snack_bar_action_title_search">חיפוש</string>
|
||||
<string name="snack_bar_action_title_search_previous">הקודם</string>
|
||||
<string name="snack_bar_action_title_search_next">המשך</string>
|
||||
<string name="snack_bar_action_title_sort">מיון</string>
|
||||
<string name="snack_bar_action_title_sort_az">א - ת</string>
|
||||
<string name="snack_bar_action_title_sort_za">ת - א</string>
|
||||
<string name="snack_bar_action_title_sort_newest">החדש ביותר</string>
|
||||
<string name="snack_bar_action_title_sort_oldest">הישן ביותר</string>
|
||||
<string name="snack_bar_action_title_sort_biggest">הכבד ביותר</string>
|
||||
<string name="snack_bar_action_title_sort_smallest">הקל ביותר</string>
|
||||
<!-- # screens -->
|
||||
<!-- # screen: vault list -->
|
||||
<string name="screen_vault_list_actions_title">הוסף ל-Cryptomator</string>
|
||||
<string name="screen_vault_list_action_create_new_vault">צור כספת חדשה</string>
|
||||
<string name="screen_vault_list_action_add_existing_vault">הוסף כספת קיימת</string>
|
||||
<string name="screen_vault_list_vault_action_delete">מחק</string>
|
||||
<string name="screen_vault_list_vault_creation_hint">ליצירת כספת חדשה לחץ כאן</string>
|
||||
<string name="screen_vault_list_change_password_successful">הסיסמה שונתה בהצלחה</string>
|
||||
<!-- # screen: file browser -->
|
||||
<string name="screen_file_browser_default_title">כספת</string>
|
||||
<string name="screen_file_browser_add_existing_vault_extra_text">בחר קובץ masterkey</string>
|
||||
<string name="screen_file_browser_create_new_vault_button_text">מקם כאן</string>
|
||||
<string name="screen_file_browser_create_new_vault_extra_text">שם כספת: %1$s</string>
|
||||
<string name="screen_file_browser_move_button_text">שנה מיקום</string>
|
||||
<string name="screen_file_browser_msg_empty_folder">תיקיה ריקה</string>
|
||||
<string name="screen_file_browser_file_info_label_date">שונה לפני %1$s</string>
|
||||
<string name="screen_file_browser_share_intent_chooser_title">שתף באמצעות</string>
|
||||
<string name="screen_file_browser_share_destination_title">בחר מיקום חדש</string>
|
||||
<string name="screen_file_browser_share_button_text">בחר</string>
|
||||
<string name="screen_file_browser_nothing_to_share">אין מה לשתף</string>
|
||||
<string name="screen_file_browser_actions_title">הוסף ל %1$s</string>
|
||||
<string name="screen_file_browser_action_create_folder">צור תיקייה</string>
|
||||
<string name="screen_file_browser_action_create_new_text_file">צור קובץ טקסט</string>
|
||||
<string name="screen_file_browser_action_upload_files">העלה קובצים</string>
|
||||
<string name="screen_file_browser_upload_files_chooser_title">קבצים</string>
|
||||
<string name="screen_file_browser_msg_file_exported">הקובץ יוצא</string>
|
||||
<string name="screen_file_browser_msg_files_exported">הקבצים יוצאו</string>
|
||||
<string name="screen_file_browser_nothing_to_export">אין מה לייצא</string>
|
||||
<string name="screen_file_browser_msg_creating_download_dir_failed">יצירת תיקייה להורדות נכשלה</string>
|
||||
<string name="screen_file_browser_node_action_share">שתף</string>
|
||||
<string name="screen_file_browser_node_action_rename">שנה שם</string>
|
||||
<string name="screen_file_browser_node_action_edit_text">ערוך</string>
|
||||
<string name="screen_file_browser_node_action_export">ייצא</string>
|
||||
<string name="screen_file_browser_node_action_delete">מחק</string>
|
||||
<string name="screen_file_browser_node_action_open_with_text">פתח באמצעות…</string>
|
||||
<string name="screen_file_browser_selection_mode_title_zero_elements">בחר פריטים</string>
|
||||
<string name="screen_file_browser_selection_mode_title_one_or_more_elements">%1$d נבחר</string>
|
||||
<string name="screen_file_browser_select_items">בחר</string>
|
||||
<string name="screen_file_browser_select_all_items">בחר הכל</string>
|
||||
<string name="screen_file_browser_refresh_items">רענן</string>
|
||||
<string name="screen_file_browser_no_connection_message">אין חיבור לרשת</string>
|
||||
<string name="screen_file_browser_no_connection_button_text">נסה שוב</string>
|
||||
<!-- ## screen: text editor -->
|
||||
<string name="screen_text_editor_save_success">נשמר בהצלחה</string>
|
||||
<!-- ## screen: share files -->
|
||||
<string name="screen_share_files_title">שמור את %1$s…</string>
|
||||
<string name="screen_share_files_content_text">טקסט</string>
|
||||
<string name="screen_share_files_content_file">קובץ</string>
|
||||
<string name="screen_share_files_content_files">קבצים</string>
|
||||
<string name="screen_share_files_msg_filenames_must_be_unique">שם קובץ חייב להיות ייחודי. אנא שנה שמות קבצים בעלי שם זהה.</string>
|
||||
<string name="screen_share_files_section_location">מיקום שמירה</string>
|
||||
<string name="screen_share_files_save_button_text">שמור</string>
|
||||
<string name="screen_share_files_msg_success">הצפנה הושלמה</string>
|
||||
<!-- ## screen: choose cloud service -->
|
||||
<string name="screen_choose_cloud_service_title">שירותי אחסון בענן</string>
|
||||
<!-- ## screen: cloud connections -->
|
||||
<string name="screen_cloud_connections_title">בחר מיקום</string>
|
||||
<string name="screen_cloud_connections_no_connections">להוספת מיקומים, לחץ כאן</string>
|
||||
<string name="screen_cloud_error_webdav_not_supported">נראה שהשרת אינו תומך ב WebDAV</string>
|
||||
<!-- ## screen: webdav settings -->
|
||||
<string name="screen_webdav_settings_url_port_label">כתובת URL</string>
|
||||
<string name="screen_webdav_settings_username_label">שם משתמש</string>
|
||||
<string name="screen_webdav_settings_password_label">סיסמה</string>
|
||||
<string name="screen_webdav_settings_done_button_text">התחבר</string>
|
||||
<string name="screen_webdav_settings_msg_url_must_not_be_empty">יש להזין כתובת URL.</string>
|
||||
<string name="screen_webdav_settings_msg_url_is_invalid">כתובת ה-URL אינה חוקית.</string>
|
||||
<string name="screen_webdav_settings_msg_username_must_not_be_empty">יש להזין שם משתמש.</string>
|
||||
<string name="screen_webdav_settings_msg_password_must_not_be_empty">יש להזין סיסמה.</string>
|
||||
<!-- ## screen: s3 settings -->
|
||||
<string name="screen_s3_settings_display_name_label">שם תצוגה</string>
|
||||
<string name="screen_s3_settings_access_key_label">מפתח גישה</string>
|
||||
<string name="screen_s3_settings_secret_key_label">מפתח סודי</string>
|
||||
<string name="screen_s3_settings_bucket_label">דלי קיים</string>
|
||||
<string name="screen_s3_settings_endpoint_label">נקודת גישה</string>
|
||||
<string name="screen_s3_settings_region_label">אזור</string>
|
||||
<string name="screen_s3_settings_msg_display_name_not_empty">יש להזין שם תצוגה</string>
|
||||
<string name="screen_s3_settings_msg_access_key_not_empty">יש להזין מפתח גישה</string>
|
||||
<string name="screen_s3_settings_msg_secret_key_not_empty">יש להזין מפתח סודי</string>
|
||||
<string name="screen_s3_settings_msg_bucket_not_empty">יש להזין דלי</string>
|
||||
<string name="screen_s3_settings_msg_endpoint_and_region_not_empty">יש להזין מפתח גישה</string>
|
||||
<!-- ## screen: enter vault name -->
|
||||
<string name="screen_enter_vault_name_msg_name_empty">יש להזין שם כספת.</string>
|
||||
<string name="screen_enter_vault_name_vault_label">שם כספת</string>
|
||||
<string name="screen_enter_vault_name_button_text">צור</string>
|
||||
<!-- ## screen: set password -->
|
||||
<string name="screen_set_password_title">הגדר סיסמה</string>
|
||||
<string name="screen_set_password_msg_password_mismatch">סיסמת האימות אינה תואמת לסיסמה שהוזנה.</string>
|
||||
<string name="screen_set_password_button_text">סיום</string>
|
||||
<string name="screen_set_password_hint">שים לב שאינך שוכח את סיסמתך! לא ניתן לשחזר את המידע שלך בחזרה מבלי הסיסמה.</string>
|
||||
<string name="screen_set_password_retype_password_label">חזור על הסיסמה</string>
|
||||
<string name="screen_set_password_strength_indicator_0">סיסמה חלשה מידי ליצירת כספת</string>
|
||||
<string name="screen_set_password_strength_indicator_1">חלשה</string>
|
||||
<string name="screen_set_password_strength_indicator_2">סבירה</string>
|
||||
<string name="screen_set_password_strength_indicator_3">חזקה</string>
|
||||
<string name="screen_set_password_strength_indicator_4">חזקה מאוד</string>
|
||||
<!-- ## screen: settings -->
|
||||
<string name="screen_settings_section_general">כללי</string>
|
||||
<string name="screen_settings_cloud_settings_label">שירותי אחסון בענן</string>
|
||||
<string name="screen_settings_biometric_auth">אימות ביומטרי</string>
|
||||
<string name="screen_settings_activate_biometric_auth">הפעל אימות ביומטרי</string>
|
||||
<string name="screen_settings_confirm_face_unlock">אפשר פתיחה באמצעות זיהוי פנים (במידה שזמין)</string>
|
||||
<string name="screen_settings_secure_screen">חסום צילומי מסך</string>
|
||||
<string name="screen_settings_secure_screen_summary">חסום צילומי מסך באפליקציה וברשימת היישומים שנפתחו לאחרונה</string>
|
||||
<string name="screen_settings_section_search">חיפוש</string>
|
||||
<string name="screen_settings_glob_search">חפש באמצעות תבניות Glob</string>
|
||||
<string name="screen_settings_glob_search_summary">השתמש בתבניות Glob כדי לבצע חיפוש כדוגמת alice.*.jpg</string>
|
||||
<string name="screen_settings_section_auto_lock">נעילה אוטומטית</string>
|
||||
<string name="screen_settings_auto_lock_timeout">נעל לאחר</string>
|
||||
<string name="screen_settings_auto_lock_on_screen_lock">כאשר מסך המכשיר נכבה</string>
|
||||
<string name="screen_settings_section_auto_photo_upload">העלאה אוטומטית של תמונות</string>
|
||||
<string name="screen_settings_section_auto_photo_upload_vault">בחר כספת להעלאת תמונות</string>
|
||||
<string name="screen_settings_section_auto_photo_upload_toggle">הפעל</string>
|
||||
<string name="screen_settings_section_auto_photo_upload_toggle_instant_upload">העלאה מיידית</string>
|
||||
<string name="screen_settings_section_auto_photo_upload_only_wifi_toggle">העלה רק כאשר המכשיר מחובר ל WiFi</string>
|
||||
<string name="screen_settings_section_auto_photo_upload_including_videos">העלה סרטוני וידאו</string>
|
||||
<string name="screen_settings_auto_photo_upload_title">שמור קבצים מועלים אוטומטית ל…</string>
|
||||
<string name="screen_settings_website_label">לאתר Cryptomator</string>
|
||||
<string name="screen_settings_twitter_label">עקבו אחרינו ב-Twitter</string>
|
||||
<string name="screen_settings_facebook_label">תנו לנו לייק ב-Facebook</string>
|
||||
<string name="screen_settings_section_legal">היבטים משפטיים</string>
|
||||
<string name="screen_settings_licenses_label">רישיונות</string>
|
||||
<string name="screen_settings_license_terms_label">תנאי שימוש</string>
|
||||
<string name="screen_settings_section_support">תמיכה</string>
|
||||
<string name="screen_settings_contact_label">בקש תמיכה</string>
|
||||
<string name="screen_settings_debug_mode_label">מצב ניפוי תקלות</string>
|
||||
<string name="screen_settings_error_report_label">שלח יומן אירועים</string>
|
||||
<string name="screen_settings_error_report_failed">השליחה נכשלה</string>
|
||||
<string name="screen_settings_section_version">גרסה</string>
|
||||
<string name="screen_settings_advanced_settings">הגדרות מתקדמות</string>
|
||||
<!-- ## screen: cloud settings -->
|
||||
<string name="screen_cloud_settings_onedrive_connections">חיבורי OneDrive</string>
|
||||
<string name="screen_cloud_settings_webdav_connections">חיבורי WebDAV</string>
|
||||
<string name="screen_cloud_settings_pcloud_connections">חיבורי pCloud</string>
|
||||
<string name="screen_cloud_settings_s3_connections">חיבורי S3</string>
|
||||
<string name="screen_cloud_settings_local_storage_locations">מקומות אחסון במכשיר</string>
|
||||
<string name="screen_cloud_settings_log_in_to">התחבר אל</string>
|
||||
<string name="screen_cloud_settings_sign_out_from_cloud">התנתק מ</string>
|
||||
<!-- ## screen: licenses -->
|
||||
<!-- ## screen: authenticate cloud -->
|
||||
<string name="screen_authenticate_auth_authentication_failed">האימות אל \'%1$s\' נכשל.</string>
|
||||
<string name="screen_update_pcloud_connections_title">עדכן את פרטי ההתחברות אל pCloud</string>
|
||||
<!-- ## screen: insecure android version info -->
|
||||
<!-- # dialogs -->
|
||||
<string name="dialog_button_cancel">ביטול</string>
|
||||
<string name="dialog_enter_password_positive_button">בטל נעילה</string>
|
||||
<string name="dialog_change_password_old_password_label">סיסמה ישנה</string>
|
||||
<string name="dialog_change_password_new_password_label">סיסמה חדשה</string>
|
||||
<string name="dialog_change_password">שנה סיסמה</string>
|
||||
<string name="dialog_change_password_msg_old_password_empty">יש להזין סיסמה ישנה.</string>
|
||||
<string name="dialog_change_password_msg_new_password_empty">יש להזין סיסמה חדשה.</string>
|
||||
<!-- Vault not found -->
|
||||
<string name="dialog_vault_not_found_title">הכספת %1$s לא נמצאה</string>
|
||||
<string name="dialog_vault_not_found_positive_button_text">מחק</string>
|
||||
<string name="dialog_existing_file_title">הקובץ כבר קיים</string>
|
||||
<string name="dialog_existing_file_positive_button">החלף</string>
|
||||
<string name="dialog_existing_file_message">כבר קיים קובץ בשם %1$s.</string>
|
||||
<string name="dialog_replace_negative_button_at_least_two_but_not_all_files_exist">דלג על קיימים</string>
|
||||
<string name="dialog_replace_positive_button_all_files_exist">החלף הכל</string>
|
||||
<string name="dialog_replace_positive_button_some_files_exist">החלף קיימים</string>
|
||||
<string name="dialog_replace_positive_button_single_file_exists">החלף</string>
|
||||
<string name="dialog_replace_title_single_file_exists">להחליף את הקובץ?</string>
|
||||
<string name="dialog_replace_title_multiple_files_exist">להחליף את הקבצים?</string>
|
||||
<string name="dialog_unable_to_share_title">אין אפשרות לשתף את הקבצים</string>
|
||||
<string name="dialog_unable_to_share_message">עדיין לא הגדרת כספת. צור כספת חדשה בעזרת אפליקציית Cryptomator.</string>
|
||||
<string name="dialog_unable_to_share_positive_button">אישור</string>
|
||||
<string name="dialog_unable_to_share_negative_button">צור כספת חדשה</string>
|
||||
<string name="dialog_filetype_not_supported_title">הקובץ %1$s לא נתמך</string>
|
||||
<string name="dialog_filetype_not_supported_message">לא נמצאה אפליקצייה שיכולה להציג את הקובץ. האם תרצה לשמור קובץ זה על המכשיר?</string>
|
||||
<string name="dialog_rename_vault_title">שנה שם כספת</string>
|
||||
<string name="dialog_rename_node_folder_title">שנה שם תיקיה</string>
|
||||
<string name="dialog_rename_node_file_title">שינוי שם קובץ</string>
|
||||
<string name="dialog_unsaved_changes_title">יש לך שינויים שלא נשמרו</string>
|
||||
<string name="dialog_unsaved_changes_message">אתה בטוח שברצנוך לצאת מבלי לשמור את השינויים שביצעת?</string>
|
||||
<string name="dialog_unsaved_changes_discard">ביטול</string>
|
||||
<string name="dialog_file_name_placeholder">text.txt</string>
|
||||
<string name="dialog_delete_vault_message">אתה בטוח שברצונך למחוק כספת זו?</string>
|
||||
<string name="dialog_delete_vault_hint">פעולה זו תעלים את הכספת מהרשימה, אך לא תמחק אותה לחלוטין ממקום האחסון.</string>
|
||||
<string name="dialog_upload_file_title">שולח…</string>
|
||||
<string name="dialog_upload_file_remaining">קובץ %1$d מתוך %2$d</string>
|
||||
<string name="dialog_export_file_title">מיצא (%2$d/%1$d)</string>
|
||||
<string name="dialog_progress_please_wait">אנא המתן…</string>
|
||||
<string name="dialog_progress_creating_folder">יוצר תיקייה…</string>
|
||||
<string name="dialog_progress_creating_text_file">יוצר קובץ טקסט…</string>
|
||||
<string name="action_progress_authentication">אימות…</string>
|
||||
<string name="action_progress_renaming">משנה שם…</string>
|
||||
<string name="action_progress_deleting">מוחק…</string>
|
||||
<string name="dialog_progress_unlocking_vault">פותח כספת…</string>
|
||||
<string name="dialog_progress_change_password">משנה סיסמה…</string>
|
||||
<string name="dialog_progress_creating_vault">יוצר כספת…</string>
|
||||
<string name="dialog_progress_upload_file">שולח…</string>
|
||||
<string name="dialog_progress_download_file">מוריד…</string>
|
||||
<string name="dialog_progress_encryption">מצפין…</string>
|
||||
<string name="dialog_progress_decryption">מפענח…</string>
|
||||
<string name="action_progress_moving">מעביר…</string>
|
||||
<string name="dialog_lock_vault">נעילה</string>
|
||||
<string name="dialog_accept_ssl_certificate_title">תעודת SSL לא חוקית</string>
|
||||
<string name="dialog_accept_ssl_certificate_hint">תעודת SSL לא חוקית. תרצה לבטוח בתעודה זו בכל מקרה?</string>
|
||||
<string name="dialog_accept_ssl_certificate_details">פרטים</string>
|
||||
<string name="dialog_accept_ssl_certificate_security_checkbox">פעולה זו יכולה לגרום לפרצות אבטחה. אני מבין מהם ההשלכות.</string>
|
||||
<string name="dialog_http_security_hint">HTTP הוא פרוטוקול לא מאובטח. אנו ממליצים להשתמש ב HTTPS במקום. אם אתה מבין את הסיכונים בכך, אתה יכול להמשיך להשתמש ב HTTP.</string>
|
||||
<string name="dialog_http_security_checkbox">עבור ל-HTTPS</string>
|
||||
<string name="dialog_http_security_title">להשתמש ב-HTTPS?</string>
|
||||
<string name="dialog_debug_mode_disclaimer_title">שים לב</string>
|
||||
<string name="dialog_debug_mode_positive_button">הפעל</string>
|
||||
<string name="dialog_disable_app_obscured_disclaimer_title">שים לב</string>
|
||||
<string name="dialog_disable_app_obscured_positive_button">כבה</string>
|
||||
<string name="dialog_app_is_obscured_info_hint">אפליקציה אחרת מציגה משהו מעל Cryptomator (לדוגמה, אפליקציית סינון אור כחול למצב לילה). מטעמי אבטחה, Cryptomator ייסגר עכשיו.\n\n<a href="https://docs.cryptomator.org/en/1.5/android/settings/#block-app-when-obscured">איך להפעיל את Cryptomator</a></string>
|
||||
<string name="dialog_app_is_obscured_info_neutral_button">סגור</string>
|
||||
<string name="dialog_confirm_delete_multiple_title">למחוק %1$d פריטים?</string>
|
||||
<string name="dialog_confirm_delete_multiple_message">אתה בטוח שברצונך למחוק קבצים אלו?</string>
|
||||
<string name="dialog_confirm_delete_file_message">אתה בטוח שברצונך למחוק קובץ זה?</string>
|
||||
<string name="dialog_confirm_delete_folder_message">אתה בטוח שברצונך למחוק תיקייה זו? הפעולה תמחק גם את כל הקבצים שנמצאים בתוך התיקייה.</string>
|
||||
<string name="dialog_biometric_auth_key_invalidated_title">האימות הביומטרי בוטל</string>
|
||||
<string name="dialog_enter_license_title">שימוש במפתח רישיון</string>
|
||||
<string name="dialog_enter_license_content">זיהינו שהתקנת את Cryptomator לא דרך ה-Google Play Store. להפעלת האפליקציה, יש להכניס מפתח רישיון. ניתן לרכוש מפתח ב-<a href="https://cryptomator.org/android/">https://cryptomator.org/android/</a></string>
|
||||
<string name="dialog_enter_license_not_valid_content">המפתח לא אומת. וודא כי הזנת אותו כהלכה.</string>
|
||||
<string name="dialog_enter_license_no_content">לא הוזן מפתח, אנא הזן מפתח רישיון תקין.</string>
|
||||
<string name="dialog_enter_license_decline_button">יציאה</string>
|
||||
<string name="dialog_license_confirmation_title">מפתח הרישיון אומת</string>
|
||||
<string name="dialog_license_confirmation_message">תודה %1$s, על כך שהזנת מפתח רישיון תקין.</string>
|
||||
<string name="dialog_update_available_title">עדכון זמין</string>
|
||||
<string name="dialog_update_available_message">עדכן לגרסה החדשה ביותר של Cryptomator. לאחר שתסכים, העדכון ירד ברקע וכשההורדה תסתיים, תתבקש לבצע את ההתקנה.</string>
|
||||
<string name="dialog_update_available_update">עדכן עכשיו</string>
|
||||
<string name="dialog_update_available_download_site">למעבר לאתר הההורדות</string>
|
||||
<string name="dialog_update_available_cancel">מאוחר יותר</string>
|
||||
<string name="dialog_download_update_title">מוריד</string>
|
||||
<string name="dialog_download_update_message">מוריד את הגרסה העדכנית ביותר של Cryptomator</string>
|
||||
<string name="dialog_sym_link_title">התיקייה היא קישור סימבולי</string>
|
||||
<string name="dialog_sym_link_message">לא ניתן לנווט אל תוך קישור סימבולי זה</string>
|
||||
<string name="dialog_sym_link_back_button">חזור</string>
|
||||
<string name="dialog_no_dir_file_title">טעינת תיקייה זו נכשלה</string>
|
||||
<string name="dialog_no_more_images_to_display">אין עוד תמונות להציג…</string>
|
||||
<string name="dialog_pcloud_credentials_updated">אם ברצונך להוסיף משתמש pCloud נוסף, לחץ על הקישור <a href="https://www.pcloud.com">www.pcloud.com</a>, התנתק מהמשתמש הנוכחי ולחץ על ה \'+\' באפליקצייה כדי להוסיף חיבור חדש.</string>
|
||||
<string name="permission_snackbar_auth_local_vault">Cryptomator זקוק להרשאת גישה לזיכרון המכשיר כדי להשתמש בכספת מקומית</string>
|
||||
<string name="permission_snackbar_auth_auto_upload">Cryptomator זקוק להרשאת גישה לזיכרון המכשיר כדי לבצע העלאה אוטומטית של תמונות</string>
|
||||
<!-- # error reports -->
|
||||
<!-- # misc -->
|
||||
<!-- ## file size helper -->
|
||||
<string name="file_size_zero">0 kB</string>
|
||||
<string name="file_size_unit_bytes">bytes</string>
|
||||
<string name="file_size_unit_kilo_bytes">kB</string>
|
||||
<string name="file_size_unit_mega_bytes">MB</string>
|
||||
<string name="file_size_unit_giga_bytes">GB</string>
|
||||
<string name="file_size_unit_tera_bytes">TB</string>
|
||||
<!-- ## date helper -->
|
||||
<string name="time_unit_seconds_sg">שנייה</string>
|
||||
<string name="time_unit_seconds_pl">שניות</string>
|
||||
<string name="time_unit_minutes_sg">דקה</string>
|
||||
<string name="time_unit_minutes_pl">דקות</string>
|
||||
<string name="time_unit_hours_sg">שעה</string>
|
||||
<string name="time_unit_hours_pl">שעות</string>
|
||||
<string name="time_unit_days_sg">יום</string>
|
||||
<string name="time_unit_days_pl">ימים</string>
|
||||
<string name="time_unit_weeks_sg">שבוע</string>
|
||||
<string name="time_unit_weeks_pl">שבועות</string>
|
||||
<string name="time_unit_months_sg">חודש</string>
|
||||
<string name="time_unit_months_pl">חודש</string>
|
||||
<string name="time_unit_years_sg">שנה</string>
|
||||
<string name="time_unit_years_pl">שנים</string>
|
||||
<!-- ## biometric authentication -->
|
||||
<string name="dialog_biometric_auth_title">אימות ביומטרי</string>
|
||||
<string name="dialog_biometric_auth_message">התחבר באמצעות אימות ביומטרי</string>
|
||||
<string name="dialog_biometric_auth_use_password">השתמש בסיסמת הכספת</string>
|
||||
<string name="dialog_unable_to_auto_upload_files_title">לא ניתן לבצע העלאה אוטומטית של קבצים</string>
|
||||
<!-- notification -->
|
||||
<string name="notification_unlocked">כספות פתוחות: %1$d</string>
|
||||
<string name="notification_timeout">נעילה אוטומטית בעוד %1$s</string>
|
||||
<string name="notification_lock_all">נעל הכל</string>
|
||||
<string name="notification_cancel_auto_upload">בטל העלאה</string>
|
||||
<string name="notification_auto_upload_title">מתבצעת העלאה אוטומטית של תמונות</string>
|
||||
<string name="notification_auto_upload_message">מעלה %1d/%2d</string>
|
||||
<string name="notification_auto_upload_finished_title">העלאה אוטומטית של תמונות הסתיימה</string>
|
||||
<string name="notification_auto_upload_failed_title">העלאה אוטומטית של תמונות נכשלה</string>
|
||||
<string name="notification_auto_upload_failed_due_to_vault_not_found">הכספת המוגדרת להעלאה אוטומטית של קבצים לא קיימת יותר.</string>
|
||||
<string name="notification_update_check_finished_latest">הגרסה העדכנית ביותר מותקנת</string>
|
||||
<string name="screen_settings_lru_cache">מטמון</string>
|
||||
<string name="screen_settings_lru_cache_size">נפח מטמון</string>
|
||||
<string name="screen_settings_clear_lru_cache_size">נקה מטמון</string>
|
||||
<string name="screen_settings_license">רשום עבור</string>
|
||||
<string name="screen_settings_license_mail">%1$s</string>
|
||||
<string name="dialog_settings_update_check_interval_title">בדוק עדכונים אוטומטית</string>
|
||||
<string name="screen_settings_check_updates">חפש עדכונים</string>
|
||||
<string name="screen_settings_last_check_updates">בדיקה אחרונה %1$s</string>
|
||||
<string name="dialog_cache_size_title">גודל המטמון עבור כל שירות ענן</string>
|
||||
<!-- lock timeout names -->
|
||||
<string name="lock_timeout_instant">מיידי</string>
|
||||
<string name="lock_timeout_1m">דקה</string>
|
||||
<string name="lock_timeout_2m">2 דקות</string>
|
||||
<string name="lock_timeout_5m">5 דקות</string>
|
||||
<string name="lock_timeout_10m">10 דקות</string>
|
||||
<string name="lock_timeout_never">אף פעם</string>
|
||||
<!-- cache size names -->
|
||||
<string name="cache_size_50m">50 MB</string>
|
||||
<string name="cache_size_100m">100 MB</string>
|
||||
<string name="cache_size_250m">250 MB</string>
|
||||
<string name="cache_size_500m">500 MB</string>
|
||||
<string name="cache_size_1000m">1 GB</string>
|
||||
<string name="cache_size_5000m">5 GB</string>
|
||||
<!-- screen scheme mode names -->
|
||||
<string name="screen_settings_style_mode">עיצוב</string>
|
||||
<string name="follow_system">אוטומטי (לפי מערכת הפעלה)</string>
|
||||
<string name="light">בהיר</string>
|
||||
<string name="dark">כהה</string>
|
||||
<!-- update interval names -->
|
||||
<string name="update_interval_1d">פעם ביום</string>
|
||||
<string name="update_interval_1w">פעם בשבוע</string>
|
||||
<string name="update_interval_1m">פעם בחודש</string>
|
||||
</resources>
|
||||
|
@ -9,7 +9,7 @@
|
||||
<string name="error_no_network_connection">ネットワーク接続がありません</string>
|
||||
<string name="error_invalid_passphrase">パスワードが正しくありません</string>
|
||||
<string name="error_file_or_folder_exists">ファイルかフォルダーが既に存在します。</string>
|
||||
<string name="error_vault_version_not_supported">サポートされない金庫です。この金庫は別のバージョンの Cryptomator を使用して作成されました。</string>
|
||||
<string name="error_vault_version_not_supported">サポートされない金庫です。この金庫は別のバージョンの Cryptomator で作成されました。</string>
|
||||
<string name="error_vault_already_exists">金庫が既に存在します。</string>
|
||||
<string name="error_no_such_file">ファイルが存在しません。</string>
|
||||
<string name="error_vault_has_been_locked">金庫が施錠されました。</string>
|
||||
@ -20,18 +20,18 @@
|
||||
<string name="error_export_illegal_file_name">エクスポートに失敗しました。ファイル名から特殊文字を削除して再度エクスポートしてください。</string>
|
||||
<string name="error_name_contains_invalid_characters">特殊文字を含めることはできません。</string>
|
||||
<string name="error_names_contains_invalid_characters">特殊文字を含むファイル名は使用できません。</string>
|
||||
<string name="error_vault_name_contains_invalid_characters">特殊文字を含む金庫名は使用できません。</string>
|
||||
<string name="error_vault_name_contains_invalid_characters">金庫名に特殊文字を含めることはできません。</string>
|
||||
<string name="error_general_update">更新の確認に失敗しました。エラーが発生しました。</string>
|
||||
<string name="error_hash_mismatch_update">更新の確認に失敗しました。ハッシュ値がアップロードされたファイルと一致しません。</string>
|
||||
<string name="error_update_no_internet">更新の確認に失敗しました。インターネット接続がありません。</string>
|
||||
<string name="error_failed_to_decrypt_webdav_password">WebDAV パスワードの復号に失敗しました。設定を再度追加してください</string>
|
||||
<string name="error_play_services_not_available">Google Play Services がインストールされていません</string>
|
||||
<string name="error_play_services_not_available">Google Playサービスがインストールされていません</string>
|
||||
<string name="error_biometric_auth_aborted">生体認証が中断されました</string>
|
||||
<string name="error_vault_version_mismatch">%1$s で指定されたバージョンが %2$s と異なります</string>
|
||||
<string name="error_vault_key_invalid">%1$s はこの %2$s と一致しません</string>
|
||||
<string name="error_vault_config_loading">金庫の設定を読み込み中に失敗しました</string>
|
||||
<string name="error_no_such_bucket">Bucket がありません</string>
|
||||
<string name="error_masterkey_location_not_supported">自由に Masterkey の場所を設定することはまだできません</string>
|
||||
<string name="error_no_such_bucket">バケットがありません</string>
|
||||
<string name="error_masterkey_location_not_supported">好きな場所に Masterkey を置くことはまだできません</string>
|
||||
<!-- # clouds -->
|
||||
<!-- ## cloud names -->
|
||||
<string name="cloud_names_local_storage">ローカルストレージ</string>
|
||||
@ -40,10 +40,11 @@
|
||||
<string name="permission_message_export_file">ファイルをエクスポートするには、Cryptomator がストレージにアクセスする許可が必要です</string>
|
||||
<string name="permission_message_upload_file">ファイルをアップロードするには、Cryptomator がストレージにアクセスする許可が必要です</string>
|
||||
<string name="permission_message_share_file">ファイルを共有するには、Cryptomator がストレージにアクセスする許可が必要です</string>
|
||||
<string name="permission_revoked_re_request_permission">Cryptomator がこの場所にアクセスする権限がなくなりました。このフォルダーを再度選択して権限を再度取得してください。</string>
|
||||
<string name="snack_bar_action_title_settings">設定</string>
|
||||
<string name="snack_bar_action_title_search">検索</string>
|
||||
<string name="snack_bar_action_title_search_previous">戻る</string>
|
||||
<string name="snack_bar_action_title_search_next">次へ</string>
|
||||
<string name="snack_bar_action_title_search_next">進む</string>
|
||||
<string name="snack_bar_action_title_sort">ソート</string>
|
||||
<string name="snack_bar_action_title_sort_az">A - Z</string>
|
||||
<string name="snack_bar_action_title_sort_za">Z - A</string>
|
||||
@ -76,16 +77,16 @@
|
||||
<string name="screen_file_browser_action_create_new_text_file">テキスト ファイルを作成</string>
|
||||
<string name="screen_file_browser_action_upload_files">ファイルをアップロード</string>
|
||||
<string name="screen_file_browser_upload_files_chooser_title">ファイル</string>
|
||||
<string name="screen_file_browser_msg_file_exported">エクスポートされたファイル</string>
|
||||
<string name="screen_file_browser_msg_files_exported">エクスポートされたファイル</string>
|
||||
<string name="screen_file_browser_msg_file_exported">ファイルをエクスポートしました</string>
|
||||
<string name="screen_file_browser_msg_files_exported">ファイルをエクスポートしました</string>
|
||||
<string name="screen_file_browser_nothing_to_export">エクスポートするものがありません</string>
|
||||
<string name="screen_file_browser_msg_creating_download_dir_failed">ダウンロード ディレクトリの作成に失敗しました</string>
|
||||
<string name="screen_file_browser_msg_creating_download_dir_failed">ダウンロードディレクトリの作成に失敗しました</string>
|
||||
<string name="screen_file_browser_node_action_share">共有</string>
|
||||
<string name="screen_file_browser_node_action_rename">名前の変更</string>
|
||||
<string name="screen_file_browser_node_action_rename">名前を変更</string>
|
||||
<string name="screen_file_browser_node_action_edit_text">編集</string>
|
||||
<string name="screen_file_browser_node_action_export">エクスポート</string>
|
||||
<string name="screen_file_browser_node_action_delete">削除</string>
|
||||
<string name="screen_file_browser_node_action_open_with_text">次で開く…</string>
|
||||
<string name="screen_file_browser_node_action_open_with_text">…で開く</string>
|
||||
<string name="screen_file_browser_selection_mode_title_zero_elements">アイテムを選択</string>
|
||||
<string name="screen_file_browser_selection_mode_title_one_or_more_elements">%1$d 個選択済み</string>
|
||||
<string name="screen_file_browser_select_items">選択</string>
|
||||
@ -100,15 +101,15 @@
|
||||
<string name="screen_share_files_content_text">テキスト</string>
|
||||
<string name="screen_share_files_content_file">ファイル</string>
|
||||
<string name="screen_share_files_content_files">ファイル</string>
|
||||
<string name="screen_share_files_msg_filenames_must_be_unique">ファイル名は重複しない必要があります。名前を変更してください</string>
|
||||
<string name="screen_share_files_msg_filenames_must_be_unique">ファイル名は重複してはなりません。名前を変更してください</string>
|
||||
<string name="screen_share_files_section_location">保存先</string>
|
||||
<string name="screen_share_files_save_button_text">保存</string>
|
||||
<string name="screen_share_files_msg_success">暗号化が完了しました</string>
|
||||
<!-- ## screen: choose cloud service -->
|
||||
<string name="screen_choose_cloud_service_title">クラウド サービス</string>
|
||||
<string name="screen_choose_cloud_service_title">クラウド・サービス</string>
|
||||
<!-- ## screen: cloud connections -->
|
||||
<string name="screen_cloud_connections_title">接続先を選択</string>
|
||||
<string name="screen_cloud_connections_no_connections">ここをタップして場所を追加する</string>
|
||||
<string name="screen_cloud_connections_no_connections">ここをタップして接続先を追加する</string>
|
||||
<string name="screen_cloud_error_webdav_not_supported">サーバーに WebDAV との互換性がありません</string>
|
||||
<string name="screen_cloud_local_error_no_content_provider">追加の利用できる保存先はありません。</string>
|
||||
<!-- ## screen: webdav settings -->
|
||||
@ -122,16 +123,16 @@
|
||||
<string name="screen_webdav_settings_msg_password_must_not_be_empty">パスワードは空にできません。</string>
|
||||
<!-- ## screen: s3 settings -->
|
||||
<string name="screen_s3_settings_display_name_label">表示名</string>
|
||||
<string name="screen_s3_settings_access_key_label">接続キー</string>
|
||||
<string name="screen_s3_settings_secret_key_label">秘密鍵</string>
|
||||
<string name="screen_s3_settings_bucket_label">既存の Bucket</string>
|
||||
<string name="screen_s3_settings_access_key_label">アクセスキー</string>
|
||||
<string name="screen_s3_settings_secret_key_label">シークレットキー</string>
|
||||
<string name="screen_s3_settings_bucket_label">既存のバケット</string>
|
||||
<string name="screen_s3_settings_endpoint_label">エンドポイント</string>
|
||||
<string name="screen_s3_settings_region_label">地域</string>
|
||||
<string name="screen_s3_settings_region_label">リージョン</string>
|
||||
<string name="screen_s3_settings_msg_display_name_not_empty">表示名は空にできません</string>
|
||||
<string name="screen_s3_settings_msg_access_key_not_empty">接続キーは空にできません</string>
|
||||
<string name="screen_s3_settings_msg_secret_key_not_empty">秘密キーは空にできません</string>
|
||||
<string name="screen_s3_settings_msg_bucket_not_empty">Bucket は空にできません</string>
|
||||
<string name="screen_s3_settings_msg_endpoint_and_region_not_empty">エンドポイントまたは地域は空にできません</string>
|
||||
<string name="screen_s3_settings_msg_access_key_not_empty">アクセスキーは空にできません</string>
|
||||
<string name="screen_s3_settings_msg_secret_key_not_empty">シークレットキーは空にできません</string>
|
||||
<string name="screen_s3_settings_msg_bucket_not_empty">バケットは空にできません</string>
|
||||
<string name="screen_s3_settings_msg_endpoint_and_region_not_empty">エンドポイントまたはリージョンは空にできません</string>
|
||||
<!-- ## screen: enter vault name -->
|
||||
<string name="screen_enter_vault_name_msg_name_empty">金庫名は空にできません。</string>
|
||||
<string name="screen_enter_vault_name_vault_label">金庫の名前</string>
|
||||
@ -149,10 +150,10 @@
|
||||
<string name="screen_set_password_strength_indicator_4">非常に強い</string>
|
||||
<!-- ## screen: settings -->
|
||||
<string name="screen_settings_section_general">基本設定</string>
|
||||
<string name="screen_settings_cloud_settings_label">クラウド サービス</string>
|
||||
<string name="screen_settings_cloud_settings_label">クラウド・サービス</string>
|
||||
<string name="screen_settings_biometric_auth">生体認証</string>
|
||||
<string name="screen_settings_activate_biometric_auth">生体認証を有効にする</string>
|
||||
<string name="screen_settings_confirm_face_unlock">顔認証のロック解除 (利用できる場合)</string>
|
||||
<string name="screen_settings_confirm_face_unlock">顔認証のロック解除(利用できる場合)</string>
|
||||
<string name="screen_settings_block_app_when_obscured">他のアプリが重なるときにアプリを停止</string>
|
||||
<string name="screen_settings_block_app_when_obscured_summary">入力をブロックし、偽のユーザー インターフェイスを表示する</string>
|
||||
<string name="screen_settings_secure_screen">スクリーンショットをブロック</string>
|
||||
@ -163,7 +164,7 @@
|
||||
<string name="screen_settings_glob_search">glob パターンを使用して検索</string>
|
||||
<string name="screen_settings_glob_search_summary">alice.*.jpg のような glob パターン一致を使用する</string>
|
||||
<string name="screen_settings_section_auto_lock">自動的な施錠</string>
|
||||
<string name="screen_settings_auto_lock_timeout">次の場合にロックする</string>
|
||||
<string name="screen_settings_auto_lock_timeout">次の場合に施錠する</string>
|
||||
<string name="screen_settings_auto_lock_on_screen_lock">画面がロックされたとき</string>
|
||||
<string name="screen_settings_section_auto_photo_upload">自動的に画像をアップロード</string>
|
||||
<string name="screen_settings_section_auto_photo_upload_vault">アップロードする金庫を選択</string>
|
||||
@ -172,7 +173,7 @@
|
||||
<string name="screen_settings_section_auto_photo_upload_toggle_instant_upload">インスタントをアップロード</string>
|
||||
<string name="screen_settings_section_auto_photo_upload_toggle_instant_upload_summary">金庫が解錠されているときに直接アップロード</string>
|
||||
<string name="screen_settings_section_auto_photo_upload_only_wifi_toggle">WIFI を使用しているときのみアップロード</string>
|
||||
<string name="screen_settings_section_auto_photo_upload_including_videos">ビデオをアップロード</string>
|
||||
<string name="screen_settings_section_auto_photo_upload_including_videos">動画をアップロード</string>
|
||||
<string name="screen_settings_auto_photo_upload_title">… へのファイルの自動アップロードを保存</string>
|
||||
<string name="screen_settings_website_label">Cryptomator のウェブサイト</string>
|
||||
<string name="screen_settings_twitter_label">Twitter でフォローする</string>
|
||||
@ -183,11 +184,12 @@
|
||||
<string name="screen_settings_section_support">サポート</string>
|
||||
<string name="screen_settings_contact_label">ヘルプを求める</string>
|
||||
<string name="screen_settings_debug_mode_label">デバッグ モード</string>
|
||||
<string name="screen_settings_error_report_label">ログ ファイルを送信</string>
|
||||
<string name="screen_settings_error_report_label">ログファイルを送信</string>
|
||||
<string name="screen_settings_error_report_failed">送信できませんでした</string>
|
||||
<string name="screen_settings_security_label">セキュリティのヒント</string>
|
||||
<string name="screen_settings_security_label">セキュリティの心得</string>
|
||||
<string name="screen_settings_section_version">バージョン</string>
|
||||
<string name="screen_settings_advanced_settings">高度な設定</string>
|
||||
<string name="screen_settings_background_unlock_preparation_label">高速な解錠</string>
|
||||
<string name="screen_settings_background_unlock_preparation_label_summary">パスワードまたは生体による認証中に、バックグラウンドで金庫の設定をダウンロードします</string>
|
||||
<string name="screen_settings_keep_unlocked_while_editing_files">解錠したままにする</string>
|
||||
<string name="screen_settings_keep_unlocked_while_editing_files_summary">ファイルの編集中は金庫を解錠しておく</string>
|
||||
@ -195,13 +197,13 @@
|
||||
<string name="screen_cloud_settings_webdav_connections">WebDAV 接続</string>
|
||||
<string name="screen_cloud_settings_pcloud_connections">pCloud 接続</string>
|
||||
<string name="screen_cloud_settings_s3_connections">S3 接続</string>
|
||||
<string name="screen_cloud_settings_local_storage_locations">ローカル ストレージの場所</string>
|
||||
<string name="screen_cloud_settings_local_storage_locations">ローカル・ストレージの場所</string>
|
||||
<string name="screen_cloud_settings_log_in_to">ログイン</string>
|
||||
<string name="screen_cloud_settings_sign_out_from_cloud">次からサインアウト</string>
|
||||
<!-- ## screen: licenses -->
|
||||
<!-- ## screen: authenticate cloud -->
|
||||
<string name="screen_authenticate_auth_authentication_failed">%1$s は認証できませんでした。</string>
|
||||
<string name="screen_update_pcloud_connections_title">pCloud 資格情報を更新</string>
|
||||
<string name="screen_update_pcloud_connections_title">pCloud アカウント情報を更新</string>
|
||||
<!-- ## screen: insecure android version info -->
|
||||
<!-- # dialogs -->
|
||||
<string name="dialog_button_cancel">キャンセル</string>
|
||||
@ -223,7 +225,7 @@
|
||||
<string name="dialog_replace_positive_button_all_files_exist">すべて置換</string>
|
||||
<string name="dialog_replace_positive_button_some_files_exist">今のものを置き換える</string>
|
||||
<string name="dialog_replace_positive_button_single_file_exists">置換</string>
|
||||
<string name="dialog_replace_msg_single_file_exists">ファイル名 \"%1$s\" が既に存在します。置き換えますか?</string>
|
||||
<string name="dialog_replace_msg_single_file_exists">ファイル名 \"%1$s\" が既に存在します。上書きしますか?</string>
|
||||
<string name="dialog_replace_msg_all_files_exists">すべてのファイルが既に存在します。すべて置き換えますか?</string>
|
||||
<string name="dialog_replace_msg_some_files_exists">%1$d ファイルが既に存在します。すべて置き換えますか?</string>
|
||||
<string name="dialog_replace_title_single_file_exists">ファイルを置き換えますか?</string>
|
||||
@ -236,7 +238,7 @@
|
||||
<string name="dialog_filetype_not_supported_message">このファイルを開くことができるアプリをダウンロードしてください。それともデバイスに保存しますか?</string>
|
||||
<string name="dialog_rename_vault_title">金庫の名前を変更</string>
|
||||
<string name="dialog_rename_node_folder_title">フォルダーの名前を変更</string>
|
||||
<string name="dialog_rename_node_file_title">ファイルの名前を変更</string>
|
||||
<string name="dialog_rename_node_file_title">ファイル名を変更</string>
|
||||
<string name="dialog_unsaved_changes_title">保存していない変更があります</string>
|
||||
<string name="dialog_unsaved_changes_message">保存せずに終了してもよろしいですか?</string>
|
||||
<string name="dialog_unsaved_changes_discard">破棄</string>
|
||||
@ -264,55 +266,56 @@
|
||||
<string name="dialog_accept_ssl_certificate_title">不正な SSL 証明書です</string>
|
||||
<string name="dialog_accept_ssl_certificate_hint">SSL 証明書が無効です。それでも信頼しますか?</string>
|
||||
<string name="dialog_accept_ssl_certificate_details">詳細</string>
|
||||
<string name="dialog_accept_ssl_certificate_security_checkbox">これはセキュリティ リスクになる可能性があります。リスクについて理解していますか</string>
|
||||
<string name="dialog_http_security_hint">HTTP の使用は安全ではありません。代わりに HTTPS を使用することを推奨します。リスクを承知の上であれば HTTP をご使用ください。</string>
|
||||
<string name="dialog_http_security_checkbox">HTTPS に変更</string>
|
||||
<string name="dialog_http_security_title">HTTPS を使用しますか?</string>
|
||||
<string name="dialog_accept_ssl_certificate_security_checkbox">これはセキュリティリスクになる可能性があります。リスクについて理解していますか</string>
|
||||
<string name="dialog_http_security_hint">HTTP は安全ではありません。代わりに HTTPS を使用することを推奨します。リスクを承知の上であれば HTTP をご使用ください。</string>
|
||||
<string name="dialog_http_security_checkbox">HTTPS を使用</string>
|
||||
<string name="dialog_http_security_title">HTTPS を使用しますか?</string>
|
||||
<string name="dialog_no_screen_lock_hint">画面ロックが設定されていません。認証情報を安全に保存するには、パターンまたはパスワードを設定してください。</string>
|
||||
<string name="dialog_no_screen_lock_title">画面ロックを設定しますか?</string>
|
||||
<string name="dialog_no_screen_lock_title">画面ロックを設定しますか?</string>
|
||||
<string name="dialog_no_screen_lock_checkbox">画面ロックを設定</string>
|
||||
<string name="dialog_no_biometric_auth_set_up_title">システムに基本的な認証が設定されていません</string>
|
||||
<string name="dialog_no_biometric_auth_set_up_message">このサービスを利用するには少なくとも 1 つの指紋/顔を登録してください。</string>
|
||||
<string name="dialog_debug_mode_disclaimer_hint">このモードでは、デバイスのログファイルに機密情報が書き込まれることがあります(例: ファイル名やパス)。パスワードや cookie 等は除外されます。\n\n できるだけ早くデバッグ モードを無効化をすることを忘れないでください。</string>
|
||||
<string name="dialog_debug_mode_disclaimer_title">注意</string>
|
||||
<string name="dialog_debug_mode_positive_button">有効</string>
|
||||
<string name="dialog_debug_mode_positive_button">有効にする</string>
|
||||
<string name="dialog_disable_app_obscured_disclaimer_hint">この設定は安全のための機能です。他のアプリがユーザーを騙してしまうことを防ぎます。\n\n無効にすることで、 <a href="https://docs.cryptomator.org/en/1.5/android/settings/#block-app-when-obscured">リスクを認識していること</a> に留意する必要があります。</string>
|
||||
<string name="dialog_disable_app_obscured_disclaimer_title">注意</string>
|
||||
<string name="dialog_disable_app_obscured_positive_button">無効</string>
|
||||
<string name="dialog_app_is_obscured_info_title">アプリが重なっています</string>
|
||||
<string name="dialog_app_is_obscured_info_hint">他のアプリケーションが Cryptomator の上に何かを表示しています (例: ブルーライト フィルターや night mode アプリ)。このため、セキュリティの観点から Cryptomator が無効化されています。\n\n<a href="https://docs.cryptomator.org/en/1.5/android/settings/#block-app-when-obscured">Cryptomator を有効にするには</a></string>
|
||||
<string name="dialog_app_is_obscured_info_hint">他のアプリケーションが Cryptomator の上に何かを表示しています(例: ブルーライト フィルターやナイト・モードアプリ)。このため、セキュリティの観点から Cryptomator が無効化されています。\n\n<a href="https://docs.cryptomator.org/en/1.5/android/settings/#block-app-when-obscured">Cryptomator を有効にするには</a></string>
|
||||
<string name="dialog_app_is_obscured_info_neutral_button">閉じる</string>
|
||||
<string name="dialog_vaults_removed_during_migration_title">%1s クラウンドの金庫を再追加してください</string>
|
||||
<string name="dialog_vault_is_root_folder_of_cloud_title">金庫がクラウト接続のルート フォルダーです</string>
|
||||
<string name="dialog_disable_secure_screen_disclaimer_hint">この設定は安全のための機能です。他のアプリがユーザーを騙してしまうことを防ぎます。\n\n無効にすることで、 <a href="https://docs.cryptomator.org/en/1.5/android/settings/#screen-security">リスクを認識していること</a> に留意する必要があります。</string>
|
||||
<string name="dialog_delete_cloud_connection_with_vaults_message">本当にこのクラウド接続を削除しますか?</string>
|
||||
<string name="dialog_delete_cloud_connection_with_vaults_hint">この操作により、クラウド接続とクラウドのすべての金庫が削除されます。</string>
|
||||
<string name="dialog_confirm_delete_multiple_title">%1$d 個の項目を削除しますか?</string>
|
||||
<string name="dialog_confirm_delete_multiple_message">本当にこれらの項目を削除してもよろしいですか?</string>
|
||||
<string name="dialog_confirm_delete_multiple_title">%1$d 個の項目を削除しますか?</string>
|
||||
<string name="dialog_confirm_delete_multiple_message">本当にこれらの項目を削除してもよろしいですか?</string>
|
||||
<string name="dialog_confirm_delete_file_message">本当にこのファイルを削除してもよろしいですか?</string>
|
||||
<string name="dialog_confirm_delete_folder_message">この操作はフォルダーの内容をすべて削除します。本当にこのフォルダーを削除してもよろしいですか?</string>
|
||||
<string name="dialog_confirm_delete_folder_message">この操作はフォルダーの内容をすべて削除します。本当にこのフォルダーを削除してもよろしいですか?</string>
|
||||
<string name="dialog_biometric_auth_key_invalidated_title">生体認証機能が無効になりました</string>
|
||||
<string name="dialog_biometric_auth_key_invalidated_message">鍵が無効になったため、生体認証が無効になりました。再度有効にするには、Cryptomator 設定を開いてください。</string>
|
||||
<string name="dialog_biometric_auth_key_invalidated_message">鍵が無効になったため、生体認証が無効になりました。再度有効にするには、Cryptomator の設定を開いてください。</string>
|
||||
<string name="dialog_enter_license_title">有効なライセンスを入力してください</string>
|
||||
<string name="dialog_enter_license_content">Google Play Store を使用せずに Cryptomator をインストールしたことを検出しました。有効なライセンスを購入してください: <a href="https://cryptomator.org/android/">https://cryptomator.org/android/</a></string>
|
||||
<string name="dialog_enter_license_content">Google Play ストアを使用せずに Cryptomator をインストールしたことを検出しました。有効なライセンスを購入してください: <a href="https://cryptomator.org/android/">https://cryptomator.org/android/</a></string>
|
||||
<string name="dialog_enter_license_not_valid_content">入力されたライセンスが無効です。正しく入力されていることを確認してください。</string>
|
||||
<string name="dialog_enter_license_no_content">ライセンスがありません。有効なライセンスを入力してください。</string>
|
||||
<string name="dialog_enter_license_decline_button">終了</string>
|
||||
<string name="dialog_license_confirmation_title">ライセンスの確認</string>
|
||||
<string name="dialog_license_confirmation_title">ライセンスの確認を完了</string>
|
||||
<string name="dialog_license_confirmation_message">%1$s-san 有効なライセンスを入力していただきありがとうございます。 </string>
|
||||
<string name="dialog_update_available_title">利用可能なアップデートがあります</string>
|
||||
<string name="dialog_update_available_message">Cryptomator を最新のバージョンに更新します。OK を押すとバックグラウンドでアプリがダウンロードされ、インストール可能になると通知されます。</string>
|
||||
<string name="dialog_update_available_update">すぐに更新</string>
|
||||
<string name="dialog_update_available_download_site">ダウンロード サイトに行く</string>
|
||||
<string name="dialog_update_available_download_site">ダウンロードサイトに行く</string>
|
||||
<string name="dialog_update_available_cancel">あとで</string>
|
||||
<string name="dialog_download_update_title">ダウンロード中</string>
|
||||
<string name="dialog_download_update_message">Cryptomator の最新バージョンをダウンロード中</string>
|
||||
<string name="dialog_sym_link_title">フォルダがシンボリック リンクです</string>
|
||||
<string name="dialog_sym_link_message">このシンボリック リンクに移動することはできません</string>
|
||||
<string name="dialog_sym_link_title">フォルダがシンボリックリンクです</string>
|
||||
<string name="dialog_sym_link_message">このシンボリックリンクに移動することはできません</string>
|
||||
<string name="dialog_sym_link_back_button">戻る</string>
|
||||
<string name="dialog_no_dir_file_title">ディレクトリのコンテンツを読み込むことができません</string>
|
||||
<string name="dialog_no_more_images_to_display">表示する画像がありません…</string>
|
||||
<string name="dialog_pcloud_credentials_updated_title">\'%1$s\' の資格情報が更新されました</string>
|
||||
<string name="dialog_pcloud_credentials_updated">pCloud アカウントを追加するには、次のリンクをクリックしてください <a href="https://www.pcloud.com">www.pcloud.com</a>。現在のアカウントからログアウトし、このアプリの「+」を再度クリックして、新しいクラウド接続を作成してください。</string>
|
||||
<string name="permission_snackbar_auth_local_vault">ローカルの金庫を使用するには、Cryptomator がストレージにアクセスする許可が必要です</string>
|
||||
<string name="permission_snackbar_auth_auto_upload">自動的に画像をアップロードするには、Cryptomator がストレージにアクセスする許可が必要です</string>
|
||||
<!-- # error reports -->
|
||||
@ -355,7 +358,9 @@
|
||||
<string name="notification_auto_upload_finished_message">%1$d 個の画像を金庫にアップロードしました</string>
|
||||
<string name="notification_auto_upload_failed_title">写真の自動アップロードが失敗しました</string>
|
||||
<string name="notification_auto_upload_failed_general_error">アップロード中にエラーが発生しました。</string>
|
||||
<string name="notification_auto_upload_failed_due_to_folder_not_exists">アップロード用として選択されたフォルダーを利用できません。設定に移動して新しいフォルダーを選択してください。</string>
|
||||
<string name="notification_auto_upload_failed_due_to_vault_locked">アップロード中は金庫が施錠されます。続けるには金庫を再度開いてください。</string>
|
||||
<string name="notification_auto_upload_failed_due_to_vault_not_found">自動アップロードに設定された金庫が存在しません。</string>
|
||||
<string name="notification_open_writable_file_title">書き込み可能なファイルを開く</string>
|
||||
<string name="notification_open_writable_file_message">編集が完了するまで金庫は施錠されます</string>
|
||||
<string name="notification_update_check_finished_latest">最新バージョンがインストールされました</string>
|
||||
@ -370,12 +375,12 @@
|
||||
<string name="screen_settings_last_check_updates">最終実行 %1$s</string>
|
||||
<string name="dialog_cache_size_title">クラウドごとのキャッシュ サイズ</string>
|
||||
<!-- lock timeout names -->
|
||||
<string name="lock_timeout_instant">インスタント</string>
|
||||
<string name="lock_timeout_1m">1分</string>
|
||||
<string name="lock_timeout_2m">2分</string>
|
||||
<string name="lock_timeout_5m">5分</string>
|
||||
<string name="lock_timeout_10m">10分</string>
|
||||
<string name="lock_timeout_never">無期限</string>
|
||||
<string name="lock_timeout_instant">即時</string>
|
||||
<string name="lock_timeout_1m">1分経過後</string>
|
||||
<string name="lock_timeout_2m">2分経過後</string>
|
||||
<string name="lock_timeout_5m">5分経過後</string>
|
||||
<string name="lock_timeout_10m">10分経過後</string>
|
||||
<string name="lock_timeout_never">ロックしない</string>
|
||||
<!-- cache size names -->
|
||||
<string name="cache_size_50m">50 MB</string>
|
||||
<string name="cache_size_100m">100 MB</string>
|
||||
@ -385,7 +390,7 @@
|
||||
<string name="cache_size_5000m">5 GB</string>
|
||||
<!-- screen scheme mode names -->
|
||||
<string name="screen_settings_style_mode">外観</string>
|
||||
<string name="follow_system">自動 (システムに従う)</string>
|
||||
<string name="follow_system">自動(システムに従う)</string>
|
||||
<string name="light">ライト</string>
|
||||
<string name="dark">ダーク</string>
|
||||
<!-- update interval names -->
|
||||
|
@ -22,11 +22,18 @@
|
||||
<string name="error_names_contains_invalid_characters">Filnavn kan ikke inneholde spesialtegn.</string>
|
||||
<string name="error_vault_name_contains_invalid_characters">Navn på hvelvet kan ikke inneholde spesialtegn.</string>
|
||||
<string name="error_general_update">Oppdateringskontrollen mislyktes. Det oppstod en generell feil.</string>
|
||||
<string name="error_hash_mismatch_update">Oppdateringskontrollen mislyktes. Den beregnede hash-koden stemmer ikke med den opplastede filen</string>
|
||||
<string name="error_biometric_auth_aborted">Biometrisk autentisering avbrutt</string>
|
||||
<string name="error_masterkey_location_not_supported">Egendefinert posisjon for Masterkey er foreløpig ikke støttet</string>
|
||||
<!-- # clouds -->
|
||||
<!-- ## cloud names -->
|
||||
<string name="cloud_names_local_storage">Lokal lagring</string>
|
||||
<!-- # permission -->
|
||||
<!-- ## permission messages -->
|
||||
<string name="permission_message_export_file">Cryptomator trenger lagringstilgang for å eksportere filer</string>
|
||||
<string name="permission_message_upload_file">Cryptomator trenger lagringstilgang for å laste opp filer</string>
|
||||
<string name="permission_message_share_file">Cryptomator trenger lagringstilgang for å dele filer</string>
|
||||
<string name="permission_revoked_re_request_permission">Cryptomator har mistet tilgangen til denne plasseringen. Velg mappen på nytt for å gjenopprette tillatelsen.</string>
|
||||
<string name="snack_bar_action_title_settings">Innstillinger</string>
|
||||
<string name="snack_bar_action_title_search">Søk</string>
|
||||
<string name="snack_bar_action_title_search_previous">Forrige</string>
|
||||
@ -68,6 +75,7 @@
|
||||
<string name="screen_file_browser_node_action_export">Eksporter</string>
|
||||
<string name="screen_file_browser_node_action_delete">Slett</string>
|
||||
<string name="screen_file_browser_node_action_open_with_text">Åpne med…</string>
|
||||
<string name="screen_file_browser_selection_mode_title_zero_elements">Velg elementer</string>
|
||||
<string name="screen_file_browser_selection_mode_title_one_or_more_elements">%1$d valgt</string>
|
||||
<string name="screen_file_browser_select_all_items">Velg alle</string>
|
||||
<string name="screen_file_browser_refresh_items">Oppdater</string>
|
||||
@ -80,7 +88,9 @@
|
||||
<string name="screen_share_files_content_text">tekst</string>
|
||||
<string name="screen_share_files_content_file">fil</string>
|
||||
<string name="screen_share_files_content_files">filer</string>
|
||||
<string name="screen_share_files_msg_filenames_must_be_unique">Filnavnene må være unike, velg nye navn på duplikatene.</string>
|
||||
<string name="screen_share_files_save_button_text">Lagre</string>
|
||||
<string name="screen_share_files_msg_success">Kryptering fullført</string>
|
||||
<!-- ## screen: choose cloud service -->
|
||||
<string name="screen_choose_cloud_service_title">Skylagringstjeneste</string>
|
||||
<!-- ## screen: cloud connections -->
|
||||
@ -88,12 +98,21 @@
|
||||
<string name="screen_webdav_settings_url_port_label">URL</string>
|
||||
<string name="screen_webdav_settings_username_label">Brukernavn</string>
|
||||
<string name="screen_webdav_settings_password_label">Passord</string>
|
||||
<string name="screen_webdav_settings_msg_url_must_not_be_empty">URL-adressen kan ikke være tomt.</string>
|
||||
<string name="screen_webdav_settings_msg_username_must_not_be_empty">Brukernavnet kan ikke være tomt.</string>
|
||||
<string name="screen_webdav_settings_msg_password_must_not_be_empty">Passordet kan ikke være tomt.</string>
|
||||
<!-- ## screen: s3 settings -->
|
||||
<string name="screen_s3_settings_display_name_label">Visningsnavn</string>
|
||||
<string name="screen_s3_settings_msg_display_name_not_empty">Visningsnavn kan ikke være tomt</string>
|
||||
<string name="screen_s3_settings_msg_access_key_not_empty">Tilgangsnøkkel kan ikke være tom</string>
|
||||
<string name="screen_s3_settings_msg_secret_key_not_empty">Hemmelig nøkkel kan ikke være tom</string>
|
||||
<!-- ## screen: enter vault name -->
|
||||
<string name="screen_enter_vault_name_msg_name_empty">Navnet på hvelvet kan ikke være tomt.</string>
|
||||
<string name="screen_enter_vault_name_vault_label">Hvelvnavn</string>
|
||||
<string name="screen_enter_vault_name_button_text">Opprett</string>
|
||||
<!-- ## screen: set password -->
|
||||
<string name="screen_set_password_title">Angi passord</string>
|
||||
<string name="screen_set_password_msg_password_mismatch">Passordene stemmer ikke overens.</string>
|
||||
<string name="screen_set_password_button_text">Ferdig</string>
|
||||
<string name="screen_set_password_retype_password_label">Gjenta passordet</string>
|
||||
<string name="screen_set_password_strength_indicator_1">Svakt</string>
|
||||
@ -103,6 +122,9 @@
|
||||
<!-- ## screen: settings -->
|
||||
<string name="screen_settings_section_general">Generelt</string>
|
||||
<string name="screen_settings_cloud_settings_label">Skylagringstjenester</string>
|
||||
<string name="screen_settings_biometric_auth">Biometrisk autentisering</string>
|
||||
<string name="screen_settings_activate_biometric_auth">Aktiver biometrisk autentisering</string>
|
||||
<string name="screen_settings_confirm_face_unlock">Bekreft ansiktsopplåsing (hvis tilgjengelig)</string>
|
||||
<string name="screen_settings_section_search">Søk</string>
|
||||
<string name="screen_settings_auto_lock_timeout">Lås etter</string>
|
||||
<string name="screen_settings_section_auto_photo_upload_toggle">Aktiver</string>
|
||||
|
@ -199,6 +199,7 @@
|
||||
<string name="screen_settings_keep_unlocked_while_editing_files">Ontgrendeld houden</string>
|
||||
<string name="screen_settings_keep_unlocked_while_editing_files_summary">Houd kluizen ontgrendeld tijdens het bewerken van bestanden</string>
|
||||
<!-- ## screen: cloud settings -->
|
||||
<string name="screen_cloud_settings_onedrive_connections">OneDrive verbindingen</string>
|
||||
<string name="screen_cloud_settings_webdav_connections">WebDAV verbindingen</string>
|
||||
<string name="screen_cloud_settings_pcloud_connections">pCloud verbindingen</string>
|
||||
<string name="screen_cloud_settings_s3_connections">S3 verbindingen</string>
|
||||
@ -372,6 +373,7 @@
|
||||
<string name="notification_open_writable_file_title">Open schrijfbaar bestand</string>
|
||||
<string name="notification_open_writable_file_message">Kluis blijft ontgrendeld tot het bewerken voltooid is</string>
|
||||
<string name="notification_update_check_finished_latest">Laatste versie geïnstalleerd</string>
|
||||
<string name="notification_authenticating">Verifiëren…</string>
|
||||
<string name="screen_settings_lru_cache">Cache</string>
|
||||
<string name="screen_settings_lru_cache_toggle_summary">Bewaar recent geopende bestanden versleuteld en lokaal op het apparaat voor later hergebruik</string>
|
||||
<string name="screen_settings_lru_cache_size">Totale cache grootte</string>
|
||||
|
@ -201,6 +201,7 @@
|
||||
<string name="screen_settings_keep_unlocked_while_editing_files">Zachowaj odblokowany</string>
|
||||
<string name="screen_settings_keep_unlocked_while_editing_files_summary">Zachowaj odblokowane sejfy podczas edycji plików</string>
|
||||
<!-- ## screen: cloud settings -->
|
||||
<string name="screen_cloud_settings_onedrive_connections">Połączenia OneDrive</string>
|
||||
<string name="screen_cloud_settings_webdav_connections">Połączenia WebDAV</string>
|
||||
<string name="screen_cloud_settings_pcloud_connections">Połączenia pCloud</string>
|
||||
<string name="screen_cloud_settings_s3_connections">Połączenia S3</string>
|
||||
@ -374,6 +375,7 @@
|
||||
<string name="notification_open_writable_file_title">Otwórz plik zapisywalny</string>
|
||||
<string name="notification_open_writable_file_message">Sejf pozostaje odblokowany do czasu zakończenia edycji</string>
|
||||
<string name="notification_update_check_finished_latest">Zainstalowano najnowszą wersję</string>
|
||||
<string name="notification_authenticating">Uwierzytelnianie…</string>
|
||||
<string name="screen_settings_lru_cache">Pamięć podręczna</string>
|
||||
<string name="screen_settings_lru_cache_toggle_summary">Pamięć podręczna ostatnio uruchomionych plików zaszyfrowanych lokalnie na urządzeniu w celu ponownego użycia po ponownym otwarciu</string>
|
||||
<string name="screen_settings_lru_cache_size">Rozmiar całkowity pamięci podręcznej</string>
|
||||
|
@ -41,6 +41,7 @@
|
||||
<string name="permission_message_export_file">Cryptomator precisa de permissão de acesso ao armazenamento para exportar arquivos</string>
|
||||
<string name="permission_message_upload_file">Cryptomator precisa de permissão de acesso ao armazenamento para fazer upload de arquivos</string>
|
||||
<string name="permission_message_share_file">Cryptomator precisa de permissão de acesso ao armazenamento para compartilhar arquivos</string>
|
||||
<string name="permission_revoked_re_request_permission">O Cryptomator perdeu a permissão para acessar este local. Por favor, selecione esta pasta novamente para restaurar sua permissão.</string>
|
||||
<string name="snack_bar_action_title_settings">Configurações</string>
|
||||
<string name="snack_bar_action_title_search">Buscar</string>
|
||||
<string name="snack_bar_action_title_search_previous">Anterior</string>
|
||||
@ -198,6 +199,7 @@
|
||||
<string name="screen_settings_keep_unlocked_while_editing_files">Manter desbloqueado</string>
|
||||
<string name="screen_settings_keep_unlocked_while_editing_files_summary">Mantenha cofres desbloqueados durante a edição de arquivos</string>
|
||||
<!-- ## screen: cloud settings -->
|
||||
<string name="screen_cloud_settings_onedrive_connections">Conexões OneDrive</string>
|
||||
<string name="screen_cloud_settings_webdav_connections">Conexões WebDAV</string>
|
||||
<string name="screen_cloud_settings_pcloud_connections">conexões pCloud</string>
|
||||
<string name="screen_cloud_settings_s3_connections">Conexões S3</string>
|
||||
@ -288,6 +290,10 @@
|
||||
<string name="dialog_app_is_obscured_info_title">O aplicativo está escuro</string>
|
||||
<string name="dialog_app_is_obscured_info_hint">Outro aplicativo está exibindo algo no topo do Cryptomator (por exemplo, um filtro de luz azul ou aplicativo de modo noturno). Por razões de segurança, o Cryptomator está desativado.\n\n<a href="https://docs.cryptomator.org/en/1.5/android/settings/#block-app-when-obscured">Como ativar o Cryptomator</a></string>
|
||||
<string name="dialog_app_is_obscured_info_neutral_button">Fechar</string>
|
||||
<string name="dialog_vaults_removed_during_migration_title">Por favor, readicione cofres para %1s nuvem</string>
|
||||
<string name="dialog_vaults_removed_during_migration_hint">Durante a migração para esta versão do aplicativo, precisamos remover os seguintes cofres do aplicativo:\n%2s \n\nEsses cofres não são removidos da nuvem, mas apenas deste aplicativo. Desculpe o incômodo e por favor adicione novamente estes cofres para continuar a trabalhar com eles.</string>
|
||||
<string name="dialog_vault_is_root_folder_of_cloud_title">O cofre é a pasta raiz da conexão com a nuvem</string>
|
||||
<string name="dialog_vault_is_root_folder_of_cloud_hint">Crie uma nova conexão com a nuvem onde você seleciona pelo menos a pasta principal desta pasta de cofre como o diretório raiz para adicionar este cofre.</string>
|
||||
<string name="dialog_disable_secure_screen_disclaimer_hint">Esta configuração é um recurso de segurança e impede que outros aplicativos induzam os usuários a fazer coisas que não devem fazer.\n\nAo desativar, você confirma que está <a href="https://docs.cryptomator.org/en/1.5/android/settings/#screen-security">ciente dos riscos</a>.</string>
|
||||
<string name="dialog_delete_cloud_connection_with_vaults_message">Tem certeza de que deseja remover esta conexão com a nuvem?</string>
|
||||
<string name="dialog_delete_cloud_connection_with_vaults_hint">Esta ação removerá a conexão com o provedor de nuvem e todos os cofres desta nuvem.</string>
|
||||
@ -367,6 +373,7 @@
|
||||
<string name="notification_open_writable_file_title">Abrir arquivo gravável</string>
|
||||
<string name="notification_open_writable_file_message">Cofre permanece desbloqueado até terminar de editar</string>
|
||||
<string name="notification_update_check_finished_latest">Versão mais recente instalada</string>
|
||||
<string name="notification_authenticating">Autenticando…</string>
|
||||
<string name="screen_settings_lru_cache">Cache</string>
|
||||
<string name="screen_settings_lru_cache_toggle_summary">Cache de arquivos acessados recentemente criptografados localmente no dispositivo para reutilização posterior quando reaberto</string>
|
||||
<string name="screen_settings_lru_cache_size">Tamanho total do cache</string>
|
||||
|
@ -40,6 +40,7 @@
|
||||
<!-- ## screen: settings -->
|
||||
<string name="screen_settings_section_general">Geral</string>
|
||||
<!-- ## screen: cloud settings -->
|
||||
<string name="screen_cloud_settings_onedrive_connections">Conexões do OneDrive</string>
|
||||
<!-- ## screen: licenses -->
|
||||
<!-- ## screen: authenticate cloud -->
|
||||
<!-- ## screen: insecure android version info -->
|
||||
@ -56,6 +57,7 @@
|
||||
<!-- ## date helper -->
|
||||
<!-- ## biometric authentication -->
|
||||
<!-- notification -->
|
||||
<string name="notification_authenticating">Autenticação…</string>
|
||||
<!-- lock timeout names -->
|
||||
<!-- cache size names -->
|
||||
<!-- screen scheme mode names -->
|
||||
|
@ -201,6 +201,7 @@
|
||||
<string name="screen_settings_keep_unlocked_while_editing_files">Держать разблокированным</string>
|
||||
<string name="screen_settings_keep_unlocked_while_editing_files_summary">Не блокировать при редактировании</string>
|
||||
<!-- ## screen: cloud settings -->
|
||||
<string name="screen_cloud_settings_onedrive_connections">Подключения OneDrive</string>
|
||||
<string name="screen_cloud_settings_webdav_connections">Подключения WebDAV</string>
|
||||
<string name="screen_cloud_settings_pcloud_connections">Подключения pCloud</string>
|
||||
<string name="screen_cloud_settings_s3_connections">Подключения S3</string>
|
||||
@ -374,6 +375,7 @@
|
||||
<string name="notification_open_writable_file_title">Открыть файл, доступный для записи</string>
|
||||
<string name="notification_open_writable_file_message">Хранилище разблокировано до завершения редактирования</string>
|
||||
<string name="notification_update_check_finished_latest">Установлена новейшая версия</string>
|
||||
<string name="notification_authenticating">Аутентификация…</string>
|
||||
<string name="screen_settings_lru_cache">Кэш</string>
|
||||
<string name="screen_settings_lru_cache_toggle_summary">Кэшировать зашифрованные локально на устройстве файлы, к которым недавно был доступ, для последующего использования при повторном открытии</string>
|
||||
<string name="screen_settings_lru_cache_size">Общий размер кэша</string>
|
||||
|
@ -3,7 +3,7 @@
|
||||
<!-- # app -->
|
||||
<string name="share_with_label">Zašifrovať</string>
|
||||
<!-- # error messages -->
|
||||
<string name="error_generic">Vyskytol sa problém</string>
|
||||
<string name="error_generic">Vyskytla sa chyba</string>
|
||||
<string name="error_authentication_failed">Neúspešná autentikácia</string>
|
||||
<string name="error_authentication_failed_re_authenticate">Neúspešná autentikácia, prosím prihláste sa pomocou %1$s</string>
|
||||
<string name="error_no_network_connection">Chýba sieťové pripojenie</string>
|
||||
@ -16,11 +16,11 @@
|
||||
<string name="error_cloud_already_exists">Cloud už existuje.</string>
|
||||
<string name="error_activity_not_found">Prosím stiahnite takú aplikáciu ktorá dokáže otvoriť tento súbor.</string>
|
||||
<string name="error_server_not_found">Server nenájdený.</string>
|
||||
<string name="error_device_policy_manager_not_found">Prosím otvorte nastavenia zaridenia a nastavte uzamykanie obrazovky ručne</string>
|
||||
<string name="error_device_policy_manager_not_found">Prosím otvorte nastavenia zariadenia a nastavte uzamykanie obrazovky ručne</string>
|
||||
<string name="error_export_illegal_file_name">Export neúspešný. Pokúste sa odstrániť špeciálne znaky z názvov súborov a exportujte znovu.</string>
|
||||
<string name="error_name_contains_invalid_characters">Nemôže obsahovať špeciálne znaky.</string>
|
||||
<string name="error_names_contains_invalid_characters">Názvy súborov nesmú obsahovať špecálne znaky.</string>
|
||||
<string name="error_vault_name_contains_invalid_characters">Meno trezoru nemôže obsahovať špecálne znaky.</string>
|
||||
<string name="error_names_contains_invalid_characters">Názvy súborov nesmú obsahovať špeciálne znaky.</string>
|
||||
<string name="error_vault_name_contains_invalid_characters">Meno trezoru nemôže obsahovať špeciálne znaky.</string>
|
||||
<string name="error_general_update">Kontrola aktualizácie zlyhala. Vyskytol sa problém.</string>
|
||||
<string name="error_hash_mismatch_update">Kontrola aktualizácie zlyhala. Vypočítaný hash nekorešponduje s nahratým súborom</string>
|
||||
<string name="error_update_no_internet">Kontrola aktualizácie zlyhala. Neexistuje spojenie s internetom.</string>
|
||||
@ -31,7 +31,7 @@
|
||||
<string name="error_vault_key_invalid">%1$s nezodpovedá s týmto %2$s</string>
|
||||
<string name="error_vault_config_loading">Chyba počas zavádzania konfigurácie trezora</string>
|
||||
<string name="error_file_not_found_after_opening_using_3party">Lokálny súbor nie je viac prezentovaný po prepnutí naspäť do Cryptomator-a. Možné zmeny sa neprenesú späť do cloudu.</string>
|
||||
<string name="error_no_such_bucket">Žiadna taká nádoba</string>
|
||||
<string name="error_no_such_bucket">Bucket neexistuje</string>
|
||||
<string name="error_masterkey_location_not_supported">Voliteľnost umiestnenia hlavného kľúča zatiaľ nie je podporovaná</string>
|
||||
<!-- # clouds -->
|
||||
<!-- ## cloud names -->
|
||||
@ -59,7 +59,7 @@
|
||||
<string name="screen_vault_list_action_create_new_vault">Vytvoriť nový trezor</string>
|
||||
<string name="screen_vault_list_action_add_existing_vault">Pridať existujúci trezor</string>
|
||||
<string name="screen_vault_list_vault_action_delete">Odstrániť</string>
|
||||
<string name="screen_vault_list_vault_creation_hint">Sem kliknúť pre vytvorenie nového trezoru</string>
|
||||
<string name="screen_vault_list_vault_creation_hint">Kliknite sem pre vytvorenie nového trezora</string>
|
||||
<string name="screen_vault_list_change_password_successful">Heslo úspešne zmenené</string>
|
||||
<!-- # screen: file browser -->
|
||||
<string name="screen_file_browser_default_title">Trezor</string>
|
||||
@ -132,13 +132,13 @@
|
||||
<string name="screen_s3_settings_display_name_label">Zobrazované meno</string>
|
||||
<string name="screen_s3_settings_access_key_label">Prístupový kľúč</string>
|
||||
<string name="screen_s3_settings_secret_key_label">Tajný kľúč</string>
|
||||
<string name="screen_s3_settings_bucket_label">Existujúce vedro</string>
|
||||
<string name="screen_s3_settings_bucket_label">Existujúci bucket</string>
|
||||
<string name="screen_s3_settings_endpoint_label">Koncový bod</string>
|
||||
<string name="screen_s3_settings_region_label">Región</string>
|
||||
<string name="screen_s3_settings_msg_display_name_not_empty">Zobrazované meno nemôže byť prázdne</string>
|
||||
<string name="screen_s3_settings_msg_access_key_not_empty">Prístupový kľúč nemôže byť prázdny</string>
|
||||
<string name="screen_s3_settings_msg_secret_key_not_empty">Tajný kľúč nesmie byť prázdny</string>
|
||||
<string name="screen_s3_settings_msg_bucket_not_empty">Vedro nemôže byť prázdne</string>
|
||||
<string name="screen_s3_settings_msg_bucket_not_empty">Bucket nemôže byť prázdny</string>
|
||||
<string name="screen_s3_settings_msg_endpoint_and_region_not_empty">Koncový bod alebo región nemôže byť prázdny</string>
|
||||
<!-- ## screen: enter vault name -->
|
||||
<string name="screen_enter_vault_name_msg_name_empty">Meno trezora nemôže byť prázdne.</string>
|
||||
@ -163,8 +163,8 @@
|
||||
<string name="screen_settings_confirm_face_unlock">Potvrďte odomykanie tvárou (ak je dostupné)</string>
|
||||
<string name="screen_settings_block_app_when_obscured">Blokovanie aplikácie pri zatemnení</string>
|
||||
<string name="screen_settings_block_app_when_obscured_summary">Blokovanie zachytávania vstupu a zobrazovania falošného uživateľského rozhrania</string>
|
||||
<string name="screen_settings_secure_screen">Blokovanie snímkov obrazovky</string>
|
||||
<string name="screen_settings_secure_screen_summary">Blokovanie snímkov obrazovky v nedávnom zozname a vo vnútri aplikácie</string>
|
||||
<string name="screen_settings_secure_screen">Blokovanie vytvorenia snímkov obrazovky</string>
|
||||
<string name="screen_settings_secure_screen_summary">Zakázať vytváranie snímkov obrazovky v tejto aplikácii a v zozname bežiacich aplikácií</string>
|
||||
<string name="screen_settings_section_search">Hľadať</string>
|
||||
<string name="screen_settings_live_search">Živé vyhľadávanie</string>
|
||||
<string name="screen_settings_live_search_summary">Aktualizovať výsledky hľadania počas zadávania otázky</string>
|
||||
@ -201,6 +201,7 @@
|
||||
<string name="screen_settings_keep_unlocked_while_editing_files">Držať odomknuté</string>
|
||||
<string name="screen_settings_keep_unlocked_while_editing_files_summary">Držať trezory odomknuté pokiaľ sa editujú súbory</string>
|
||||
<!-- ## screen: cloud settings -->
|
||||
<string name="screen_cloud_settings_onedrive_connections">OneDrive pripojenia</string>
|
||||
<string name="screen_cloud_settings_webdav_connections">WebDAV pripojenia</string>
|
||||
<string name="screen_cloud_settings_pcloud_connections">pCloud pripojenia</string>
|
||||
<string name="screen_cloud_settings_s3_connections">S3 pripojenia</string>
|
||||
@ -292,7 +293,7 @@
|
||||
<string name="dialog_app_is_obscured_info_hint">Ďalšia aplikácia zobrazuje niečo na vrchu Cryptomator-a (ako napr. modré svetlo alebo nočný režim). Z bezpečnostných dôvodov je Cryptomator zakázaný.\n\n<a href="https://docs.cryptomator.org/en/1.5/android/settings/#block-app-when-obscured">Ako povoliť Cryptomator</a></string>
|
||||
<string name="dialog_app_is_obscured_info_neutral_button">Zavrieť</string>
|
||||
<string name="dialog_vaults_removed_during_migration_title">Prosím zadajte znovu trezory pre %1s cloud</string>
|
||||
<string name="dialog_vaults_removed_during_migration_hint">Počas migrácie na túto verziu aplikácie potrebujeme odstrániť nasledujúce trezory z aplikácie:\n%2s \n\n Tieto trezory nebudú odstránené z clodu ale len z tejto aplikácie. Prepáčte za nepohodlie a prosím znovu zadajte trezory pre pokračovanie ich používania.</string>
|
||||
<string name="dialog_vaults_removed_during_migration_hint">Počas migrácie na túto verziu aplikácie potrebujeme odstrániť nasledujúce trezory z aplikácie:\n%2s \n\n Tieto trezory nebudú odstránené z cloudu, ale len z tejto aplikácie. Prepáčte za nepohodlie a prosím znovu zadajte trezory pre pokračovanie ich používania.</string>
|
||||
<string name="dialog_vault_is_root_folder_of_cloud_title">Trezor je koreňovým adresárom cloudového spojenia</string>
|
||||
<string name="dialog_vault_is_root_folder_of_cloud_hint">Vytvorte nové cloudové pripojenie kde vyberiete minimálne nadradený adresár tohto trezora ako koreňový adresár pre pridanie tohot trezora.</string>
|
||||
<string name="dialog_disable_secure_screen_disclaimer_hint">Toto nastavenie je bezpečnostná vlastnosť a zabraňuje ostatným aplikáciám klamať užívateľov robiť veci čo nechcú robiť.\n\nVypnutím súhlasíte s tým že<a href="https://docs.cryptomator.org/en/1.5/android/settings/#screen-security">ste si vedomí rizika</a>.</string>
|
||||
@ -304,11 +305,11 @@
|
||||
<string name="dialog_confirm_delete_folder_message">Toto zmaže obsah celého adresára. Ste si istý že chcete zmazať tento adresár?</string>
|
||||
<string name="dialog_biometric_auth_key_invalidated_title">Funkcia biometrickej autentikácie je deaktivovaná</string>
|
||||
<string name="dialog_biometric_auth_key_invalidated_message">Pretože kľúč bol zneplatnený, funkcia biometrickej autentikácie bola deaktivovaná. Pre opätovné povolenie, otvorte nastavenia Cryptomator-a.</string>
|
||||
<string name="dialog_enter_license_title">Poskytnite platnú licenciu</string>
|
||||
<string name="dialog_enter_license_title">Zadajte platný licenčný kľúč</string>
|
||||
<string name="dialog_enter_license_content">Detekovali sme že máte nainštalovaný Cryptomator bez použitia Google Play Store. Poskytnite platnú licenciu, ktorá bola zakúpená v <a href="https://cryptomator.org/android/">https://cryptomator.org/android/</a></string>
|
||||
<string name="dialog_enter_license_not_valid_content">Poskytnutá licencia nie je platná. Uistite sa či bola zadaná korektne.</string>
|
||||
<string name="dialog_enter_license_no_content">Licencia neposkytnutá. Prosím zadajte platnú licenciu.</string>
|
||||
<string name="dialog_enter_license_decline_button">Výstup</string>
|
||||
<string name="dialog_enter_license_decline_button">Ukončiť</string>
|
||||
<string name="dialog_license_confirmation_title">Potvrdenie licencie</string>
|
||||
<string name="dialog_license_confirmation_message">Ďakujeme %1$s za poskytnutie Vašej platnej licencie.</string>
|
||||
<string name="dialog_update_available_title">Aktualizácia k dispozícii</string>
|
||||
@ -374,6 +375,7 @@
|
||||
<string name="notification_open_writable_file_title">Otvoriť zapisovateľný súbor</string>
|
||||
<string name="notification_open_writable_file_message">Trezor zostáva odomknutý pokial nie je ukončené editovanie</string>
|
||||
<string name="notification_update_check_finished_latest">Najnovšia verzia inštalovaná</string>
|
||||
<string name="notification_authenticating">Autentikujem…</string>
|
||||
<string name="screen_settings_lru_cache">Vyrovnávacia pamäť</string>
|
||||
<string name="screen_settings_lru_cache_toggle_summary">Nedávno sprístupnené súbory vyrovnávaciej pamäťe lokálne zašifrované na zariadení pre neskoršie použitie pri novom otvoreni</string>
|
||||
<string name="screen_settings_lru_cache_size">Celková veľkosť vyrovovnávacej pamäte</string>
|
||||
|
@ -6,9 +6,11 @@
|
||||
<!-- ## cloud names -->
|
||||
<!-- # permission -->
|
||||
<!-- ## permission messages -->
|
||||
<string name="snack_bar_action_title_search_next">ถัดไป</string>
|
||||
<!-- # screens -->
|
||||
<!-- # screen: vault list -->
|
||||
<!-- # screen: file browser -->
|
||||
<string name="screen_file_browser_default_title">Vault</string>
|
||||
<!-- ## screen: text editor -->
|
||||
<!-- ## screen: share files -->
|
||||
<!-- ## screen: choose cloud service -->
|
||||
@ -17,13 +19,19 @@
|
||||
<!-- ## screen: s3 settings -->
|
||||
<!-- ## screen: enter vault name -->
|
||||
<!-- ## screen: set password -->
|
||||
<string name="screen_set_password_button_text">เสร็จสิ้น</string>
|
||||
<!-- ## screen: settings -->
|
||||
<!-- ## screen: cloud settings -->
|
||||
<!-- ## screen: licenses -->
|
||||
<!-- ## screen: authenticate cloud -->
|
||||
<!-- ## screen: insecure android version info -->
|
||||
<!-- # dialogs -->
|
||||
<string name="dialog_button_cancel">ยกเลิก</string>
|
||||
<string name="dialog_enter_password_positive_button">ปลดล็อก</string>
|
||||
<!-- Vault not found -->
|
||||
<string name="dialog_lock_vault">ล็อก</string>
|
||||
<string name="dialog_app_is_obscured_info_neutral_button">ปิด</string>
|
||||
<string name="dialog_sym_link_back_button">ย้อนกลับ</string>
|
||||
<!-- # error reports -->
|
||||
<!-- # misc -->
|
||||
<!-- ## file size helper -->
|
||||
|
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