Skip to content

Commit e4775e3

Browse files
committed
Merge branch 'release/1.11.1'
2 parents 0af633c + ffee91b commit e4775e3

File tree

34 files changed

+441
-234
lines changed

34 files changed

+441
-234
lines changed

README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ Cryptomator for Android is currently available in the following distribution ch
1313
2. [Using Cryptomator's Website](https://cryptomator.org/android/)
1414
3. [Using Cryptomator's F-Droid Repository](https://cryptomator.org/android/)
1515
4. [Using F-Droid's Main Repository](https://f-droid.org/en/packages/org.cryptomator.lite)
16-
5. Building from source using Gradle (instructions below)
16+
5. [Using Accrescent](https://accrescent.app/app/org.cryptomator)
17+
6. Building from source using Gradle (instructions below)
1718

1819
## Building
1920

build.gradle

+2-2
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ def getVersionCode = { ->
3636
allprojects {
3737
ext {
3838
androidApplicationId = 'org.cryptomator'
39-
androidVersionCode = getVersionCode()
40-
androidVersionName = '1.11.0'
39+
androidVersionCode = 2991 // must be getVersionCode(). only at release tag set the actual value
40+
androidVersionName = '1.11.1'
4141
}
4242
repositories {
4343
mavenCentral()

data/src/main/java/org/cryptomator/data/repository/HubRepositoryImpl.java

+1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ private Interceptor httpLoggingInterceptor(Context context) {
5858
public String getVaultKeyJwe(UnverifiedHubVaultConfig unverifiedHubVaultConfig, String accessToken) throws BackendException {
5959
var request = new Request.Builder().get() //
6060
.header("Authorization", "Bearer " + accessToken) //
61+
.header("deviceId", getHubDeviceCryptor().getDeviceId()) //
6162
.url(unverifiedHubVaultConfig.getApiBaseUrl() + "vaults/" + unverifiedHubVaultConfig.vaultId() + "/access-token") //
6263
.build();
6364
try (var response = getHttpClient().newCall(request).execute()) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package org.cryptomator.domain.exception;
2+
3+
public class IllegalFileNameException extends BackendException {
4+
5+
public IllegalFileNameException() {
6+
}
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package org.cryptomator.domain.usecases;
2+
3+
import android.content.Context;
4+
import android.net.Uri;
5+
6+
import org.cryptomator.domain.exception.BackendException;
7+
import org.cryptomator.domain.exception.FatalBackendException;
8+
import org.cryptomator.generator.Parameter;
9+
import org.cryptomator.generator.UseCase;
10+
11+
import java.io.FileNotFoundException;
12+
import java.io.IOException;
13+
import java.io.InputStream;
14+
import java.security.DigestInputStream;
15+
import java.security.MessageDigest;
16+
import java.security.NoSuchAlgorithmException;
17+
18+
@UseCase
19+
public class CalculateFileHash {
20+
21+
private final Context context;
22+
private final Uri uri;
23+
24+
CalculateFileHash(final Context context, @Parameter Uri uri) {
25+
this.context = context;
26+
this.uri = uri;
27+
}
28+
29+
public byte[] execute() throws BackendException, FileNotFoundException {
30+
try {
31+
MessageDigest digest = MessageDigest.getInstance("MD5");
32+
try (InputStream inputStream = context.getContentResolver().openInputStream(uri); //
33+
DigestInputStream dis = new DigestInputStream(inputStream, digest)) {
34+
byte[] buffer = new byte[4096];
35+
while (dis.read(buffer) != -1) {
36+
}
37+
return digest.digest();
38+
} catch (IOException e) {
39+
throw new FatalBackendException(e);
40+
}
41+
} catch (NoSuchAlgorithmException e) {
42+
throw new RuntimeException(e);
43+
}
44+
}
45+
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package org.cryptomator.domain.usecases;
2+
3+
import android.content.ContentResolver;
4+
import android.content.Context;
5+
import android.net.Uri;
6+
import android.provider.DocumentsContract;
7+
8+
import org.cryptomator.domain.CloudFile;
9+
import org.cryptomator.domain.exception.BackendException;
10+
import org.cryptomator.domain.exception.FatalBackendException;
11+
import org.cryptomator.domain.exception.IllegalFileNameException;
12+
import org.cryptomator.domain.exception.NoSuchCloudFileException;
13+
import org.cryptomator.generator.Parameter;
14+
import org.cryptomator.generator.UseCase;
15+
import org.cryptomator.util.file.MimeType;
16+
import org.cryptomator.util.file.MimeTypes;
17+
18+
import java.io.FileNotFoundException;
19+
import java.util.ArrayList;
20+
import java.util.List;
21+
22+
@UseCase
23+
public class PrepareDownloadFiles {
24+
25+
private final Context context;
26+
private final MimeTypes mimeTypes;
27+
private final List<CloudFile> filesToExport;
28+
private final Uri parentUri;
29+
private final ContentResolver contentResolver;
30+
private final CloudNodeRecursiveListing cloudNodeRecursiveListing;
31+
32+
private List<DownloadFile> downloadFiles = new ArrayList<>();
33+
34+
PrepareDownloadFiles(Context context, MimeTypes mimeTypes, @Parameter List<CloudFile> filesToExport, @Parameter Uri parentUri, @Parameter CloudNodeRecursiveListing cloudNodeRecursiveListing) {
35+
this.context = context;
36+
this.mimeTypes = mimeTypes;
37+
this.filesToExport = filesToExport;
38+
this.parentUri = parentUri;
39+
this.contentResolver = context.getContentResolver();
40+
this.cloudNodeRecursiveListing = cloudNodeRecursiveListing;
41+
}
42+
43+
List<DownloadFile> execute() throws BackendException, FileNotFoundException {
44+
downloadFiles = prepareFilesForExport(filesToExport, parentUri);
45+
for (CloudFolderRecursiveListing folderRecursiveListing : cloudNodeRecursiveListing.getFoldersContent()) {
46+
prepareFolderContentForExport(folderRecursiveListing, parentUri);
47+
}
48+
return downloadFiles;
49+
}
50+
51+
private List<DownloadFile> prepareFilesForExport(List<CloudFile> filesToExport, Uri parentUri) throws NoSuchCloudFileException, FileNotFoundException, IllegalFileNameException {
52+
List<DownloadFile> downloadFiles = new ArrayList<>();
53+
for (CloudFile cloudFile : filesToExport) {
54+
DownloadFile downloadFile = createDownloadFile(cloudFile, parentUri);
55+
downloadFiles.add(downloadFile);
56+
}
57+
return downloadFiles;
58+
}
59+
60+
private void prepareFolderContentForExport(CloudFolderRecursiveListing folderRecursiveListing, Uri parentUri) throws FileNotFoundException, NoSuchCloudFileException, IllegalFileNameException {
61+
Uri createdFolder = createFolder(parentUri, folderRecursiveListing.getParent().getName());
62+
if (createdFolder != null) {
63+
downloadFiles.addAll(prepareFilesForExport(folderRecursiveListing.getFiles(), createdFolder));
64+
for (CloudFolderRecursiveListing childFolder : folderRecursiveListing.getFolders()) {
65+
prepareFolderContentForExport(childFolder, createdFolder);
66+
}
67+
} else {
68+
throw new FatalBackendException("Failed to create parent folder for export");
69+
}
70+
}
71+
72+
private Uri createFolder(Uri parentUri, String folderName) throws NoSuchCloudFileException {
73+
try {
74+
return DocumentsContract.createDocument(contentResolver, parentUri, DocumentsContract.Document.MIME_TYPE_DIR, folderName);
75+
} catch (FileNotFoundException e) {
76+
throw new NoSuchCloudFileException("Creating folder failed");
77+
}
78+
}
79+
80+
private DownloadFile createDownloadFile(CloudFile file, Uri documentUri) throws NoSuchCloudFileException, IllegalFileNameException {
81+
try {
82+
return new DownloadFile.Builder().setDownloadFile(file).setDataSink(contentResolver.openOutputStream(createNewDocumentUri(documentUri, file.getName()))).build();
83+
} catch (FileNotFoundException e) {
84+
throw new NoSuchCloudFileException(file.getName());
85+
}
86+
}
87+
88+
private Uri createNewDocumentUri(Uri parentUri, String fileName) throws IllegalFileNameException, NoSuchCloudFileException {
89+
MimeType mimeType = mimeTypes.fromFilename(fileName);
90+
if (mimeType == null) {
91+
mimeType = MimeType.APPLICATION_OCTET_STREAM;
92+
}
93+
try {
94+
Uri documentUri = DocumentsContract.createDocument(context.getContentResolver(), parentUri, mimeType.toString(), fileName);
95+
if (documentUri == null) {
96+
throw new IllegalFileNameException();
97+
}
98+
return documentUri;
99+
} catch (FileNotFoundException e) {
100+
throw new NoSuchCloudFileException(fileName);
101+
}
102+
}
103+
104+
}

fastlane/release-notes-de.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
- Hub-Unterstützung hinzugefügt
1+
- Cryptomator Hub erhält nun die Geräte-ID während der Tresorschlüsselabfrage
2+
- Potenzieller Absturz beim Öffnen großer Dateien und Exportieren großer Ordner behoben

fastlane/release-notes-en.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
- Add Hub support
1+
- Cryptomator Hub now receives the Device ID during vault key retrieval
2+
- Fixed potential crash when handling large files and exporting large folders

fastlane/release-notes.html

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
<ul>
2-
<li>Add Hub support</li>
2+
<li>Cryptomator Hub now receives the Device ID during vault key retrieval</li>
3+
<li>Fixed potential crash when handling large files and exporting large folders</li>
34
</ul>

presentation/src/main/java/org/cryptomator/presentation/exception/ExceptionHandlers.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import org.cryptomator.cryptolib.api.InvalidPassphraseException
66
import org.cryptomator.domain.di.PerView
77
import org.cryptomator.domain.exception.CloudAlreadyExistsException
88
import org.cryptomator.domain.exception.CloudNodeAlreadyExistsException
9+
import org.cryptomator.domain.exception.IllegalFileNameException
910
import org.cryptomator.domain.exception.NetworkConnectionException
1011
import org.cryptomator.domain.exception.NoSuchBucketException
1112
import org.cryptomator.domain.exception.NoSuchCloudFileException
@@ -39,7 +40,7 @@ import timber.log.Timber
3940
class ExceptionHandlers @Inject constructor(private val context: Context, defaultExceptionHandler: DefaultExceptionHandler) : Iterable<ExceptionHandler?> {
4041

4142
private val exceptionHandlers: MutableList<ExceptionHandler> = ArrayList()
42-
private val defaultExceptionHandler: ExceptionHandler
43+
private val defaultExceptionHandler: ExceptionHandler = defaultExceptionHandler
4344

4445
private fun setupHandlers() {
4546
staticHandler(AuthenticationException::class.java, R.string.error_authentication_failed)
@@ -122,7 +123,6 @@ class ExceptionHandlers @Inject constructor(private val context: Context, defaul
122123
}
123124

124125
init {
125-
this.defaultExceptionHandler = defaultExceptionHandler
126126
setupHandlers()
127127
}
128128
}

presentation/src/main/java/org/cryptomator/presentation/exception/IllegalFileNameException.kt

-3
This file was deleted.

0 commit comments

Comments
 (0)