diff --git a/app/src/main/java/im/vector/riotredesign/features/home/AvatarRenderer.kt b/app/src/main/java/im/vector/riotredesign/features/home/AvatarRenderer.kt index 34091211..68dc1cfb 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/AvatarRenderer.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/AvatarRenderer.kt @@ -4,16 +4,13 @@ import android.widget.ImageView import androidx.core.content.ContextCompat import com.amulyakhare.textdrawable.TextDrawable import com.bumptech.glide.request.RequestOptions -import im.vector.matrix.android.api.session.content.ContentUrlResolver +import im.vector.matrix.android.api.Matrix import im.vector.matrix.android.api.session.room.model.RoomMember import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.riotredesign.R import im.vector.riotredesign.core.extensions.firstCharAsString import im.vector.riotredesign.core.glide.GlideApp -private const val MEDIA_URL = "https://matrix.org/_matrix/media/v1/download/" -private const val MXC_PREFIX = "mxc://" - object AvatarRenderer { fun render(roomMember: RoomMember, imageView: ImageView) { @@ -28,7 +25,7 @@ object AvatarRenderer { if (name.isNullOrEmpty()) { return } - val resolvedUrl = ContentUrlResolver.resolve(avatarUrl) + val resolvedUrl = Matrix.getInstance().currentSession.contentUrlResolver().resolveFullSize(avatarUrl) val avatarColor = ContextCompat.getColor(imageView.context, R.color.pale_teal) val fallbackDrawable = TextDrawable.builder().buildRound(name.firstCharAsString().toUpperCase(), avatarColor) diff --git a/app/src/main/java/im/vector/riotredesign/features/media/MessageImageRenderer.kt b/app/src/main/java/im/vector/riotredesign/features/media/MessageImageRenderer.kt index 87eb6abc..7b437ef9 100644 --- a/app/src/main/java/im/vector/riotredesign/features/media/MessageImageRenderer.kt +++ b/app/src/main/java/im/vector/riotredesign/features/media/MessageImageRenderer.kt @@ -5,6 +5,7 @@ import android.graphics.Point import android.media.ExifInterface import android.view.WindowManager import android.widget.ImageView +import im.vector.matrix.android.api.Matrix import im.vector.matrix.android.api.session.content.ContentUrlResolver import im.vector.matrix.android.api.session.room.model.message.MessageImageContent import im.vector.riotredesign.core.glide.GlideApp @@ -48,12 +49,17 @@ object MessageImageRenderer { imageView.layoutParams.height = finalHeight imageView.layoutParams.width = finalWidth - val resolvedUrl = ContentUrlResolver.resolve(messageContent.url) ?: return + val contentUrlResolver = Matrix.getInstance().currentSession.contentUrlResolver() + val resolvedUrl = contentUrlResolver.resolveThumbnail( + messageContent.url, + finalWidth, + finalHeight, + ContentUrlResolver.ThumbnailMethod.SCALE + ) ?: return + GlideApp .with(imageView) .load(resolvedUrl) - .override(finalWidth, finalHeight) - .thumbnail(0.3f) .into(imageView) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/Matrix.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/Matrix.kt index 5326a796..29d1292b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/Matrix.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/Matrix.kt @@ -1,7 +1,7 @@ package im.vector.matrix.android.api -import androidx.lifecycle.ProcessLifecycleOwner import android.content.Context +import androidx.lifecycle.ProcessLifecycleOwner import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.auth.Authenticator import im.vector.matrix.android.api.session.Session diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt index 7157e764..00205e00 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt @@ -2,6 +2,7 @@ package im.vector.matrix.android.api.session import androidx.annotation.MainThread import im.vector.matrix.android.api.auth.data.SessionParams +import im.vector.matrix.android.api.session.content.ContentUrlResolver import im.vector.matrix.android.api.session.group.GroupService import im.vector.matrix.android.api.session.room.RoomService @@ -15,6 +16,8 @@ interface Session : RoomService, GroupService { @MainThread fun close() + fun contentUrlResolver(): ContentUrlResolver + fun addListener(listener: Listener) fun removeListener(listener: Listener) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/content/ContentUrlResolver.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/content/ContentUrlResolver.kt index a8f116fb..3274d45f 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/content/ContentUrlResolver.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/content/ContentUrlResolver.kt @@ -1,9 +1,32 @@ +/* + * + * * Copyright 2019 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 im.vector.matrix.android.api.session.content -object ContentUrlResolver { +/** + * This interface defines methods for accessing content from the current session. + */ +interface ContentUrlResolver { - private const val MATRIX_CONTENT_URI_SCHEME = "mxc://" - private const val MEDIA_URL = "https://matrix.org/_matrix/media/v1/download/" + enum class ThumbnailMethod(val value: String) { + CROP("crop"), + SCALE("scale") + } /** * Get the actual URL for accessing the full-size image of a Matrix media content URI. @@ -11,21 +34,16 @@ object ContentUrlResolver { * @param contentUrl the Matrix media content URI (in the form of "mxc://..."). * @return the URL to access the described resource, or null if the url is invalid. */ - fun resolve(contentUrl: String?): String? { - if (contentUrl.isValidMatrixContentUrl()) { - return contentUrl?.replace(MATRIX_CONTENT_URI_SCHEME, MEDIA_URL) - } - return null - } + fun resolveFullSize(contentUrl: String?): String? /** - * Check whether an url is a valid matrix content url. + * Get the actual URL for accessing the thumbnail image of a given Matrix media content URI. * - * @param contentUrl the content URL (in the form of "mxc://..."). - * @return true if contentUrl is valid. + * @param contentUrl the Matrix media content URI (in the form of "mxc://..."). + * @param width the desired width + * @param height the desired height + * @param method the desired method (METHOD_CROP or METHOD_SCALE) + * @return the URL to access the described resource, or null if the url is invalid. */ - private fun String?.isValidMatrixContentUrl(): Boolean { - return !this.isNullOrEmpty() && startsWith(MATRIX_CONTENT_URI_SCHEME) - } - + fun resolveThumbnail(contentUrl: String?, width: Int, height: Int, method: ThumbnailMethod): String? } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt index 5ba6e91f..6ec8b232 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt @@ -1,10 +1,11 @@ package im.vector.matrix.android.internal.session -import androidx.lifecycle.LiveData import android.os.Looper import androidx.annotation.MainThread +import androidx.lifecycle.LiveData import im.vector.matrix.android.api.auth.data.SessionParams import im.vector.matrix.android.api.session.Session +import im.vector.matrix.android.api.session.content.ContentUrlResolver import im.vector.matrix.android.api.session.group.Group import im.vector.matrix.android.api.session.group.GroupService import im.vector.matrix.android.api.session.group.model.GroupSummary @@ -35,6 +36,7 @@ internal class DefaultSession(override val sessionParams: SessionParams) : Sessi private val roomService by inject() private val groupService by inject() private val syncThread by inject() + private val contentUrlResolver by inject() private var isOpen = false @MainThread @@ -63,6 +65,10 @@ internal class DefaultSession(override val sessionParams: SessionParams) : Sessi isOpen = false } + override fun contentUrlResolver(): ContentUrlResolver { + return contentUrlResolver + } + override fun addListener(listener: Session.Listener) { sessionListeners.addListener(listener) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt index d36cbcbf..0307d4f2 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt @@ -3,9 +3,11 @@ package im.vector.matrix.android.internal.session import android.content.Context import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.auth.data.SessionParams +import im.vector.matrix.android.api.session.content.ContentUrlResolver import im.vector.matrix.android.api.session.group.GroupService import im.vector.matrix.android.api.session.room.RoomService import im.vector.matrix.android.internal.database.LiveEntityObserver +import im.vector.matrix.android.internal.session.content.DefaultContentUrlResolver import im.vector.matrix.android.internal.session.group.DefaultGroupService import im.vector.matrix.android.internal.session.group.GroupSummaryUpdater import im.vector.matrix.android.internal.session.room.DefaultRoomService @@ -78,6 +80,10 @@ internal class SessionModule(private val sessionParams: SessionParams) { SessionListeners() } + scope(DefaultSession.SCOPE) { + DefaultContentUrlResolver(sessionParams.homeServerConnectionConfig) as ContentUrlResolver + } + scope(DefaultSession.SCOPE) { val roomSummaryUpdater = RoomSummaryUpdater(get(), get(), get(), get(), sessionParams.credentials) val groupSummaryUpdater = GroupSummaryUpdater(get()) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/DefaultContentUrlResolver.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/DefaultContentUrlResolver.kt new file mode 100644 index 00000000..182986e2 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/DefaultContentUrlResolver.kt @@ -0,0 +1,69 @@ +/* + * + * * Copyright 2019 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 im.vector.matrix.android.internal.session.content + +import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig +import im.vector.matrix.android.api.session.content.ContentUrlResolver + + +private const val MATRIX_CONTENT_URI_SCHEME = "mxc://" +private const val URI_PREFIX_CONTENT_API = "/_matrix/media/v1/" + +internal class DefaultContentUrlResolver(private val homeServerConnectionConfig: HomeServerConnectionConfig) : ContentUrlResolver { + + override fun resolveFullSize(contentUrl: String?): String? { + if (contentUrl?.isValidMatrixContentUrl() == true) { + val baseUrl = homeServerConnectionConfig.homeServerUri.toString() + val prefix = URI_PREFIX_CONTENT_API + "download/" + return resolve(baseUrl, contentUrl, prefix) + } + return null + } + + override fun resolveThumbnail(contentUrl: String?, width: Int, height: Int, method: ContentUrlResolver.ThumbnailMethod): String? { + if (contentUrl?.isValidMatrixContentUrl() == true) { + val baseUrl = homeServerConnectionConfig.homeServerUri.toString() + val prefix = URI_PREFIX_CONTENT_API + "thumbnail/" + val params = "?width=$width&height=$height&method=${method.value}" + return resolve(baseUrl, contentUrl, prefix, params) + } + // do not allow non-mxc content URLs + return null + } + + private fun resolve(baseUrl: String, + contentUrl: String, + prefix: String, + params: String? = null): String? { + + var serverAndMediaId = contentUrl.removePrefix(MATRIX_CONTENT_URI_SCHEME) + val fragmentOffset = serverAndMediaId.indexOf("#") + var fragment = "" + if (fragmentOffset >= 0) { + fragment = serverAndMediaId.substring(fragmentOffset) + serverAndMediaId = serverAndMediaId.substring(0, fragmentOffset) + } + return baseUrl + prefix + serverAndMediaId + (params ?: "") + fragment + } + + private fun String.isValidMatrixContentUrl(): Boolean { + return startsWith(MATRIX_CONTENT_URI_SCHEME) + } + +} \ No newline at end of file