diff --git a/buildsystem/dependencies.gradle b/buildsystem/dependencies.gradle index 4c853160..3d4338a5 100644 --- a/buildsystem/dependencies.gradle +++ b/buildsystem/dependencies.gradle @@ -8,8 +8,8 @@ allprojects { ext { androidBuildToolsVersion = "30.0.2" androidMinSdkVersion = 24 - androidTargetSdkVersion = 29 - androidCompileSdkVersion = 29 + androidTargetSdkVersion = 30 + androidCompileSdkVersion = 30 // android and java libs androidVersion = '4.1.1.4' diff --git a/data/src/main/java/org/cryptomator/data/cloud/crypto/CryptoCloud.java b/data/src/main/java/org/cryptomator/data/cloud/crypto/CryptoCloud.java index ca6dd52e..eeadb135 100644 --- a/data/src/main/java/org/cryptomator/data/cloud/crypto/CryptoCloud.java +++ b/data/src/main/java/org/cryptomator/data/cloud/crypto/CryptoCloud.java @@ -31,11 +31,6 @@ public class CryptoCloud implements Cloud { return vault.equals(cloud.vault); } - @Override - public boolean predefined() { - return false; - } - @Override public boolean persistent() { return false; diff --git a/data/src/main/java/org/cryptomator/data/cloud/local/storageaccessframework/DocumentIdCache.kt b/data/src/main/java/org/cryptomator/data/cloud/local/DocumentIdCache.kt similarity index 93% rename from data/src/main/java/org/cryptomator/data/cloud/local/storageaccessframework/DocumentIdCache.kt rename to data/src/main/java/org/cryptomator/data/cloud/local/DocumentIdCache.kt index b4c9698d..db3abc87 100644 --- a/data/src/main/java/org/cryptomator/data/cloud/local/storageaccessframework/DocumentIdCache.kt +++ b/data/src/main/java/org/cryptomator/data/cloud/local/DocumentIdCache.kt @@ -1,4 +1,4 @@ -package org.cryptomator.data.cloud.local.storageaccessframework +package org.cryptomator.data.cloud.local import android.util.LruCache import org.cryptomator.domain.CloudFolder diff --git a/data/src/main/java/org/cryptomator/data/cloud/local/storageaccessframework/LocalStorageAccessFile.kt b/data/src/main/java/org/cryptomator/data/cloud/local/LocalStorageAccessFile.kt similarity index 93% rename from data/src/main/java/org/cryptomator/data/cloud/local/storageaccessframework/LocalStorageAccessFile.kt rename to data/src/main/java/org/cryptomator/data/cloud/local/LocalStorageAccessFile.kt index cffbb02c..c6bc7971 100644 --- a/data/src/main/java/org/cryptomator/data/cloud/local/storageaccessframework/LocalStorageAccessFile.kt +++ b/data/src/main/java/org/cryptomator/data/cloud/local/LocalStorageAccessFile.kt @@ -1,4 +1,4 @@ -package org.cryptomator.data.cloud.local.storageaccessframework +package org.cryptomator.data.cloud.local import android.net.Uri import org.cryptomator.domain.Cloud diff --git a/data/src/main/java/org/cryptomator/data/cloud/local/storageaccessframework/LocalStorageAccessFolder.kt b/data/src/main/java/org/cryptomator/data/cloud/local/LocalStorageAccessFolder.kt similarity index 94% rename from data/src/main/java/org/cryptomator/data/cloud/local/storageaccessframework/LocalStorageAccessFolder.kt rename to data/src/main/java/org/cryptomator/data/cloud/local/LocalStorageAccessFolder.kt index af3d8027..d147d6b0 100644 --- a/data/src/main/java/org/cryptomator/data/cloud/local/storageaccessframework/LocalStorageAccessFolder.kt +++ b/data/src/main/java/org/cryptomator/data/cloud/local/LocalStorageAccessFolder.kt @@ -1,4 +1,4 @@ -package org.cryptomator.data.cloud.local.storageaccessframework +package org.cryptomator.data.cloud.local import android.net.Uri import org.cryptomator.domain.Cloud diff --git a/data/src/main/java/org/cryptomator/data/cloud/local/storageaccessframework/LocalStorageAccessFrameworkContentRepository.kt b/data/src/main/java/org/cryptomator/data/cloud/local/LocalStorageAccessFrameworkContentRepository.kt similarity index 98% rename from data/src/main/java/org/cryptomator/data/cloud/local/storageaccessframework/LocalStorageAccessFrameworkContentRepository.kt rename to data/src/main/java/org/cryptomator/data/cloud/local/LocalStorageAccessFrameworkContentRepository.kt index 0f21ca53..76dc5456 100644 --- a/data/src/main/java/org/cryptomator/data/cloud/local/storageaccessframework/LocalStorageAccessFrameworkContentRepository.kt +++ b/data/src/main/java/org/cryptomator/data/cloud/local/LocalStorageAccessFrameworkContentRepository.kt @@ -1,4 +1,4 @@ -package org.cryptomator.data.cloud.local.storageaccessframework +package org.cryptomator.data.cloud.local import android.content.Context import org.cryptomator.domain.LocalStorageCloud diff --git a/data/src/main/java/org/cryptomator/data/cloud/local/storageaccessframework/LocalStorageAccessFrameworkImpl.kt b/data/src/main/java/org/cryptomator/data/cloud/local/LocalStorageAccessFrameworkImpl.kt similarity index 94% rename from data/src/main/java/org/cryptomator/data/cloud/local/storageaccessframework/LocalStorageAccessFrameworkImpl.kt rename to data/src/main/java/org/cryptomator/data/cloud/local/LocalStorageAccessFrameworkImpl.kt index bf93abcd..904f8b15 100644 --- a/data/src/main/java/org/cryptomator/data/cloud/local/storageaccessframework/LocalStorageAccessFrameworkImpl.kt +++ b/data/src/main/java/org/cryptomator/data/cloud/local/LocalStorageAccessFrameworkImpl.kt @@ -1,4 +1,4 @@ -package org.cryptomator.data.cloud.local.storageaccessframework +package org.cryptomator.data.cloud.local import android.content.ContentResolver import android.content.Context @@ -7,10 +7,10 @@ import android.net.Uri import android.os.Build import android.provider.DocumentsContract import androidx.documentfile.provider.DocumentFile -import org.cryptomator.data.cloud.local.storageaccessframework.LocalStorageAccessFrameworkNodeFactory.file -import org.cryptomator.data.cloud.local.storageaccessframework.LocalStorageAccessFrameworkNodeFactory.folder -import org.cryptomator.data.cloud.local.storageaccessframework.LocalStorageAccessFrameworkNodeFactory.from -import org.cryptomator.data.cloud.local.storageaccessframework.LocalStorageAccessFrameworkNodeFactory.getNodePath +import org.cryptomator.data.cloud.local.LocalStorageAccessFrameworkNodeFactory.file +import org.cryptomator.data.cloud.local.LocalStorageAccessFrameworkNodeFactory.folder +import org.cryptomator.data.cloud.local.LocalStorageAccessFrameworkNodeFactory.from +import org.cryptomator.data.cloud.local.LocalStorageAccessFrameworkNodeFactory.getNodePath import org.cryptomator.data.util.CopyStream import org.cryptomator.data.util.TransferredBytesAwareInputStream import org.cryptomator.data.util.TransferredBytesAwareOutputStream @@ -243,7 +243,8 @@ internal class LocalStorageAccessFrameworkImpl(context: Context, private val mim private fun rename(source: LocalStorageAccessNode, name: String): LocalStorageAccessNode { source.parent?.let { parent -> var newUri = try { - DocumentsContract.renameDocument(contentResolver(), source.uri, name) + requireNotNull(source.uri) + DocumentsContract.renameDocument(contentResolver(), source.uri!!, name) } catch (e: FileNotFoundException) { /* Bug in Android 9 see #460 TLDR; In this renameDocument-method, Android 9 throws a `FileNotFoundException` although the file exists and is also renamed. */ @@ -336,11 +337,13 @@ internal class LocalStorageAccessFrameworkImpl(context: Context, private val mim private fun createNewDocumentSupplier(file: LocalStorageAccessFile): Supplier { return Supplier { - val mimeType = if (mimeTypes.fromFilename(file.name) == null) MimeType.APPLICATION_OCTET_STREAM else mimeTypes.fromFilename(file.name) - try { - DocumentsContract.createDocument(contentResolver(), file.parent.uri, mimeType.toString(), file.name) // FIXME - } catch (e: FileNotFoundException) { - null + file.parent.uri?.let { + val mimeType = if (mimeTypes.fromFilename(file.name) == null) MimeType.APPLICATION_OCTET_STREAM else mimeTypes.fromFilename(file.name) + try { + DocumentsContract.createDocument(contentResolver(), it, mimeType.toString(), file.name) // FIXME + } catch (e: FileNotFoundException) { + null + } } } } @@ -372,7 +375,7 @@ internal class LocalStorageAccessFrameworkImpl(context: Context, private val mim fun delete(node: LocalStorageAccessNode) { requireNotNull(node.uri) try { - DocumentsContract.deleteDocument(contentResolver(), node.uri) + DocumentsContract.deleteDocument(contentResolver(), node.uri!!) } catch (e: FileNotFoundException) { throw NoSuchCloudFileException(node.name) } diff --git a/data/src/main/java/org/cryptomator/data/cloud/local/storageaccessframework/LocalStorageAccessFrameworkNodeFactory.kt b/data/src/main/java/org/cryptomator/data/cloud/local/LocalStorageAccessFrameworkNodeFactory.kt similarity index 98% rename from data/src/main/java/org/cryptomator/data/cloud/local/storageaccessframework/LocalStorageAccessFrameworkNodeFactory.kt rename to data/src/main/java/org/cryptomator/data/cloud/local/LocalStorageAccessFrameworkNodeFactory.kt index f352c4bb..d65b3761 100644 --- a/data/src/main/java/org/cryptomator/data/cloud/local/storageaccessframework/LocalStorageAccessFrameworkNodeFactory.kt +++ b/data/src/main/java/org/cryptomator/data/cloud/local/LocalStorageAccessFrameworkNodeFactory.kt @@ -1,4 +1,4 @@ -package org.cryptomator.data.cloud.local.storageaccessframework +package org.cryptomator.data.cloud.local import android.database.Cursor import android.provider.DocumentsContract diff --git a/data/src/main/java/org/cryptomator/data/cloud/local/storageaccessframework/LocalStorageAccessNode.kt b/data/src/main/java/org/cryptomator/data/cloud/local/LocalStorageAccessNode.kt similarity index 76% rename from data/src/main/java/org/cryptomator/data/cloud/local/storageaccessframework/LocalStorageAccessNode.kt rename to data/src/main/java/org/cryptomator/data/cloud/local/LocalStorageAccessNode.kt index 12ffd6df..aa6fa800 100644 --- a/data/src/main/java/org/cryptomator/data/cloud/local/storageaccessframework/LocalStorageAccessNode.kt +++ b/data/src/main/java/org/cryptomator/data/cloud/local/LocalStorageAccessNode.kt @@ -1,4 +1,4 @@ -package org.cryptomator.data.cloud.local.storageaccessframework +package org.cryptomator.data.cloud.local import android.net.Uri import org.cryptomator.domain.CloudNode diff --git a/data/src/main/java/org/cryptomator/data/cloud/local/LocalStorageContentRepositoryFactory.java b/data/src/main/java/org/cryptomator/data/cloud/local/LocalStorageContentRepositoryFactory.java index c3ea26b0..b07bfebb 100644 --- a/data/src/main/java/org/cryptomator/data/cloud/local/LocalStorageContentRepositoryFactory.java +++ b/data/src/main/java/org/cryptomator/data/cloud/local/LocalStorageContentRepositoryFactory.java @@ -1,9 +1,10 @@ package org.cryptomator.data.cloud.local; -import android.content.Context; +import static org.cryptomator.domain.CloudType.LOCAL; + +import android.content.Context; +import android.content.UriPermission; -import org.cryptomator.data.cloud.local.file.LocalStorageContentRepository; -import org.cryptomator.data.cloud.local.storageaccessframework.LocalStorageAccessFrameworkContentRepository; import org.cryptomator.data.repository.CloudContentRepositoryFactory; import org.cryptomator.domain.Cloud; import org.cryptomator.domain.LocalStorageCloud; @@ -11,15 +12,11 @@ import org.cryptomator.domain.exception.authentication.NoAuthenticationProvidedE import org.cryptomator.domain.repository.CloudContentRepository; import org.cryptomator.util.file.MimeTypes; +import java.util.List; + import javax.inject.Inject; import javax.inject.Singleton; -import static android.Manifest.permission.READ_EXTERNAL_STORAGE; -import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; -import static androidx.core.content.ContextCompat.checkSelfPermission; -import static org.cryptomator.domain.CloudType.LOCAL; - @Singleton public class LocalStorageContentRepositoryFactory implements CloudContentRepositoryFactory { @@ -39,23 +36,14 @@ public class LocalStorageContentRepositoryFactory implements CloudContentReposit @Override public CloudContentRepository cloudContentRepositoryFor(Cloud cloud) { - if (!hasPermissions(WRITE_EXTERNAL_STORAGE, READ_EXTERNAL_STORAGE)) { - throw new NoAuthenticationProvidedException(cloud); - } - if (((LocalStorageCloud) cloud).rootUri() != null) { - return new LocalStorageAccessFrameworkContentRepository(context, mimeTypes, (LocalStorageCloud) cloud); - } else { - return new LocalStorageContentRepository(context, (LocalStorageCloud) cloud); - } - } - - private boolean hasPermissions(String... permissions) { - for (String permission : permissions) { - if (checkSelfPermission(context, permission) != PERMISSION_GRANTED) { - return false; + List permissions = context.getContentResolver().getPersistedUriPermissions(); + for (UriPermission permission : permissions) { + if(permission.getUri().toString().equals(((LocalStorageCloud) cloud).rootUri())) { + return new LocalStorageAccessFrameworkContentRepository(context, mimeTypes, (LocalStorageCloud) cloud); } } - return true; + + throw new NoAuthenticationProvidedException(cloud); } } diff --git a/data/src/main/java/org/cryptomator/data/cloud/local/storageaccessframework/RootLocalStorageAccessFolder.kt b/data/src/main/java/org/cryptomator/data/cloud/local/RootLocalStorageAccessFolder.kt similarity index 92% rename from data/src/main/java/org/cryptomator/data/cloud/local/storageaccessframework/RootLocalStorageAccessFolder.kt rename to data/src/main/java/org/cryptomator/data/cloud/local/RootLocalStorageAccessFolder.kt index bc59d39b..9268ac45 100644 --- a/data/src/main/java/org/cryptomator/data/cloud/local/storageaccessframework/RootLocalStorageAccessFolder.kt +++ b/data/src/main/java/org/cryptomator/data/cloud/local/RootLocalStorageAccessFolder.kt @@ -1,4 +1,4 @@ -package org.cryptomator.data.cloud.local.storageaccessframework +package org.cryptomator.data.cloud.local import android.net.Uri import android.provider.DocumentsContract diff --git a/data/src/main/java/org/cryptomator/data/cloud/local/file/LocalFile.kt b/data/src/main/java/org/cryptomator/data/cloud/local/file/LocalFile.kt deleted file mode 100644 index 43eb8ad6..00000000 --- a/data/src/main/java/org/cryptomator/data/cloud/local/file/LocalFile.kt +++ /dev/null @@ -1,11 +0,0 @@ -package org.cryptomator.data.cloud.local.file - -import org.cryptomator.domain.Cloud -import org.cryptomator.domain.CloudFile -import java.util.Date - -class LocalFile(override val parent: LocalFolder, override val name: String, override val path: String, override val size: Long?, override val modified: Date?) : CloudFile, LocalNode { - - override val cloud: Cloud? - get() = parent.cloud -} diff --git a/data/src/main/java/org/cryptomator/data/cloud/local/file/LocalFolder.kt b/data/src/main/java/org/cryptomator/data/cloud/local/file/LocalFolder.kt deleted file mode 100644 index 40d60d43..00000000 --- a/data/src/main/java/org/cryptomator/data/cloud/local/file/LocalFolder.kt +++ /dev/null @@ -1,14 +0,0 @@ -package org.cryptomator.data.cloud.local.file - -import org.cryptomator.domain.Cloud -import org.cryptomator.domain.CloudFolder - -open class LocalFolder(override val parent: LocalFolder?, override val name: String, override val path: String) : CloudFolder, LocalNode { - - override val cloud: Cloud? - get() = parent?.cloud - - override fun withCloud(cloud: Cloud?): LocalFolder? { - return LocalFolder(parent?.withCloud(cloud), name, path) - } -} diff --git a/data/src/main/java/org/cryptomator/data/cloud/local/file/LocalNode.kt b/data/src/main/java/org/cryptomator/data/cloud/local/file/LocalNode.kt deleted file mode 100644 index 313b8859..00000000 --- a/data/src/main/java/org/cryptomator/data/cloud/local/file/LocalNode.kt +++ /dev/null @@ -1,9 +0,0 @@ -package org.cryptomator.data.cloud.local.file - -import org.cryptomator.domain.CloudNode - -interface LocalNode : CloudNode { - - override val parent: LocalFolder? - -} diff --git a/data/src/main/java/org/cryptomator/data/cloud/local/file/LocalStorageContentRepository.kt b/data/src/main/java/org/cryptomator/data/cloud/local/file/LocalStorageContentRepository.kt deleted file mode 100644 index 46c225f1..00000000 --- a/data/src/main/java/org/cryptomator/data/cloud/local/file/LocalStorageContentRepository.kt +++ /dev/null @@ -1,111 +0,0 @@ -package org.cryptomator.data.cloud.local.file - -import android.content.Context -import org.cryptomator.domain.LocalStorageCloud -import org.cryptomator.domain.exception.BackendException -import org.cryptomator.domain.exception.FatalBackendException -import org.cryptomator.domain.exception.NoSuchCloudFileException -import org.cryptomator.domain.repository.CloudContentRepository -import org.cryptomator.domain.usecases.ProgressAware -import org.cryptomator.domain.usecases.cloud.DataSource -import org.cryptomator.domain.usecases.cloud.DownloadState -import org.cryptomator.domain.usecases.cloud.UploadState -import org.cryptomator.util.ExceptionUtil -import java.io.File -import java.io.FileNotFoundException -import java.io.IOException -import java.io.OutputStream - -class LocalStorageContentRepository(context: Context, localStorageCloud: LocalStorageCloud) : CloudContentRepository { - - private val localStorageImpl: LocalStorageImpl = LocalStorageImpl(context, localStorageCloud) - - @Throws(BackendException::class) - override fun root(cloud: LocalStorageCloud): LocalFolder { - return localStorageImpl.root() - } - - override fun resolve(cloud: LocalStorageCloud, path: String): LocalFolder { - return localStorageImpl.resolve(path) - } - - @Throws(BackendException::class) - override fun file(parent: LocalFolder, name: String): LocalFile { - return localStorageImpl.file(parent, name, null) - } - - @Throws(BackendException::class) - override fun file(parent: LocalFolder, name: String, size: Long?): LocalFile { - return localStorageImpl.file(parent, name, size) - } - - @Throws(BackendException::class) - override fun folder(parent: LocalFolder, name: String): LocalFolder { - return localStorageImpl.folder(parent, name) - } - - @Throws(BackendException::class) - override fun exists(node: LocalNode): Boolean { - return localStorageImpl.exists(node) - } - - @Throws(BackendException::class) - override fun list(folder: LocalFolder): List { - return localStorageImpl.list(folder) - } - - @Throws(BackendException::class) - override fun create(folder: LocalFolder): LocalFolder { - return localStorageImpl.create(folder) - } - - @Throws(BackendException::class) - override fun move(source: LocalFolder, target: LocalFolder): LocalFolder { - return localStorageImpl.move(source, target) as LocalFolder - } - - @Throws(BackendException::class) - override fun move(source: LocalFile, target: LocalFile): LocalFile { - return localStorageImpl.move(source, target) as LocalFile - } - - @Throws(BackendException::class) - override fun write(file: LocalFile, data: DataSource, progressAware: ProgressAware, replace: Boolean, size: Long): LocalFile { - return try { - localStorageImpl.write(file, data, progressAware, replace, size) - } catch (e: IOException) { - if (ExceptionUtil.contains(e, FileNotFoundException::class.java)) { - throw NoSuchCloudFileException(file.name) - } - throw FatalBackendException(e) - } - } - - @Throws(BackendException::class) - override fun read(file: LocalFile, encryptedTmpFile: File?, data: OutputStream, progressAware: ProgressAware) { - try { - localStorageImpl.read(file, data, progressAware) - } catch (e: IOException) { - if (ExceptionUtil.contains(e, FileNotFoundException::class.java)) { - throw NoSuchCloudFileException(file.name) - } - throw FatalBackendException(e) - } - } - - @Throws(BackendException::class) - override fun delete(node: LocalNode) { - localStorageImpl.delete(node) - } - - @Throws(BackendException::class) - override fun checkAuthenticationAndRetrieveCurrentAccount(cloud: LocalStorageCloud): String { - return "" - } - - @Throws(BackendException::class) - override fun logout(cloud: LocalStorageCloud) { - // empty - } - -} diff --git a/data/src/main/java/org/cryptomator/data/cloud/local/file/LocalStorageImpl.kt b/data/src/main/java/org/cryptomator/data/cloud/local/file/LocalStorageImpl.kt deleted file mode 100644 index 18547d8e..00000000 --- a/data/src/main/java/org/cryptomator/data/cloud/local/file/LocalStorageImpl.kt +++ /dev/null @@ -1,165 +0,0 @@ -package org.cryptomator.data.cloud.local.file - -import android.content.Context -import org.cryptomator.data.util.CopyStream -import org.cryptomator.data.util.TransferredBytesAwareInputStream -import org.cryptomator.data.util.TransferredBytesAwareOutputStream -import org.cryptomator.domain.CloudNode -import org.cryptomator.domain.LocalStorageCloud -import org.cryptomator.domain.exception.BackendException -import org.cryptomator.domain.exception.CloudNodeAlreadyExistsException -import org.cryptomator.domain.exception.FatalBackendException -import org.cryptomator.domain.exception.NoSuchCloudFileException -import org.cryptomator.domain.exception.ParentFolderIsNullException -import org.cryptomator.domain.usecases.ProgressAware -import org.cryptomator.domain.usecases.cloud.DataSource -import org.cryptomator.domain.usecases.cloud.DownloadState -import org.cryptomator.domain.usecases.cloud.Progress -import org.cryptomator.domain.usecases.cloud.UploadState -import java.io.File -import java.io.FileInputStream -import java.io.FileOutputStream -import java.io.IOException -import java.io.OutputStream -import java.util.Date - -internal class LocalStorageImpl(private val context: Context, localStorageCloud: LocalStorageCloud) { - - private val root: RootLocalFolder = RootLocalFolder(localStorageCloud) - - fun root(): LocalFolder { - return root - } - - fun resolve(path: String): LocalFolder { - val names = path.substring(root.path.length + 1).split("/").toTypedArray() - var folder: LocalFolder = root - for (name in names) { - folder = folder(folder, name) - } - return folder - } - - fun file(folder: LocalFolder, name: String, size: Long?): LocalFile { - return LocalStorageNodeFactory.file(folder, name, folder.path + '/' + name, size, null) - } - - fun folder(folder: LocalFolder, name: String): LocalFolder { - return LocalStorageNodeFactory.folder(folder, name, folder.path + '/' + name) - } - - fun exists(node: CloudNode): Boolean { - return File(node.path).exists() - } - - @Throws(BackendException::class) - fun list(folder: LocalFolder): List { - val localDirectory = File(folder.path) - if (!exists(folder)) { - throw NoSuchCloudFileException() - } - return localDirectory.listFiles()?.map { file -> LocalStorageNodeFactory.from(folder, file) } - ?: throw FatalBackendException("listFiles() shouldn't return null") - } - - @Throws(BackendException::class) - fun create(folder: LocalFolder): LocalFolder { - folder.parent?.let { parentFolder -> - val createFolder = File(folder.path) - if (createFolder.exists()) { - throw CloudNodeAlreadyExistsException(folder.name) - } - if (!createFolder.mkdirs()) { - throw FatalBackendException("Couldn't create a local folder at " + folder.path) - } - return LocalStorageNodeFactory.folder(parentFolder, createFolder) - } ?: throw ParentFolderIsNullException(folder.name) - } - - @Throws(BackendException::class) - fun move(source: LocalNode, target: LocalNode): LocalNode { - target.parent?.let { - val sourceFile = File(source.path) - val targetFile = File(target.path) - if (targetFile.exists()) { - throw CloudNodeAlreadyExistsException(target.name) - } - if (!sourceFile.exists()) { - throw NoSuchCloudFileException(source.name) - } - if (!sourceFile.renameTo(targetFile)) { - throw FatalBackendException("Couldn't move " + source.path + " to " + target.path) - } - - return LocalStorageNodeFactory.from(it, targetFile) - } ?: throw ParentFolderIsNullException(target.name) - } - - fun delete(node: CloudNode) { - val fileOrDirectory = File(node.path) - if (!deleteRecursive(fileOrDirectory)) { - throw FatalBackendException("Couldn't delete local CloudNode $fileOrDirectory") - } - } - - private fun deleteRecursive(fileOrDirectory: File): Boolean { - if (fileOrDirectory.isDirectory) { - fileOrDirectory.listFiles()?.forEach { - deleteRecursive(it) - } - } - return fileOrDirectory.delete() - } - - @Throws(IOException::class, BackendException::class) - fun write(file: LocalFile, data: DataSource, progressAware: ProgressAware, replace: Boolean, size: Long): LocalFile { - if (!replace && exists(file)) { - throw CloudNodeAlreadyExistsException("CloudNode already exists and replace is false") - } - progressAware.onProgress(Progress.started(UploadState.upload(file))) - val localFile = File(file.path) - FileOutputStream(localFile).use { out -> - 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 { CopyStream.copyStreamToStream(it, out) } - } ?: throw FatalBackendException("InputStream shouldn't be null") - } - progressAware.onProgress(Progress.completed(UploadState.upload(file))) - return LocalStorageNodeFactory.file( // - file.parent, // - file.name, // - localFile.path, // - localFile.length(), // - Date(localFile.lastModified()) - ) - } - - @Throws(IOException::class) - fun read(file: LocalFile, data: OutputStream, progressAware: ProgressAware) { - progressAware.onProgress(Progress.started(DownloadState.download(file))) - val localFile = File(file.path) - FileInputStream(localFile).use { inputStream -> - object : TransferredBytesAwareOutputStream(data) { - override fun bytesTransferred(transferred: Long) { - progressAware // - .onProgress( - Progress.progress(DownloadState.download(file)) // - .between(0) // - .and(localFile.length()) // - .withValue(transferred) - ) - } - }.use { out -> CopyStream.copyStreamToStream(inputStream, out) } - } - progressAware.onProgress(Progress.completed(DownloadState.download(file))) - } - -} diff --git a/data/src/main/java/org/cryptomator/data/cloud/local/file/LocalStorageNodeFactory.kt b/data/src/main/java/org/cryptomator/data/cloud/local/file/LocalStorageNodeFactory.kt deleted file mode 100644 index e6788644..00000000 --- a/data/src/main/java/org/cryptomator/data/cloud/local/file/LocalStorageNodeFactory.kt +++ /dev/null @@ -1,36 +0,0 @@ -package org.cryptomator.data.cloud.local.file - -import java.io.File -import java.util.Date - -internal object LocalStorageNodeFactory { - - @JvmStatic - fun from(parent: LocalFolder, file: File): LocalNode { - return if (file.isDirectory) { - folder(parent, file) - } else { - file( // - parent, // - file.name, // - file.path, // - file.length(), // - Date(file.lastModified()) - ) - } - } - - fun folder(parent: LocalFolder, file: File): LocalFolder { - return folder(parent, file.name, file.path) - } - - @JvmStatic - fun folder(parent: LocalFolder, name: String, path: String): LocalFolder { - return LocalFolder(parent, name, path) - } - - @JvmStatic - fun file(folder: LocalFolder, name: String, path: String, size: Long?, modified: Date?): LocalFile { - return LocalFile(folder, name, path, size, modified) - } -} diff --git a/data/src/main/java/org/cryptomator/data/cloud/local/file/RootLocalFolder.kt b/data/src/main/java/org/cryptomator/data/cloud/local/file/RootLocalFolder.kt deleted file mode 100644 index fde9d439..00000000 --- a/data/src/main/java/org/cryptomator/data/cloud/local/file/RootLocalFolder.kt +++ /dev/null @@ -1,15 +0,0 @@ -package org.cryptomator.data.cloud.local.file - -import android.os.Environment -import org.cryptomator.domain.Cloud -import org.cryptomator.domain.LocalStorageCloud - -class RootLocalFolder(private val localStorageCloud: LocalStorageCloud) : LocalFolder(null, "", Environment.getExternalStorageDirectory().path) { - - override val cloud: Cloud - get() = localStorageCloud - - override fun withCloud(cloud: Cloud?): RootLocalFolder { - return RootLocalFolder(cloud as LocalStorageCloud) - } -} diff --git a/data/src/main/java/org/cryptomator/data/repository/CloudRepositoryImpl.java b/data/src/main/java/org/cryptomator/data/repository/CloudRepositoryImpl.java index bdb422c3..61f1307c 100644 --- a/data/src/main/java/org/cryptomator/data/repository/CloudRepositoryImpl.java +++ b/data/src/main/java/org/cryptomator/data/repository/CloudRepositoryImpl.java @@ -76,9 +76,6 @@ class CloudRepositoryImpl implements CloudRepository { @Override public void delete(Cloud cloud) { - if (cloud.predefined()) { - throw new IllegalArgumentException("Can not delete predefined cloud"); - } if (!cloud.persistent()) { throw new IllegalArgumentException("Can not delete non persistent cloud"); } diff --git a/data/src/main/java/org/cryptomator/data/util/NetworkConnectionCheck.kt b/data/src/main/java/org/cryptomator/data/util/NetworkConnectionCheck.kt index 14332c8d..9ad87420 100644 --- a/data/src/main/java/org/cryptomator/data/util/NetworkConnectionCheck.kt +++ b/data/src/main/java/org/cryptomator/data/util/NetworkConnectionCheck.kt @@ -28,6 +28,6 @@ class NetworkConnectionCheck @Inject internal constructor(private val context: C fun checkWifiOnAndConnected(): Boolean { val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager val activeNetwork = connectivityManager.activeNetwork - return connectivityManager.getNetworkCapabilities(activeNetwork).hasTransport(NetworkCapabilities.TRANSPORT_WIFI) + return connectivityManager.getNetworkCapabilities(activeNetwork)?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) == true } } diff --git a/domain/src/main/java/org/cryptomator/domain/Cloud.kt b/domain/src/main/java/org/cryptomator/domain/Cloud.kt index ee1aba13..6c1f1e22 100644 --- a/domain/src/main/java/org/cryptomator/domain/Cloud.kt +++ b/domain/src/main/java/org/cryptomator/domain/Cloud.kt @@ -7,7 +7,6 @@ interface Cloud : Serializable { fun id(): Long? fun type(): CloudType? fun configurationMatches(cloud: Cloud?): Boolean - fun predefined(): Boolean fun persistent(): Boolean fun requiresNetwork(): Boolean } diff --git a/domain/src/main/java/org/cryptomator/domain/DropboxCloud.java b/domain/src/main/java/org/cryptomator/domain/DropboxCloud.java index b8f4fc8a..2178e3c7 100644 --- a/domain/src/main/java/org/cryptomator/domain/DropboxCloud.java +++ b/domain/src/main/java/org/cryptomator/domain/DropboxCloud.java @@ -48,11 +48,6 @@ public class DropboxCloud implements Cloud { return true; } - @Override - public boolean predefined() { - return true; - } - @Override public boolean persistent() { return true; diff --git a/domain/src/main/java/org/cryptomator/domain/GoogleDriveCloud.java b/domain/src/main/java/org/cryptomator/domain/GoogleDriveCloud.java index e8b27ce9..8ebd70f0 100644 --- a/domain/src/main/java/org/cryptomator/domain/GoogleDriveCloud.java +++ b/domain/src/main/java/org/cryptomator/domain/GoogleDriveCloud.java @@ -48,11 +48,6 @@ public class GoogleDriveCloud implements Cloud { return true; } - @Override - public boolean predefined() { - return true; - } - @Override public boolean persistent() { return true; diff --git a/domain/src/main/java/org/cryptomator/domain/LocalStorageCloud.java b/domain/src/main/java/org/cryptomator/domain/LocalStorageCloud.java index ee895359..fb96449c 100644 --- a/domain/src/main/java/org/cryptomator/domain/LocalStorageCloud.java +++ b/domain/src/main/java/org/cryptomator/domain/LocalStorageCloud.java @@ -1,6 +1,5 @@ package org.cryptomator.domain; -import android.os.Build; import android.text.TextUtils; import org.jetbrains.annotations.NotNull; @@ -48,11 +47,6 @@ public class LocalStorageCloud implements Cloud { } - @Override - public boolean predefined() { - return Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP; - } - @Override public boolean persistent() { return true; diff --git a/domain/src/main/java/org/cryptomator/domain/OnedriveCloud.java b/domain/src/main/java/org/cryptomator/domain/OnedriveCloud.java index 5b082012..a56accf0 100644 --- a/domain/src/main/java/org/cryptomator/domain/OnedriveCloud.java +++ b/domain/src/main/java/org/cryptomator/domain/OnedriveCloud.java @@ -43,11 +43,6 @@ public class OnedriveCloud implements Cloud { return CloudType.ONEDRIVE; } - @Override - public boolean predefined() { - return true; - } - @Override public boolean persistent() { return true; diff --git a/domain/src/main/java/org/cryptomator/domain/PCloud.java b/domain/src/main/java/org/cryptomator/domain/PCloud.java index 95164c65..89b01617 100644 --- a/domain/src/main/java/org/cryptomator/domain/PCloud.java +++ b/domain/src/main/java/org/cryptomator/domain/PCloud.java @@ -59,12 +59,6 @@ public class PCloud implements Cloud { return username.equals(cloud.username); } - - @Override - public boolean predefined() { - return false; - } - @Override public boolean persistent() { return true; diff --git a/domain/src/main/java/org/cryptomator/domain/S3Cloud.java b/domain/src/main/java/org/cryptomator/domain/S3Cloud.java index 44268df6..064cc93a 100644 --- a/domain/src/main/java/org/cryptomator/domain/S3Cloud.java +++ b/domain/src/main/java/org/cryptomator/domain/S3Cloud.java @@ -80,12 +80,6 @@ public class S3Cloud implements Cloud { return s3Bucket.equals(cloud.s3Bucket) && s3Endpoint.equals(cloud.s3Endpoint) && s3Region.equals(cloud.s3Region); } - - @Override - public boolean predefined() { - return false; - } - @Override public boolean persistent() { return true; diff --git a/domain/src/main/java/org/cryptomator/domain/WebDavCloud.java b/domain/src/main/java/org/cryptomator/domain/WebDavCloud.java index 2ffba185..ccb21dd6 100644 --- a/domain/src/main/java/org/cryptomator/domain/WebDavCloud.java +++ b/domain/src/main/java/org/cryptomator/domain/WebDavCloud.java @@ -66,11 +66,6 @@ public class WebDavCloud implements Cloud { return certificate; } - @Override - public boolean predefined() { - return false; - } - @Override public boolean persistent() { return true; diff --git a/presentation/src/foss/java/org/cryptomator/presentation/presenter/AuthenticateCloudPresenter.kt b/presentation/src/foss/java/org/cryptomator/presentation/presenter/AuthenticateCloudPresenter.kt index abfe6e74..07bce89b 100644 --- a/presentation/src/foss/java/org/cryptomator/presentation/presenter/AuthenticateCloudPresenter.kt +++ b/presentation/src/foss/java/org/cryptomator/presentation/presenter/AuthenticateCloudPresenter.kt @@ -1,6 +1,5 @@ package org.cryptomator.presentation.presenter -import android.Manifest import android.accounts.AccountManager import android.widget.Toast import com.dropbox.core.android.Auth @@ -35,6 +34,7 @@ import org.cryptomator.presentation.intent.AuthenticateCloudIntent import org.cryptomator.presentation.intent.Intents import org.cryptomator.presentation.model.CloudModel import org.cryptomator.presentation.model.CloudTypeModel +import org.cryptomator.presentation.model.LocalStorageModel import org.cryptomator.presentation.model.ProgressModel import org.cryptomator.presentation.model.ProgressStateModel import org.cryptomator.presentation.model.S3CloudModel @@ -44,7 +44,6 @@ import org.cryptomator.presentation.ui.activity.view.AuthenticateCloudView import org.cryptomator.presentation.workflow.ActivityResult import org.cryptomator.presentation.workflow.AddExistingVaultWorkflow import org.cryptomator.presentation.workflow.CreateNewVaultWorkflow -import org.cryptomator.presentation.workflow.PermissionsResult import org.cryptomator.presentation.workflow.Workflow import org.cryptomator.util.ExceptionUtil import org.cryptomator.util.crypto.CredentialCryptor @@ -445,20 +444,15 @@ class AuthenticateCloudPresenter @Inject constructor( // private fun startAuthentication(cloud: CloudModel) { authenticationStarted = true - requestPermissions( - PermissionsResultCallbacks.onLocalStorageAuthenticated(cloud), // - R.string.permission_snackbar_auth_local_vault, // - Manifest.permission.READ_EXTERNAL_STORAGE, // - Manifest.permission.WRITE_EXTERNAL_STORAGE - ) - } - } + val permissions = context().contentResolver.persistedUriPermissions + for (permission in permissions) { + if (permission.uri.toString() == (cloud as LocalStorageModel).uri()) { + succeedAuthenticationWith(cloud.toCloud()) + } + } - @Callback - fun onLocalStorageAuthenticated(result: PermissionsResult, cloud: CloudModel) { - if (result.granted()) { - succeedAuthenticationWith(cloud.toCloud()) - } else { + // FIXME think about how to re-request permission + // FIXME change in the FOSS variant too failAuthentication(PermissionNotGrantedException(R.string.permission_snackbar_auth_local_vault)) } } diff --git a/presentation/src/main/AndroidManifest.xml b/presentation/src/main/AndroidManifest.xml index 0e2bbcf7..5b155c10 100644 --- a/presentation/src/main/AndroidManifest.xml +++ b/presentation/src/main/AndroidManifest.xml @@ -7,7 +7,6 @@ - @@ -28,7 +27,6 @@ android:allowBackup="false" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" - android:requestLegacyExternalStorage="true" android:supportsRtl="true" android:theme="@style/AppTheme" android:usesCleartextTraffic="true"> diff --git a/presentation/src/main/java/org/cryptomator/presentation/BootAwareReceiver.kt b/presentation/src/main/java/org/cryptomator/presentation/BootAwareReceiver.kt index b0124eb2..f20baf4a 100644 --- a/presentation/src/main/java/org/cryptomator/presentation/BootAwareReceiver.kt +++ b/presentation/src/main/java/org/cryptomator/presentation/BootAwareReceiver.kt @@ -3,7 +3,6 @@ package org.cryptomator.presentation import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -import android.os.Build import org.cryptomator.presentation.service.CryptorsService import org.cryptomator.presentation.service.PhotoContentJob import org.cryptomator.util.SharedPreferencesHandler @@ -18,7 +17,7 @@ class BootAwareReceiver : BroadcastReceiver() { context.stopService(CryptorsService.lockAllIntent(context)) } intent.action.equals(Intent.ACTION_BOOT_COMPLETED, ignoreCase = true) -> { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && SharedPreferencesHandler(context).usePhotoUpload()) { + if (SharedPreferencesHandler(context).usePhotoUpload()) { Timber.tag("BootAwareReceiver").i("Starting AutoUploadJobScheduler") PhotoContentJob.scheduleJob(context) } diff --git a/presentation/src/main/java/org/cryptomator/presentation/presenter/BrowseFilesPresenter.kt b/presentation/src/main/java/org/cryptomator/presentation/presenter/BrowseFilesPresenter.kt index 47e03969..391d30a9 100644 --- a/presentation/src/main/java/org/cryptomator/presentation/presenter/BrowseFilesPresenter.kt +++ b/presentation/src/main/java/org/cryptomator/presentation/presenter/BrowseFilesPresenter.kt @@ -4,11 +4,8 @@ import android.Manifest import android.content.ActivityNotFoundException import android.content.Intent import android.net.Uri -import android.os.Build -import android.os.Environment import android.provider.DocumentsContract import android.widget.Toast -import androidx.annotation.RequiresApi import org.cryptomator.domain.CloudFile import org.cryptomator.domain.CloudFolder import org.cryptomator.domain.CloudNode @@ -83,10 +80,8 @@ import org.cryptomator.util.SharedPreferencesHandler import org.cryptomator.util.file.FileCacheUtils import org.cryptomator.util.file.MimeType import org.cryptomator.util.file.MimeTypes -import java.io.File import java.io.FileInputStream import java.io.FileNotFoundException -import java.io.FileOutputStream import java.io.Serializable import java.security.DigestInputStream import java.security.MessageDigest @@ -738,30 +733,6 @@ class BrowseFilesPresenter @Inject constructor( // exportNodesToUserSelectedLocation(selectedCloudFiles, trigger) } - @Callback - fun exportFileToDownloadDirectory(result: PermissionsResult, fileToExport: CloudFileModel, exportOperation: ExportOperation) { - if (result.granted()) { - val downloads = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) - val cryptomatorDownloads = File(downloads, context().getString(R.string.download_subdirectory_name)) - cryptomatorDownloads.mkdirs() - if (cryptomatorDownloads.isDirectory) { - val target = File(cryptomatorDownloads, fileToExport.name) - try { - val downloadFile = DownloadFile.Builder() // - .setDownloadFile(fileToExport.toCloudNode()) // - .setDataSink(FileOutputStream(target)) // - .build() - exportOperation.export(this, listOf(downloadFile)) - } catch (e: FileNotFoundException) { - showError(e) - } - } else { - view?.showError(R.string.screen_file_browser_msg_creating_download_dir_failed) - } - } - } - - @RequiresApi(Build.VERSION_CODES.KITKAT) private fun exportFileToUserSelectedLocation(fileToExport: CloudFileModel, exportOperation: ExportOperation) { val intent = Intent(Intent.ACTION_CREATE_DOCUMENT) intent.addCategory(Intent.CATEGORY_OPENABLE) @@ -789,7 +760,6 @@ class BrowseFilesPresenter @Inject constructor( // } @Callback - @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) fun pickedLocalStorageLocation( result: ActivityResult, // nodesToExport: ArrayList>, // @@ -809,7 +779,6 @@ class BrowseFilesPresenter @Inject constructor( // disableSelectionMode() } - @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) private fun collectNodesToExport( parentUri: Uri, // exportOperation: ExportOperation, // @@ -827,7 +796,6 @@ class BrowseFilesPresenter @Inject constructor( // collectFolderContentForExport(parentUri, exportOperation, foldersForRecursiveDirListing, filesToExport) } - @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) private fun collectFolderContentForExport( parentUri: Uri, exportOperation: ExportOperation, folders: List, // filesToExport: List @@ -847,7 +815,6 @@ class BrowseFilesPresenter @Inject constructor( // }) } - @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) private fun prepareExportingOf(parentUri: Uri, exportOperation: ExportOperation, filesToExport: List, cloudNodeRecursiveListing: CloudNodeRecursiveListing) { downloadFiles = ArrayList() downloadFiles.addAll(prepareFilesForExport(cloudFileModelMapper.fromModels(filesToExport), parentUri)) @@ -862,12 +829,10 @@ class BrowseFilesPresenter @Inject constructor( // } } - @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) private fun prepareFilesForExport(filesToExport: List, parentUri: Uri): List { return filesToExport.mapTo(ArrayList()) { createDownloadFile(it, parentUri) } } - @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) private fun prepareFolderContentForExport(cloudFolderRecursiveListing: CloudFolderRecursiveListing, parentUri: Uri) { createFolder(parentUri, cloudFolderRecursiveListing.parent.name)?.let { downloadFiles.addAll(prepareFilesForExport(cloudFolderRecursiveListing.files, it)) @@ -877,7 +842,6 @@ class BrowseFilesPresenter @Inject constructor( // } ?: throw FatalBackendException("Failed to create parent folder for export") } - @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) private fun createFolder(parentUri: Uri, folderName: String): Uri? { return try { DocumentsContract.createDocument( // @@ -892,7 +856,6 @@ class BrowseFilesPresenter @Inject constructor( // } } - @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) private fun createDownloadFile(file: CloudFile, documentUri: Uri): DownloadFile { return try { DownloadFile.Builder() // @@ -918,7 +881,6 @@ class BrowseFilesPresenter @Inject constructor( // } } - @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Throws(IllegalFileNameException::class, NoSuchCloudFileException::class) private fun createNewDocumentUri(parentUri: Uri, fileName: String): Uri { val mimeType = mimeTypes.fromFilename(fileName) ?: MimeType.APPLICATION_OCTET_STREAM diff --git a/presentation/src/main/java/org/cryptomator/presentation/presenter/CloudConnectionListPresenter.kt b/presentation/src/main/java/org/cryptomator/presentation/presenter/CloudConnectionListPresenter.kt index ff98ce85..ba4ab6a3 100644 --- a/presentation/src/main/java/org/cryptomator/presentation/presenter/CloudConnectionListPresenter.kt +++ b/presentation/src/main/java/org/cryptomator/presentation/presenter/CloudConnectionListPresenter.kt @@ -3,9 +3,7 @@ package org.cryptomator.presentation.presenter import android.content.ActivityNotFoundException import android.content.Intent import android.net.Uri -import android.os.Build import android.widget.Toast -import androidx.annotation.RequiresApi import org.cryptomator.domain.Cloud import org.cryptomator.domain.LocalStorageCloud import org.cryptomator.domain.PCloud @@ -251,7 +249,6 @@ class CloudConnectionListPresenter @Inject constructor( // } @Callback - @RequiresApi(api = Build.VERSION_CODES.KITKAT) fun pickedLocalStorageLocation(result: ActivityResult) { val rootTreeUriOfLocalStorage = result.intent().data persistUriPermission(rootTreeUriOfLocalStorage) @@ -266,7 +263,6 @@ class CloudConnectionListPresenter @Inject constructor( // }) } - @RequiresApi(api = Build.VERSION_CODES.KITKAT) private fun persistUriPermission(rootTreeUriOfLocalStorage: Uri?) { rootTreeUriOfLocalStorage?.let { context() // @@ -278,7 +274,6 @@ class CloudConnectionListPresenter @Inject constructor( // } } - @RequiresApi(api = Build.VERSION_CODES.KITKAT) private fun releaseUriPermission(uri: String) { context() // .contentResolver // diff --git a/presentation/src/main/java/org/cryptomator/presentation/presenter/ImagePreviewPresenter.kt b/presentation/src/main/java/org/cryptomator/presentation/presenter/ImagePreviewPresenter.kt index 31b066d8..393aac0a 100644 --- a/presentation/src/main/java/org/cryptomator/presentation/presenter/ImagePreviewPresenter.kt +++ b/presentation/src/main/java/org/cryptomator/presentation/presenter/ImagePreviewPresenter.kt @@ -3,8 +3,6 @@ package org.cryptomator.presentation.presenter import android.Manifest import android.content.Intent import android.net.Uri -import android.os.Build -import android.os.Environment import org.cryptomator.domain.CloudFile import org.cryptomator.domain.CloudNode import org.cryptomator.domain.di.PerView @@ -30,9 +28,7 @@ import org.cryptomator.presentation.util.ShareFileHelper import org.cryptomator.presentation.workflow.ActivityResult import org.cryptomator.presentation.workflow.PermissionsResult import org.cryptomator.util.ExceptionUtil -import java.io.File import java.io.FileNotFoundException -import java.io.FileOutputStream import java.io.IOException import java.io.InputStream import java.io.OutputStream @@ -58,37 +54,33 @@ class ImagePreviewPresenter @Inject constructor( // @InstanceState lateinit var pageIndexes: ArrayList - fun onExportImageClicked(uri: Uri) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { - copyFileToDownloadDirectory(uri) - } else { - copyFileToUserSelectedLocation(uri) - } + fun exportImageToUserSelectedLocation(uri: Uri) { + val intent = Intent(Intent.ACTION_CREATE_DOCUMENT) + intent.addCategory(Intent.CATEGORY_OPENABLE) + intent.type = "*/*" + intent.putExtra(Intent.EXTRA_TITLE, contentResolverUtil.fileName(uri)) + requestActivityResult(ActivityResultCallbacks.exportImageToUserSelectedLocation(uri.toString()), intent) } - private fun copyFileToDownloadDirectory(uri: Uri) { + @Callback + fun exportImageToUserSelectedLocation(result: ActivityResult, sourceUri: String?) { requestPermissions( - PermissionsResultCallbacks.copyFileToDownloadDirectory(uri.toString()), // - R.string.permission_message_export_file, Manifest.permission.WRITE_EXTERNAL_STORAGE + PermissionsResultCallbacks.exportImageToUserSelectedLocation(result.intent()?.dataString, sourceUri), // + R.string.permission_message_export_file, // + Manifest.permission.READ_EXTERNAL_STORAGE ) } @Callback - fun copyFileToDownloadDirectory(result: PermissionsResult, uriString: String?) { + fun exportImageToUserSelectedLocation(result: PermissionsResult, targetUri: String?, sourceUri: String?) { if (result.granted()) { - val uriFile = Uri.parse(uriString) - val downloads = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) - val cryptomatorDownloads = File(downloads, context().getString(R.string.download_subdirectory_name)) - cryptomatorDownloads.mkdirs() - if (cryptomatorDownloads.isDirectory) { - val target = File(cryptomatorDownloads, contentResolverUtil.fileName(uriFile)) - try { - copyFile(contentResolverUtil.openInputStream(uriFile), FileOutputStream(target)) - } catch (e: FileNotFoundException) { - showError(e) - } - } else { - view?.showError(R.string.screen_file_browser_msg_creating_download_dir_failed) + try { + copyFile( + contentResolverUtil.openInputStream(Uri.parse(sourceUri)), // + contentResolverUtil.openOutputStream(Uri.parse(targetUri)) + ) + } catch (e: FileNotFoundException) { + showError(e) } } } @@ -107,37 +99,6 @@ class ImagePreviewPresenter @Inject constructor( // }) } - private fun copyFileToUserSelectedLocation(uri: Uri) { - val intent = Intent(Intent.ACTION_CREATE_DOCUMENT) - intent.addCategory(Intent.CATEGORY_OPENABLE) - intent.type = "*/*" - intent.putExtra(Intent.EXTRA_TITLE, contentResolverUtil.fileName(uri)) - requestActivityResult(ActivityResultCallbacks.copyFileToUserSelectedLocation(uri.toString()), intent) - } - - @Callback - fun copyFileToUserSelectedLocation(result: ActivityResult, sourceUri: String?) { - requestPermissions( - PermissionsResultCallbacks.copyFileToUserSelectedLocation(result.intent()?.dataString, sourceUri), // - R.string.permission_message_export_file, // - Manifest.permission.READ_EXTERNAL_STORAGE - ) - } - - @Callback - fun copyFileToUserSelectedLocation(result: PermissionsResult, targetUri: String?, sourceUri: String?) { - if (result.granted()) { - try { - copyFile( - contentResolverUtil.openInputStream(Uri.parse(sourceUri)), // - contentResolverUtil.openOutputStream(Uri.parse(targetUri)) - ) - } catch (e: FileNotFoundException) { - showError(e) - } - } - } - fun onShareImageClicked(uri: Uri) { shareFileHelper.shareFile(this, uri) } diff --git a/presentation/src/main/java/org/cryptomator/presentation/presenter/SettingsPresenter.kt b/presentation/src/main/java/org/cryptomator/presentation/presenter/SettingsPresenter.kt index f40390d1..43cb4dc4 100644 --- a/presentation/src/main/java/org/cryptomator/presentation/presenter/SettingsPresenter.kt +++ b/presentation/src/main/java/org/cryptomator/presentation/presenter/SettingsPresenter.kt @@ -90,17 +90,14 @@ class SettingsPresenter @Inject internal constructor( requestPermissions( PermissionsResultCallbacks.onLocalStoragePermissionGranted(), // R.string.permission_snackbar_auth_auto_upload, // - Manifest.permission.READ_EXTERNAL_STORAGE, // - Manifest.permission.WRITE_EXTERNAL_STORAGE + Manifest.permission.READ_EXTERNAL_STORAGE ) } @Callback fun onLocalStoragePermissionGranted(result: PermissionsResult) { if (result.granted()) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - scheduleJob(context()) - } + scheduleJob(context()) } else { view?.disableAutoUpload() } diff --git a/presentation/src/main/java/org/cryptomator/presentation/presenter/UnlockVaultPresenter.kt b/presentation/src/main/java/org/cryptomator/presentation/presenter/UnlockVaultPresenter.kt index f5b327b2..edea86e7 100644 --- a/presentation/src/main/java/org/cryptomator/presentation/presenter/UnlockVaultPresenter.kt +++ b/presentation/src/main/java/org/cryptomator/presentation/presenter/UnlockVaultPresenter.kt @@ -67,7 +67,7 @@ class UnlockVaultPresenter @Inject constructor( super.destroyed() if (retryUnlockHandler != null) { running = false - retryUnlockHandler?.removeCallbacks(null) + retryUnlockHandler?.removeCallbacksAndMessages(null) } } @@ -140,11 +140,12 @@ class UnlockVaultPresenter @Inject constructor( } } + // FIXME why is this method not used? fun onWindowFocusChanged(hasFocus: Boolean) { if (hasFocus) { if (retryUnlockHandler != null) { running = false - retryUnlockHandler?.removeCallbacks(null) + retryUnlockHandler?.removeCallbacksAndMessages(null) } } } diff --git a/presentation/src/main/java/org/cryptomator/presentation/ui/activity/ImagePreviewActivity.kt b/presentation/src/main/java/org/cryptomator/presentation/ui/activity/ImagePreviewActivity.kt index 209f27ea..901b67cb 100644 --- a/presentation/src/main/java/org/cryptomator/presentation/ui/activity/ImagePreviewActivity.kt +++ b/presentation/src/main/java/org/cryptomator/presentation/ui/activity/ImagePreviewActivity.kt @@ -66,7 +66,7 @@ class ImagePreviewActivity : BaseActivity(), ImagePreviewView, ConfirmDeleteClou presenter.onDeleteImageClicked(imagePreviewFiles[imagePreviewSliderAdapter.getIndex(viewPager.currentItem)]) } exportImage.setOnClickListener { - currentImageUri?.let { presenter.onExportImageClicked(it) } + currentImageUri?.let { presenter.exportImageToUserSelectedLocation(it) } } shareImage.setOnClickListener { currentImageUri?.let { presenter.onShareImageClicked(it) } diff --git a/presentation/src/main/java/org/cryptomator/presentation/ui/bottomsheet/FolderSettingsBottomSheet.kt b/presentation/src/main/java/org/cryptomator/presentation/ui/bottomsheet/FolderSettingsBottomSheet.kt index 6440caa7..1c3a587f 100644 --- a/presentation/src/main/java/org/cryptomator/presentation/ui/bottomsheet/FolderSettingsBottomSheet.kt +++ b/presentation/src/main/java/org/cryptomator/presentation/ui/bottomsheet/FolderSettingsBottomSheet.kt @@ -1,6 +1,5 @@ package org.cryptomator.presentation.ui.bottomsheet -import android.os.Build import android.os.Bundle import android.view.View import org.cryptomator.generator.BottomSheet @@ -46,12 +45,10 @@ class FolderSettingsBottomSheet : BaseBottomSheet= Build.VERSION_CODES.LOLLIPOP) { - export_folder.visibility = View.VISIBLE - export_folder.setOnClickListener { - callback?.onExportFolderClicked(cloudFolderModel) - dismiss() - } + export_folder.visibility = View.VISIBLE + export_folder.setOnClickListener { + callback?.onExportFolderClicked(cloudFolderModel) + dismiss() } delete_folder.setOnClickListener { callback?.onDeleteNodeClicked(cloudFolderModel) diff --git a/presentation/src/main/java/org/cryptomator/presentation/ui/dialog/UpdateAppAvailableDialog.kt b/presentation/src/main/java/org/cryptomator/presentation/ui/dialog/UpdateAppAvailableDialog.kt index 7f477e70..074113f5 100644 --- a/presentation/src/main/java/org/cryptomator/presentation/ui/dialog/UpdateAppAvailableDialog.kt +++ b/presentation/src/main/java/org/cryptomator/presentation/ui/dialog/UpdateAppAvailableDialog.kt @@ -1,7 +1,6 @@ package org.cryptomator.presentation.ui.dialog import android.content.DialogInterface -import android.os.Build import android.os.Bundle import android.text.Html import android.view.View @@ -32,11 +31,7 @@ class UpdateAppAvailableDialog : BaseProgressErrorDialog= Build.VERSION_CODES.N) { - tv_message.text = Html.fromHtml(message, Html.FROM_HTML_MODE_COMPACT) - } else { - tv_message.text = Html.fromHtml(message) - } + tv_message.text = Html.fromHtml(message, Html.FROM_HTML_MODE_COMPACT) } override fun enableViewAfterError(): View { diff --git a/presentation/src/main/java/org/cryptomator/presentation/ui/fragment/CloudConnectionListFragment.kt b/presentation/src/main/java/org/cryptomator/presentation/ui/fragment/CloudConnectionListFragment.kt index bf974c6f..57d8a0ed 100644 --- a/presentation/src/main/java/org/cryptomator/presentation/ui/fragment/CloudConnectionListFragment.kt +++ b/presentation/src/main/java/org/cryptomator/presentation/ui/fragment/CloudConnectionListFragment.kt @@ -1,6 +1,5 @@ package org.cryptomator.presentation.ui.fragment -import android.os.Environment import android.util.TypedValue import android.view.View.GONE import android.view.View.VISIBLE @@ -13,10 +12,7 @@ import org.cryptomator.presentation.presenter.CloudConnectionListPresenter import org.cryptomator.presentation.ui.adapter.CloudConnectionListAdapter import javax.inject.Inject import kotlinx.android.synthetic.main.fragment_browse_cloud_connections.floating_action_button -import kotlinx.android.synthetic.main.fragment_browse_cloud_connections.rv_local_default_cloud import kotlinx.android.synthetic.main.recycler_view_layout.recyclerView -import kotlinx.android.synthetic.main.view_cloud_connection_content.cloudSubText -import kotlinx.android.synthetic.main.view_cloud_connection_content.cloudText import kotlinx.android.synthetic.main.view_empty_cloud_connections.rl_creation_hint @Fragment(R.layout.fragment_browse_cloud_connections) @@ -42,7 +38,6 @@ class CloudConnectionListFragment : BaseFragment() { override fun setupView() { setupRecyclerView() - rv_local_default_cloud.setOnClickListener { cloudConnectionListPresenter.onDefaultLocalCloudConnectionClicked() } floating_action_button.setOnClickListener { cloudConnectionListPresenter.onAddConnectionClicked() } } @@ -71,11 +66,5 @@ class CloudConnectionListFragment : BaseFragment() { fun setSelectedCloudType(selectedCloudType: CloudTypeModel) { this.selectedCloudType = selectedCloudType - - if (CloudTypeModel.LOCAL == selectedCloudType) { - rv_local_default_cloud.visibility = VISIBLE - cloudText.text = getString(R.string.screen_cloud_local_default_storage_title) - cloudSubText.text = Environment.getExternalStorageDirectory().toString() - } } } diff --git a/presentation/src/main/java/org/cryptomator/presentation/ui/fragment/SettingsFragment.kt b/presentation/src/main/java/org/cryptomator/presentation/ui/fragment/SettingsFragment.kt index 1022f509..d40769ff 100644 --- a/presentation/src/main/java/org/cryptomator/presentation/ui/fragment/SettingsFragment.kt +++ b/presentation/src/main/java/org/cryptomator/presentation/ui/fragment/SettingsFragment.kt @@ -1,6 +1,5 @@ package org.cryptomator.presentation.ui.fragment -import android.os.Build import android.os.Bundle import android.text.SpannableString import android.text.style.ForegroundColorSpan @@ -259,9 +258,7 @@ class SettingsFragment : PreferenceFragmentCompat() { if (enabled) { activity().grantLocalStoragePermissionForAutoUpload() } else { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - PhotoContentJob.cancelJob(activity().applicationContext) - } + PhotoContentJob.cancelJob(activity().applicationContext) } (findPreference(SharedPreferencesHandler.PHOTO_UPLOAD) as SwitchPreferenceCompat?)?.isChecked = enabled } diff --git a/presentation/src/main/res/layout/fragment_browse_cloud_connections.xml b/presentation/src/main/res/layout/fragment_browse_cloud_connections.xml index 4da30d58..c0930487 100644 --- a/presentation/src/main/res/layout/fragment_browse_cloud_connections.xml +++ b/presentation/src/main/res/layout/fragment_browse_cloud_connections.xml @@ -9,38 +9,10 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - - - - - - - - diff --git a/presentation/src/main/res/values/strings.xml b/presentation/src/main/res/values/strings.xml index 8a87b0b0..d9312db4 100644 --- a/presentation/src/main/res/values/strings.xml +++ b/presentation/src/main/res/values/strings.xml @@ -167,7 +167,6 @@ Click here to add locations Server doesn\'t seem to be WebDAV compatible Custom locations - Default storage No additional locations available. diff --git a/presentation/src/notFoss/java/org/cryptomator/presentation/presenter/AuthenticateCloudPresenter.kt b/presentation/src/notFoss/java/org/cryptomator/presentation/presenter/AuthenticateCloudPresenter.kt index 8f33fa66..f542dd4d 100644 --- a/presentation/src/notFoss/java/org/cryptomator/presentation/presenter/AuthenticateCloudPresenter.kt +++ b/presentation/src/notFoss/java/org/cryptomator/presentation/presenter/AuthenticateCloudPresenter.kt @@ -1,6 +1,5 @@ package org.cryptomator.presentation.presenter -import android.Manifest import android.accounts.AccountManager import android.content.ActivityNotFoundException import android.widget.Toast @@ -38,6 +37,7 @@ import org.cryptomator.presentation.intent.AuthenticateCloudIntent import org.cryptomator.presentation.intent.Intents import org.cryptomator.presentation.model.CloudModel import org.cryptomator.presentation.model.CloudTypeModel +import org.cryptomator.presentation.model.LocalStorageModel import org.cryptomator.presentation.model.ProgressModel import org.cryptomator.presentation.model.ProgressStateModel import org.cryptomator.presentation.model.S3CloudModel @@ -47,7 +47,6 @@ import org.cryptomator.presentation.ui.activity.view.AuthenticateCloudView import org.cryptomator.presentation.workflow.ActivityResult import org.cryptomator.presentation.workflow.AddExistingVaultWorkflow import org.cryptomator.presentation.workflow.CreateNewVaultWorkflow -import org.cryptomator.presentation.workflow.PermissionsResult import org.cryptomator.presentation.workflow.Workflow import org.cryptomator.util.ExceptionUtil import org.cryptomator.util.crypto.CredentialCryptor @@ -491,20 +490,15 @@ class AuthenticateCloudPresenter @Inject constructor( // private fun startAuthentication(cloud: CloudModel) { authenticationStarted = true - requestPermissions( - PermissionsResultCallbacks.onLocalStorageAuthenticated(cloud), // - R.string.permission_snackbar_auth_local_vault, // - Manifest.permission.READ_EXTERNAL_STORAGE, // - Manifest.permission.WRITE_EXTERNAL_STORAGE - ) - } - } + val permissions = context().contentResolver.persistedUriPermissions + for (permission in permissions) { + if (permission.uri.toString() == (cloud as LocalStorageModel).uri()) { + succeedAuthenticationWith(cloud.toCloud()) + } + } - @Callback - fun onLocalStorageAuthenticated(result: PermissionsResult, cloud: CloudModel) { - if (result.granted()) { - succeedAuthenticationWith(cloud.toCloud()) - } else { + // FIXME think about how to re-request permission + // FIXME change in the FOSS variant too failAuthentication(PermissionNotGrantedException(R.string.permission_snackbar_auth_local_vault)) } } diff --git a/util/src/main/java/org/cryptomator/util/crypto/CryptoOperationsImpl.java b/util/src/main/java/org/cryptomator/util/crypto/CryptoOperationsImpl.java index 85b11a79..04acdceb 100644 --- a/util/src/main/java/org/cryptomator/util/crypto/CryptoOperationsImpl.java +++ b/util/src/main/java/org/cryptomator/util/crypto/CryptoOperationsImpl.java @@ -1,7 +1,6 @@ package org.cryptomator.util.crypto; import android.content.Context; -import android.os.Build; import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.KeyProperties; @@ -43,13 +42,10 @@ class CryptoOperationsImpl implements CryptoOperations { KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec // .Builder(alias, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) // .setBlockModes(KeyProperties.BLOCK_MODE_CBC) // - .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7); + .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7) // + .setUserAuthenticationRequired(requireUserAuthentication) // + .setInvalidatedByBiometricEnrollment(requireUserAuthentication); - builder.setUserAuthenticationRequired(requireUserAuthentication); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - builder.setInvalidatedByBiometricEnrollment(requireUserAuthentication); - } generator.init(builder.build()); generator.generateKey(); };