diff --git a/opencloudApp/src/main/AndroidManifest.xml b/opencloudApp/src/main/AndroidManifest.xml index 0d6ba96aa..ca76bb9ca 100644 --- a/opencloudApp/src/main/AndroidManifest.xml +++ b/opencloudApp/src/main/AndroidManifest.xml @@ -23,6 +23,7 @@ API >= 23; the app needs to handle this --> + + Download all files + Download all files from your cloud for offline access (requires significant storage) + Download Everything + This will download ALL files from your cloud. This may use significant storage space and bandwidth. Continue? + + + Auto-sync local changes + Automatically upload changes to locally modified files + Auto-Sync + Local file changes will be automatically synced to the cloud. This requires a stable network connection. Continue? + + + Prefer local version on conflict + When a file is modified both locally and on server, upload local version instead of creating a conflicted copy + diff --git a/opencloudApp/src/main/res/xml/settings_security.xml b/opencloudApp/src/main/res/xml/settings_security.xml index 91c72bb0d..3e2888145 100644 --- a/opencloudApp/src/main/res/xml/settings_security.xml +++ b/opencloudApp/src/main/res/xml/settings_security.xml @@ -49,4 +49,25 @@ app:summary="@string/prefs_touches_with_other_visible_windows_summary" app:title="@string/prefs_touches_with_other_visible_windows" /> + + + + + + + + + \ No newline at end of file diff --git a/opencloudComLibrary/src/main/java/eu/opencloud/android/lib/resources/users/GetRemoteUserAvatarOperation.kt b/opencloudComLibrary/src/main/java/eu/opencloud/android/lib/resources/users/GetRemoteUserAvatarOperation.kt index 9e355c03c..6f0bfd4b1 100644 --- a/opencloudComLibrary/src/main/java/eu/opencloud/android/lib/resources/users/GetRemoteUserAvatarOperation.kt +++ b/opencloudComLibrary/src/main/java/eu/opencloud/android/lib/resources/users/GetRemoteUserAvatarOperation.kt @@ -31,7 +31,6 @@ import eu.opencloud.android.lib.common.network.WebdavUtils import eu.opencloud.android.lib.common.operations.RemoteOperation import eu.opencloud.android.lib.common.operations.RemoteOperationResult import timber.log.Timber -import java.io.File import java.io.IOException import java.io.InputStream import java.net.URL @@ -42,14 +41,14 @@ import java.net.URL * @author David A. Velasco * @author David González Verdugo */ +@Suppress("UnusedPrivateProperty") class GetRemoteUserAvatarOperation(private val avatarDimension: Int) : RemoteOperation() { override fun run(client: OpenCloudClient): RemoteOperationResult { var inputStream: InputStream? = null var result: RemoteOperationResult try { - val endPoint = - client.baseUri.toString() + NON_OFFICIAL_AVATAR_PATH + client.credentials.username + File.separator + avatarDimension + val endPoint = client.baseUri.toString() + GRAPH_AVATAR_PATH Timber.d("avatar URI: %s", endPoint) val getMethod = GetMethod(URL(endPoint)) @@ -109,6 +108,6 @@ class GetRemoteUserAvatarOperation(private val avatarDimension: Int) : RemoteOpe private fun isSuccess(status: Int) = status == HttpConstants.HTTP_OK companion object { - private const val NON_OFFICIAL_AVATAR_PATH = "/index.php/avatar/" + private const val GRAPH_AVATAR_PATH = "/graph/v1.0/me/photo/\$value" } } diff --git a/opencloudData/src/main/java/eu/opencloud/android/data/providers/LocalStorageProvider.kt b/opencloudData/src/main/java/eu/opencloud/android/data/providers/LocalStorageProvider.kt index c41846fa6..fde8bdcfb 100644 --- a/opencloudData/src/main/java/eu/opencloud/android/data/providers/LocalStorageProvider.kt +++ b/opencloudData/src/main/java/eu/opencloud/android/data/providers/LocalStorageProvider.kt @@ -49,7 +49,10 @@ sealed class LocalStorageProvider(private val rootFolderName: String) { /** * Get local storage path for accountName. */ - private fun getAccountDirectoryPath( + /** + * Get local storage path for accountName. + */ + protected open fun getAccountDirectoryPath( accountName: String ): String = getRootFolderPath() + File.separator + getEncodedAccountName(accountName) @@ -62,9 +65,15 @@ sealed class LocalStorageProvider(private val rootFolderName: String) { accountName: String, remotePath: String, spaceId: String?, + spaceName: String? = null, ): String = if (spaceId != null) { - getAccountDirectoryPath(accountName) + File.separator + spaceId + File.separator + remotePath + val spaceFolder = if (!spaceName.isNullOrBlank()) { + spaceName.replace("/", "_").replace("\\", "_").replace(":", "_") + } else { + spaceId + } + getAccountDirectoryPath(accountName) + File.separator + spaceFolder + File.separator + remotePath } else { getAccountDirectoryPath(accountName) + remotePath } diff --git a/opencloudData/src/main/java/eu/opencloud/android/data/providers/ScopedStorageProvider.kt b/opencloudData/src/main/java/eu/opencloud/android/data/providers/ScopedStorageProvider.kt index a9a4c996c..fce95812c 100644 --- a/opencloudData/src/main/java/eu/opencloud/android/data/providers/ScopedStorageProvider.kt +++ b/opencloudData/src/main/java/eu/opencloud/android/data/providers/ScopedStorageProvider.kt @@ -20,12 +20,19 @@ package eu.opencloud.android.data.providers import android.content.Context +import android.os.Environment import java.io.File +@Suppress("UnusedPrivateProperty") class ScopedStorageProvider( rootFolderName: String, private val context: Context ) : LocalStorageProvider(rootFolderName) { - override fun getPrimaryStorageDirectory(): File = context.filesDir + override fun getPrimaryStorageDirectory(): File = Environment.getExternalStorageDirectory() + + override fun getAccountDirectoryPath(accountName: String): String { + val sanitizedName = accountName.replace("/", "_").replace("\\", "_").replace(":", "_") + return getRootFolderPath() + File.separator + sanitizedName + } } diff --git a/opencloudData/src/test/java/eu/opencloud/android/data/providers/ScopedStorageProviderTest.kt b/opencloudData/src/test/java/eu/opencloud/android/data/providers/ScopedStorageProviderTest.kt index d01ab0ae0..fbc213f42 100644 --- a/opencloudData/src/test/java/eu/opencloud/android/data/providers/ScopedStorageProviderTest.kt +++ b/opencloudData/src/test/java/eu/opencloud/android/data/providers/ScopedStorageProviderTest.kt @@ -2,13 +2,17 @@ package eu.opencloud.android.data.providers import android.content.Context import android.net.Uri +import android.os.Environment import eu.opencloud.android.domain.transfers.model.OCTransfer import eu.opencloud.android.testutil.OC_FILE import eu.opencloud.android.testutil.OC_SPACE_PROJECT_WITH_IMAGE import io.mockk.every import io.mockk.mockk import io.mockk.mockkStatic +import io.mockk.unmockkAll +import io.mockk.spyk import io.mockk.verify +import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Before @@ -43,17 +47,25 @@ class ScopedStorageProviderTest { File(this, "child.bin").writeBytes(ByteArray(expectedSizeOfDirectoryValue.toInt())) } - scopedStorageProvider = ScopedStorageProvider(rootFolderName, context) + mockkStatic(Environment::class) + every { Environment.getExternalStorageDirectory() } returns filesDir + + scopedStorageProvider = spyk(ScopedStorageProvider(rootFolderName, context)) every { context.filesDir } returns filesDir } + @After + fun tearDown() { + unmockkAll() + } + @Test fun `getPrimaryStorageDirectory returns filesDir`() { val result = scopedStorageProvider.getPrimaryStorageDirectory() assertEquals(filesDir, result) verify(exactly = 1) { - context.filesDir + Environment.getExternalStorageDirectory() } } @@ -71,10 +83,8 @@ class ScopedStorageProviderTest { @Test fun `getDefaultSavePathFor returns the path with spaces when there is a space`() { - mockkStatic(Uri::class) - every { Uri.encode(accountName, "@") } returns uriEncoded - - val accountDirectoryPath = filesDir.absolutePath + File.separator + rootFolderName + File.separator + uriEncoded + // ScopedStorageProvider overrides getAccountDirectoryPath and does NOT use Uri.encode + val accountDirectoryPath = filesDir.absolutePath + File.separator + rootFolderName + File.separator + accountName val expectedPath = accountDirectoryPath + File.separator + spaceId + File.separator + remotePath val actualPath = scopedStorageProvider.getDefaultSavePathFor(accountName, remotePath, spaceId) @@ -89,10 +99,8 @@ class ScopedStorageProviderTest { fun `getDefaultSavePathFor returns the path without spaces when there is not space`() { val spaceId = null - mockkStatic(Uri::class) - every { Uri.encode(accountName, "@") } returns uriEncoded - - val accountDirectoryPath = filesDir.absolutePath + File.separator + rootFolderName + File.separator + uriEncoded + // ScopedStorageProvider overrides getAccountDirectoryPath and does NOT use Uri.encode + val accountDirectoryPath = filesDir.absolutePath + File.separator + rootFolderName + File.separator + accountName val expectedPath = accountDirectoryPath + remotePath val actualPath = scopedStorageProvider.getDefaultSavePathFor(accountName, remotePath, spaceId)