diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessageMediaRepo.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessageMediaRepo.kt index cc8bd1945f..62ddc4845b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessageMediaRepo.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessageMediaRepo.kt @@ -24,6 +24,7 @@ import io.element.android.libraries.di.CacheDirectory import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.media.MatrixMediaLoader import io.element.android.libraries.matrix.api.media.MediaSource +import io.element.android.libraries.matrix.api.mxc.MxcTools import java.io.File /** @@ -66,6 +67,7 @@ interface VoiceMessageMediaRepo { class DefaultVoiceMessageMediaRepo @AssistedInject constructor( @CacheDirectory private val cacheDir: File, + mxcTools: MxcTools, private val matrixMediaLoader: MatrixMediaLoader, @Assisted private val mediaSource: MediaSource, @Assisted("mimeType") private val mimeType: String?, @@ -101,7 +103,7 @@ class DefaultVoiceMessageMediaRepo @AssistedInject constructor( } } - private val cachedFile: File? = mxcUri2FilePath(mediaSource.url)?.let { + private val cachedFile: File? = mxcTools.mxcUri2FilePath(mediaSource.url)?.let { File("${cacheDir.path}/$CACHE_VOICE_SUBDIR/$it") } } @@ -110,24 +112,3 @@ class DefaultVoiceMessageMediaRepo @AssistedInject constructor( * Subdirectory of the application's cache directory where voice messages are stored. */ private const val CACHE_VOICE_SUBDIR = "temp/voice" - -/** - * Regex to match a Matrix Content (mxc://) URI. - * - * See: https://spec.matrix.org/v1.8/client-server-api/#matrix-content-mxc-uris - */ -private val mxcRegex = Regex("""^mxc:\/\/([^\/]+)\/([^\/]+)$""") - -/** - * Sanitizes an mxcUri to be used as a relative file path. - * - * @param mxcUri the Matrix Content (mxc://) URI of the voice message. - * @return the relative file path as "/" or null if the mxcUri is invalid. - */ -private fun mxcUri2FilePath(mxcUri: String): String? = mxcRegex.matchEntire(mxcUri)?.let { match -> - buildString { - append(match.groupValues[1]) - append("/") - append(match.groupValues[2]) - } -} diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/DefaultVoiceMessageMediaRepoTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/DefaultVoiceMessageMediaRepoTest.kt index 0a62d6ca91..0208d06e95 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/DefaultVoiceMessageMediaRepoTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/DefaultVoiceMessageMediaRepoTest.kt @@ -20,6 +20,7 @@ import com.google.common.truth.Truth.assertThat import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.matrix.api.media.MatrixMediaLoader import io.element.android.libraries.matrix.api.media.MediaSource +import io.element.android.libraries.matrix.api.mxc.MxcTools import io.element.android.libraries.matrix.test.media.FakeMediaLoader import kotlinx.coroutines.test.runTest import org.junit.Rule @@ -139,6 +140,7 @@ private fun createDefaultVoiceMessageMediaRepo( mxcUri: String = MXC_URI, ) = DefaultVoiceMessageMediaRepo( cacheDir = temporaryFolder.root, + mxcTools = MxcTools(), matrixMediaLoader = matrixMediaLoader, mediaSource = MediaSource( url = mxcUri, diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/mxc/MxcTools.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/mxc/MxcTools.kt new file mode 100644 index 0000000000..43a78d1d23 --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/mxc/MxcTools.kt @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.matrix.api.mxc + +import javax.inject.Inject + +class MxcTools @Inject constructor() { + /** + * Regex to match a Matrix Content (mxc://) URI. + * + * See: https://spec.matrix.org/v1.8/client-server-api/#matrix-content-mxc-uris + */ + private val mxcRegex = Regex("""^mxc://([^/]+)/([^/]+)$""") + + /** + * Sanitizes an mxcUri to be used as a relative file path. + * + * @param mxcUri the Matrix Content (mxc://) URI of the file. + * @return the relative file path as "/" or null if the mxcUri is invalid. + */ + fun mxcUri2FilePath(mxcUri: String): String? = mxcRegex.matchEntire(mxcUri)?.let { match -> + buildString { + append(match.groupValues[1]) + append("/") + append(match.groupValues[2]) + } + } +} diff --git a/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/mxc/MxcTools.kt b/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/mxc/MxcTools.kt new file mode 100644 index 0000000000..652b5a6de6 --- /dev/null +++ b/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/mxc/MxcTools.kt @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.matrix.api.mxc + +import com.google.common.truth.Truth.assertThat +import org.junit.Test + +class MxcToolsTest { + @Test + fun `mxcUri2FilePath returns extracted path`() { + val mxcTools = MxcTools() + val mxcUri = "mxc://server.org/abc123" + val filePath = mxcTools.mxcUri2FilePath(mxcUri) + assertThat(filePath).isEqualTo("server.org/abc123") + } + + @Test + fun `mxcUri2FilePath returns null for invalid data`() { + val mxcTools = MxcTools() + assertThat(mxcTools.mxcUri2FilePath("")).isNull() + assertThat(mxcTools.mxcUri2FilePath("mxc://server.org")).isNull() + assertThat(mxcTools.mxcUri2FilePath("mxc://server.org/")).isNull() + assertThat(mxcTools.mxcUri2FilePath("m://server.org/abc123")).isNull() + } +} diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationMediaRepo.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationMediaRepo.kt index 3676ff961e..505a4f4a3c 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationMediaRepo.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationMediaRepo.kt @@ -24,6 +24,7 @@ import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.CacheDirectory import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.media.MediaSource +import io.element.android.libraries.matrix.api.mxc.MxcTools import java.io.File /** @@ -68,6 +69,7 @@ interface NotificationMediaRepo { class DefaultNotificationMediaRepo @AssistedInject constructor( @CacheDirectory private val cacheDir: File, + private val mxcTools: MxcTools, @Assisted private val client: MatrixClient, ) : NotificationMediaRepo { @@ -107,7 +109,7 @@ class DefaultNotificationMediaRepo @AssistedInject constructor( } } - private fun MediaSource.cachedFile(): File? = mxcUri2FilePath(url)?.let { + private fun MediaSource.cachedFile(): File? = mxcTools.mxcUri2FilePath(url)?.let { File("${cacheDir.path}/$CACHE_NOTIFICATION_SUBDIR/$it") } } @@ -116,24 +118,3 @@ class DefaultNotificationMediaRepo @AssistedInject constructor( * Subdirectory of the application's cache directory where file are stored. */ private const val CACHE_NOTIFICATION_SUBDIR = "temp/notif" - -/** - * Regex to match a Matrix Content (mxc://) URI. - * - * See: https://spec.matrix.org/v1.8/client-server-api/#matrix-content-mxc-uris - */ -private val mxcRegex = Regex("""^mxc:\/\/([^\/]+)\/([^\/]+)$""") - -/** - * Sanitizes an mxcUri to be used as a relative file path. - * - * @param mxcUri the Matrix Content (mxc://) URI of the file. - * @return the relative file path as "/" or null if the mxcUri is invalid. - */ -private fun mxcUri2FilePath(mxcUri: String): String? = mxcRegex.matchEntire(mxcUri)?.let { match -> - buildString { - append(match.groupValues[1]) - append("/") - append(match.groupValues[2]) - } -}