Fix / Support open and view of sending attachment
This commit is contained in:
parent
e02b9b7736
commit
8103081e0e
@ -34,7 +34,8 @@ interface VideoLoaderTarget {
|
|||||||
|
|
||||||
fun onVideoFileLoading(uid: String)
|
fun onVideoFileLoading(uid: String)
|
||||||
fun onVideoFileLoadFailed(uid: String)
|
fun onVideoFileLoadFailed(uid: String)
|
||||||
fun onVideoFileReady(uid: String, file: File)
|
fun onVideoURLReady(uid: String, file: File)
|
||||||
|
fun onVideoURLReady(uid: String, path: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class DefaultVideoLoaderTarget(val holder: VideoViewHolder, private val contextView: ImageView) : VideoLoaderTarget {
|
internal class DefaultVideoLoaderTarget(val holder: VideoViewHolder, private val contextView: ImageView) : VideoLoaderTarget {
|
||||||
@ -66,11 +67,21 @@ internal class DefaultVideoLoaderTarget(val holder: VideoViewHolder, private val
|
|||||||
holder.videoFileLoadError()
|
holder.videoFileLoadError()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onVideoFileReady(uid: String, file: File) {
|
override fun onVideoURLReady(uid: String, file: File) {
|
||||||
if (holder.boundResourceUid != uid) return
|
if (holder.boundResourceUid != uid) return
|
||||||
|
arrangeForVideoReady()
|
||||||
|
holder.videoReady(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onVideoURLReady(uid: String, contentPath: String) {
|
||||||
|
if (holder.boundResourceUid != uid) return
|
||||||
|
arrangeForVideoReady()
|
||||||
|
holder.videoReady(contentPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun arrangeForVideoReady() {
|
||||||
holder.thumbnailImage.isVisible = false
|
holder.thumbnailImage.isVisible = false
|
||||||
holder.loaderProgressBar.isVisible = false
|
holder.loaderProgressBar.isVisible = false
|
||||||
holder.videoView.isVisible = true
|
holder.videoView.isVisible = true
|
||||||
holder.videoReady(file)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,6 +65,13 @@ class VideoViewHolder constructor(itemView: View) :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun videoReady(path: String) {
|
||||||
|
mVideoPath = path
|
||||||
|
if (isSelected) {
|
||||||
|
startPlaying()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun videoFileLoadError() {
|
fun videoFileLoadError() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ sealed class RoomDetailAction : VectorViewModelAction {
|
|||||||
data class UpdateQuickReactAction(val targetEventId: String, val selectedReaction: String, val add: Boolean) : RoomDetailAction()
|
data class UpdateQuickReactAction(val targetEventId: String, val selectedReaction: String, val add: Boolean) : RoomDetailAction()
|
||||||
data class NavigateToEvent(val eventId: String, val highlight: Boolean) : RoomDetailAction()
|
data class NavigateToEvent(val eventId: String, val highlight: Boolean) : RoomDetailAction()
|
||||||
object MarkAllAsRead : RoomDetailAction()
|
object MarkAllAsRead : RoomDetailAction()
|
||||||
data class DownloadOrOpen(val eventId: String, val messageFileContent: MessageWithAttachmentContent) : RoomDetailAction()
|
data class DownloadOrOpen(val eventId: String, val senderId: String?, val messageFileContent: MessageWithAttachmentContent) : RoomDetailAction()
|
||||||
data class HandleTombstoneEvent(val event: Event) : RoomDetailAction()
|
data class HandleTombstoneEvent(val event: Event) : RoomDetailAction()
|
||||||
object AcceptInvite : RoomDetailAction()
|
object AcceptInvite : RoomDetailAction()
|
||||||
object RejectInvite : RoomDetailAction()
|
object RejectInvite : RoomDetailAction()
|
||||||
|
@ -1423,7 +1423,7 @@ class RoomDetailFragment @Inject constructor(
|
|||||||
roomDetailViewModel.handle(RoomDetailAction.ResumeVerification(informationData.eventId, null))
|
roomDetailViewModel.handle(RoomDetailAction.ResumeVerification(informationData.eventId, null))
|
||||||
}
|
}
|
||||||
is MessageWithAttachmentContent -> {
|
is MessageWithAttachmentContent -> {
|
||||||
val action = RoomDetailAction.DownloadOrOpen(informationData.eventId, messageContent)
|
val action = RoomDetailAction.DownloadOrOpen(informationData.eventId, informationData.senderId, messageContent)
|
||||||
roomDetailViewModel.handle(action)
|
roomDetailViewModel.handle(action)
|
||||||
}
|
}
|
||||||
is EncryptedEventContent -> {
|
is EncryptedEventContent -> {
|
||||||
|
@ -57,6 +57,7 @@ import org.commonmark.renderer.html.HtmlRenderer
|
|||||||
import org.matrix.android.sdk.api.MatrixCallback
|
import org.matrix.android.sdk.api.MatrixCallback
|
||||||
import org.matrix.android.sdk.api.MatrixPatterns
|
import org.matrix.android.sdk.api.MatrixPatterns
|
||||||
import org.matrix.android.sdk.api.NoOpMatrixCallback
|
import org.matrix.android.sdk.api.NoOpMatrixCallback
|
||||||
|
import org.matrix.android.sdk.api.extensions.tryThis
|
||||||
import org.matrix.android.sdk.api.query.QueryStringValue
|
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
|
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
|
||||||
@ -990,8 +991,18 @@ class RoomDetailViewModel @AssistedInject constructor(
|
|||||||
|
|
||||||
private fun handleOpenOrDownloadFile(action: RoomDetailAction.DownloadOrOpen) {
|
private fun handleOpenOrDownloadFile(action: RoomDetailAction.DownloadOrOpen) {
|
||||||
val mxcUrl = action.messageFileContent.getFileUrl()
|
val mxcUrl = action.messageFileContent.getFileUrl()
|
||||||
|
val isLocalSendingFile = action.senderId == session.myUserId
|
||||||
|
&& action.messageFileContent.getFileUrl()?.startsWith("content://") ?: false
|
||||||
val isDownloaded = mxcUrl?.let { session.fileService().isFileInCache(it, action.messageFileContent.mimeType) } ?: false
|
val isDownloaded = mxcUrl?.let { session.fileService().isFileInCache(it, action.messageFileContent.mimeType) } ?: false
|
||||||
if (isDownloaded) {
|
if (isLocalSendingFile) {
|
||||||
|
tryThis { Uri.parse(mxcUrl) }?.let {
|
||||||
|
_viewEvents.post(RoomDetailViewEvents.OpenFile(
|
||||||
|
action.messageFileContent.mimeType,
|
||||||
|
it,
|
||||||
|
null
|
||||||
|
))
|
||||||
|
}
|
||||||
|
} else if (isDownloaded) {
|
||||||
// we can open it
|
// we can open it
|
||||||
session.fileService().getTemporarySharableURI(mxcUrl!!, action.messageFileContent.mimeType)?.let { uri ->
|
session.fileService().getTemporarySharableURI(mxcUrl!!, action.messageFileContent.mimeType)?.let { uri ->
|
||||||
_viewEvents.post(RoomDetailViewEvents.OpenFile(
|
_viewEvents.post(RoomDetailViewEvents.OpenFile(
|
||||||
|
@ -27,6 +27,7 @@ import im.vector.lib.attachmentviewer.AttachmentSourceProvider
|
|||||||
import im.vector.lib.attachmentviewer.ImageLoaderTarget
|
import im.vector.lib.attachmentviewer.ImageLoaderTarget
|
||||||
import im.vector.lib.attachmentviewer.VideoLoaderTarget
|
import im.vector.lib.attachmentviewer.VideoLoaderTarget
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
import org.matrix.android.sdk.api.MatrixCallback
|
||||||
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
import org.matrix.android.sdk.api.session.file.FileService
|
import org.matrix.android.sdk.api.session.file.FileService
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@ -101,11 +102,7 @@ abstract class BaseAttachmentProvider(val imageContentRenderer: ImageContentRend
|
|||||||
|
|
||||||
override fun loadVideo(target: VideoLoaderTarget, info: AttachmentInfo.Video) {
|
override fun loadVideo(target: VideoLoaderTarget, info: AttachmentInfo.Video) {
|
||||||
val data = info.data as? VideoContentRenderer.Data ?: return
|
val data = info.data as? VideoContentRenderer.Data ?: return
|
||||||
// videoContentRenderer.render(data,
|
|
||||||
// holder.thumbnailImage,
|
|
||||||
// holder.loaderProgressBar,
|
|
||||||
// holder.videoView,
|
|
||||||
// holder.errorTextView)
|
|
||||||
imageContentRenderer.render(data.thumbnailMediaData, target.contextView(), object : CustomViewTarget<ImageView, Drawable>(target.contextView()) {
|
imageContentRenderer.render(data.thumbnailMediaData, target.contextView(), object : CustomViewTarget<ImageView, Drawable>(target.contextView()) {
|
||||||
override fun onLoadFailed(errorDrawable: Drawable?) {
|
override fun onLoadFailed(errorDrawable: Drawable?) {
|
||||||
target.onThumbnailLoadFailed(info.uid, errorDrawable)
|
target.onThumbnailLoadFailed(info.uid, errorDrawable)
|
||||||
@ -120,6 +117,9 @@ abstract class BaseAttachmentProvider(val imageContentRenderer: ImageContentRend
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (data.url?.startsWith("content://").orFalse() && data.allowNonMxcUrls) {
|
||||||
|
target.onVideoURLReady(info.uid, data.url!!)
|
||||||
|
} else {
|
||||||
target.onVideoFileLoading(info.uid)
|
target.onVideoFileLoading(info.uid)
|
||||||
fileService.downloadFile(
|
fileService.downloadFile(
|
||||||
downloadMode = FileService.DownloadMode.FOR_INTERNAL_USE,
|
downloadMode = FileService.DownloadMode.FOR_INTERNAL_USE,
|
||||||
@ -130,7 +130,7 @@ abstract class BaseAttachmentProvider(val imageContentRenderer: ImageContentRend
|
|||||||
url = data.url,
|
url = data.url,
|
||||||
callback = object : MatrixCallback<File> {
|
callback = object : MatrixCallback<File> {
|
||||||
override fun onSuccess(data: File) {
|
override fun onSuccess(data: File) {
|
||||||
target.onVideoFileReady(info.uid, data)
|
target.onVideoURLReady(info.uid, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(failure: Throwable) {
|
override fun onFailure(failure: Throwable) {
|
||||||
@ -139,6 +139,7 @@ abstract class BaseAttachmentProvider(val imageContentRenderer: ImageContentRend
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun clear(id: String) {
|
override fun clear(id: String) {
|
||||||
// TODO("Not yet implemented")
|
// TODO("Not yet implemented")
|
||||||
|
@ -51,6 +51,8 @@ interface AttachmentData : Parcelable {
|
|||||||
val mimeType: String?
|
val mimeType: String?
|
||||||
val url: String?
|
val url: String?
|
||||||
val elementToDecrypt: ElementToDecrypt?
|
val elementToDecrypt: ElementToDecrypt?
|
||||||
|
// If true will load non mxc url, be careful to set it only for images sent by you
|
||||||
|
val allowNonMxcUrls: Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
class ImageContentRenderer @Inject constructor(private val activeSessionHolder: ActiveSessionHolder,
|
class ImageContentRenderer @Inject constructor(private val activeSessionHolder: ActiveSessionHolder,
|
||||||
@ -66,7 +68,9 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder:
|
|||||||
val height: Int?,
|
val height: Int?,
|
||||||
val maxHeight: Int,
|
val maxHeight: Int,
|
||||||
val width: Int?,
|
val width: Int?,
|
||||||
val maxWidth: Int
|
val maxWidth: Int,
|
||||||
|
// If true will load non mxc url, be careful to set it only for images sent by you
|
||||||
|
override val allowNonMxcUrls: Boolean = false
|
||||||
) : AttachmentData {
|
) : AttachmentData {
|
||||||
|
|
||||||
fun isLocalFile() = url.isLocalFile()
|
fun isLocalFile() = url.isLocalFile()
|
||||||
@ -121,7 +125,8 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder:
|
|||||||
.load(data)
|
.load(data)
|
||||||
} else {
|
} else {
|
||||||
// Clear image
|
// Clear image
|
||||||
val resolvedUrl = activeSessionHolder.getActiveSession().contentUrlResolver().resolveFullSize(data.url)
|
val resolvedUrl = resolveUrl(data)
|
||||||
|
?: data.url.takeIf { data.allowNonMxcUrls }
|
||||||
GlideApp
|
GlideApp
|
||||||
.with(contextView)
|
.with(contextView)
|
||||||
.load(resolvedUrl)
|
.load(resolvedUrl)
|
||||||
@ -175,7 +180,7 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder:
|
|||||||
.load(data)
|
.load(data)
|
||||||
} else {
|
} else {
|
||||||
// Clear image
|
// Clear image
|
||||||
val resolvedUrl = activeSessionHolder.getActiveSession().contentUrlResolver().resolveFullSize(data.url)
|
val resolvedUrl = resolveUrl(data)
|
||||||
GlideApp
|
GlideApp
|
||||||
.with(imageView)
|
.with(imageView)
|
||||||
.load(resolvedUrl)
|
.load(resolvedUrl)
|
||||||
@ -215,7 +220,7 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder:
|
|||||||
val contentUrlResolver = activeSessionHolder.getActiveSession().contentUrlResolver()
|
val contentUrlResolver = activeSessionHolder.getActiveSession().contentUrlResolver()
|
||||||
val resolvedUrl = when (mode) {
|
val resolvedUrl = when (mode) {
|
||||||
Mode.FULL_SIZE,
|
Mode.FULL_SIZE,
|
||||||
Mode.STICKER -> contentUrlResolver.resolveFullSize(data.url)
|
Mode.STICKER -> resolveUrl(data)
|
||||||
Mode.THUMBNAIL -> contentUrlResolver.resolveThumbnail(data.url, size.width, size.height, ContentUrlResolver.ThumbnailMethod.SCALE)
|
Mode.THUMBNAIL -> contentUrlResolver.resolveThumbnail(data.url, size.width, size.height, ContentUrlResolver.ThumbnailMethod.SCALE)
|
||||||
}
|
}
|
||||||
// Fallback to base url
|
// Fallback to base url
|
||||||
@ -229,7 +234,7 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder:
|
|||||||
error(
|
error(
|
||||||
GlideApp
|
GlideApp
|
||||||
.with(imageView)
|
.with(imageView)
|
||||||
.load(contentUrlResolver.resolveFullSize(data.url))
|
.load(resolveUrl(data))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -242,7 +247,7 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder:
|
|||||||
|
|
||||||
val (width, height) = processSize(data, Mode.THUMBNAIL)
|
val (width, height) = processSize(data, Mode.THUMBNAIL)
|
||||||
val contentUrlResolver = activeSessionHolder.getActiveSession().contentUrlResolver()
|
val contentUrlResolver = activeSessionHolder.getActiveSession().contentUrlResolver()
|
||||||
val fullSize = contentUrlResolver.resolveFullSize(data.url)
|
val fullSize = resolveUrl(data)
|
||||||
val thumbnail = contentUrlResolver.resolveThumbnail(data.url, width, height, ContentUrlResolver.ThumbnailMethod.SCALE)
|
val thumbnail = contentUrlResolver.resolveThumbnail(data.url, width, height, ContentUrlResolver.ThumbnailMethod.SCALE)
|
||||||
|
|
||||||
if (fullSize.isNullOrBlank() || thumbnail.isNullOrBlank()) {
|
if (fullSize.isNullOrBlank() || thumbnail.isNullOrBlank()) {
|
||||||
@ -262,6 +267,10 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder:
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun resolveUrl(data: Data) =
|
||||||
|
(activeSessionHolder.getActiveSession().contentUrlResolver().resolveFullSize(data.url)
|
||||||
|
?: data.url?.takeIf { data.isLocalFile() && data.allowNonMxcUrls })
|
||||||
|
|
||||||
private fun processSize(data: Data, mode: Mode): Size {
|
private fun processSize(data: Data, mode: Mode): Size {
|
||||||
val maxImageWidth = data.maxWidth
|
val maxImageWidth = data.maxWidth
|
||||||
val maxImageHeight = data.maxHeight
|
val maxImageHeight = data.maxHeight
|
||||||
|
@ -63,7 +63,9 @@ class RoomEventsAttachmentProvider(
|
|||||||
maxHeight = -1,
|
maxHeight = -1,
|
||||||
maxWidth = -1,
|
maxWidth = -1,
|
||||||
width = null,
|
width = null,
|
||||||
height = null
|
height = null,
|
||||||
|
allowNonMxcUrls = it.root.sendState.isSending()
|
||||||
|
|
||||||
)
|
)
|
||||||
if (content.mimeType == "image/gif") {
|
if (content.mimeType == "image/gif") {
|
||||||
AttachmentInfo.AnimatedImage(
|
AttachmentInfo.AnimatedImage(
|
||||||
@ -89,7 +91,8 @@ class RoomEventsAttachmentProvider(
|
|||||||
height = content.videoInfo?.height,
|
height = content.videoInfo?.height,
|
||||||
maxHeight = -1,
|
maxHeight = -1,
|
||||||
width = content.videoInfo?.width,
|
width = content.videoInfo?.width,
|
||||||
maxWidth = -1
|
maxWidth = -1,
|
||||||
|
allowNonMxcUrls = it.root.sendState.isSending()
|
||||||
)
|
)
|
||||||
val data = VideoContentRenderer.Data(
|
val data = VideoContentRenderer.Data(
|
||||||
eventId = it.eventId,
|
eventId = it.eventId,
|
||||||
@ -97,7 +100,8 @@ class RoomEventsAttachmentProvider(
|
|||||||
mimeType = content.mimeType,
|
mimeType = content.mimeType,
|
||||||
url = content.getFileUrl(),
|
url = content.getFileUrl(),
|
||||||
elementToDecrypt = content.encryptedFileInfo?.toElementToDecrypt(),
|
elementToDecrypt = content.encryptedFileInfo?.toElementToDecrypt(),
|
||||||
thumbnailMediaData = thumbnailData
|
thumbnailMediaData = thumbnailData,
|
||||||
|
allowNonMxcUrls = it.root.sendState.isSending()
|
||||||
)
|
)
|
||||||
AttachmentInfo.Video(
|
AttachmentInfo.Video(
|
||||||
uid = it.eventId,
|
uid = it.eventId,
|
||||||
|
@ -24,12 +24,14 @@ import androidx.core.view.isVisible
|
|||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.di.ActiveSessionHolder
|
import im.vector.app.core.di.ActiveSessionHolder
|
||||||
import im.vector.app.core.error.ErrorFormatter
|
import im.vector.app.core.error.ErrorFormatter
|
||||||
|
import im.vector.app.core.utils.isLocalFile
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
import org.matrix.android.sdk.api.MatrixCallback
|
||||||
import org.matrix.android.sdk.api.session.file.FileService
|
import org.matrix.android.sdk.api.session.file.FileService
|
||||||
import org.matrix.android.sdk.internal.crypto.attachments.ElementToDecrypt
|
import org.matrix.android.sdk.internal.crypto.attachments.ElementToDecrypt
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.net.URLEncoder
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class VideoContentRenderer @Inject constructor(private val activeSessionHolder: ActiveSessionHolder,
|
class VideoContentRenderer @Inject constructor(private val activeSessionHolder: ActiveSessionHolder,
|
||||||
@ -42,7 +44,9 @@ class VideoContentRenderer @Inject constructor(private val activeSessionHolder:
|
|||||||
override val mimeType: String?,
|
override val mimeType: String?,
|
||||||
override val url: String?,
|
override val url: String?,
|
||||||
override val elementToDecrypt: ElementToDecrypt?,
|
override val elementToDecrypt: ElementToDecrypt?,
|
||||||
val thumbnailMediaData: ImageContentRenderer.Data
|
val thumbnailMediaData: ImageContentRenderer.Data,
|
||||||
|
// If true will load non mxc url, be careful to set it only for video sent by you
|
||||||
|
override val allowNonMxcUrls: Boolean = false
|
||||||
) : AttachmentData
|
) : AttachmentData
|
||||||
|
|
||||||
fun render(data: Data,
|
fun render(data: Data,
|
||||||
@ -60,6 +64,12 @@ class VideoContentRenderer @Inject constructor(private val activeSessionHolder:
|
|||||||
loadingView.isVisible = false
|
loadingView.isVisible = false
|
||||||
errorView.isVisible = true
|
errorView.isVisible = true
|
||||||
errorView.setText(R.string.unknown_error)
|
errorView.setText(R.string.unknown_error)
|
||||||
|
} else if (data.url.isLocalFile() && data.allowNonMxcUrls) {
|
||||||
|
thumbnailView.isVisible = false
|
||||||
|
loadingView.isVisible = false
|
||||||
|
videoView.isVisible = true
|
||||||
|
videoView.setVideoPath(URLEncoder.encode(data.url, Charsets.US_ASCII.displayName()))
|
||||||
|
videoView.start()
|
||||||
} else {
|
} else {
|
||||||
thumbnailView.isVisible = true
|
thumbnailView.isVisible = true
|
||||||
loadingView.isVisible = true
|
loadingView.isVisible = true
|
||||||
@ -91,6 +101,7 @@ class VideoContentRenderer @Inject constructor(private val activeSessionHolder:
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val resolvedUrl = contentUrlResolver.resolveFullSize(data.url)
|
val resolvedUrl = contentUrlResolver.resolveFullSize(data.url)
|
||||||
|
?: data.url?.takeIf { data.url.isLocalFile() && data.allowNonMxcUrls }
|
||||||
|
|
||||||
if (resolvedUrl == null) {
|
if (resolvedUrl == null) {
|
||||||
thumbnailView.isVisible = false
|
thumbnailView.isVisible = false
|
||||||
|
Loading…
x
Reference in New Issue
Block a user