Support open from upload media tab

This commit is contained in:
Valere 2020-07-09 15:22:34 +02:00
parent e38cb7c1a6
commit 195e2703b9
10 changed files with 455 additions and 187 deletions

View File

@ -0,0 +1,148 @@
/*
* Copyright (c) 2020 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.riotx.features.media
import android.content.Context
import android.graphics.drawable.Drawable
import android.view.View
import android.widget.ImageView
import com.bumptech.glide.request.target.CustomViewTarget
import com.bumptech.glide.request.transition.Transition
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.file.FileService
import im.vector.riotx.attachmentviewer.AttachmentInfo
import im.vector.riotx.attachmentviewer.AttachmentSourceProvider
import im.vector.riotx.attachmentviewer.ImageLoaderTarget
import im.vector.riotx.attachmentviewer.VideoLoaderTarget
import java.io.File
abstract class BaseAttachmentProvider(val imageContentRenderer: ImageContentRenderer, val fileService: FileService) : AttachmentSourceProvider {
interface InteractionListener {
fun onDismissTapped()
fun onShareTapped()
fun onPlayPause(play: Boolean)
fun videoSeekTo(percent: Int)
}
var interactionListener: InteractionListener? = null
protected var overlayView: AttachmentOverlayView? = null
override fun overlayViewAtPosition(context: Context, position: Int): View? {
if (position == -1) return null
if (overlayView == null) {
overlayView = AttachmentOverlayView(context)
overlayView?.onBack = {
interactionListener?.onDismissTapped()
}
overlayView?.onShareCallback = {
interactionListener?.onShareTapped()
}
overlayView?.onPlayPause = { play ->
interactionListener?.onPlayPause(play)
}
overlayView?.videoSeekTo = { percent ->
interactionListener?.videoSeekTo(percent)
}
}
return overlayView
}
override fun loadImage(target: ImageLoaderTarget, info: AttachmentInfo.Image) {
(info.data as? ImageContentRenderer.Data)?.let {
imageContentRenderer.render(it, target.contextView(), object : CustomViewTarget<ImageView, Drawable>(target.contextView()) {
override fun onLoadFailed(errorDrawable: Drawable?) {
target.onLoadFailed(info.uid, errorDrawable)
}
override fun onResourceCleared(placeholder: Drawable?) {
target.onResourceCleared(info.uid, placeholder)
}
override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
target.onResourceReady(info.uid, resource)
}
})
}
}
override fun loadImage(target: ImageLoaderTarget, info: AttachmentInfo.AnimatedImage) {
(info.data as? ImageContentRenderer.Data)?.let {
imageContentRenderer.render(it, target.contextView(), object : CustomViewTarget<ImageView, Drawable>(target.contextView()) {
override fun onLoadFailed(errorDrawable: Drawable?) {
target.onLoadFailed(info.uid, errorDrawable)
}
override fun onResourceCleared(placeholder: Drawable?) {
target.onResourceCleared(info.uid, placeholder)
}
override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
target.onResourceReady(info.uid, resource)
}
})
}
}
override fun loadVideo(target: VideoLoaderTarget, info: AttachmentInfo.Video) {
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()) {
override fun onLoadFailed(errorDrawable: Drawable?) {
target.onThumbnailLoadFailed(info.uid, errorDrawable)
}
override fun onResourceCleared(placeholder: Drawable?) {
target.onThumbnailResourceCleared(info.uid, placeholder)
}
override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
target.onThumbnailResourceReady(info.uid, resource)
}
})
target.onVideoFileLoading(info.uid)
fileService.downloadFile(
downloadMode = FileService.DownloadMode.FOR_INTERNAL_USE,
id = data.eventId,
mimeType = data.mimeType,
elementToDecrypt = data.elementToDecrypt,
fileName = data.filename,
url = data.url,
callback = object : MatrixCallback<File> {
override fun onSuccess(data: File) {
target.onVideoFileReady(info.uid, data)
}
override fun onFailure(failure: Throwable) {
target.onVideoFileLoadFailed(info.uid)
}
}
)
}
override fun clear(id: String) {
// TODO("Not yet implemented")
}
abstract fun getFileForSharing(position: Int, callback: ((File?) -> Unit))
}

View File

@ -0,0 +1,112 @@
/*
* Copyright (c) 2020 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.riotx.features.media
import android.content.Context
import android.view.View
import androidx.core.view.isVisible
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.events.model.isVideoMessage
import im.vector.matrix.android.api.session.file.FileService
import im.vector.matrix.android.api.session.room.Room
import im.vector.riotx.attachmentviewer.AttachmentInfo
import im.vector.riotx.core.date.VectorDateFormatter
import im.vector.riotx.core.extensions.localDateTime
import java.io.File
class DataAttachmentRoomProvider(
private val attachments: List<AttachmentData>,
private val room: Room?,
private val initialIndex: Int,
imageContentRenderer: ImageContentRenderer,
private val dateFormatter: VectorDateFormatter,
fileService: FileService) : BaseAttachmentProvider(imageContentRenderer, fileService) {
override fun getItemCount(): Int = attachments.size
override fun getAttachmentInfoAt(position: Int): AttachmentInfo {
return attachments[position].let {
when (it) {
is ImageContentRenderer.Data -> {
if (it.mimeType == "image/gif") {
AttachmentInfo.AnimatedImage(
uid = it.eventId,
url = it.url ?: "",
data = it
)
} else {
AttachmentInfo.Image(
uid = it.eventId,
url = it.url ?: "",
data = it
)
}
}
is VideoContentRenderer.Data -> {
AttachmentInfo.Video(
uid = it.eventId,
url = it.url ?: "",
data = it,
thumbnail = AttachmentInfo.Image(
uid = it.eventId,
url = it.thumbnailMediaData.url ?: "",
data = it.thumbnailMediaData
)
)
}
else -> throw IllegalArgumentException()
}
}
}
override fun overlayViewAtPosition(context: Context, position: Int): View? {
super.overlayViewAtPosition(context, position)
val item = attachments[position]
val timeLineEvent = room?.getTimeLineEvent(item.eventId)
if (timeLineEvent != null) {
val dateString = timeLineEvent.root.localDateTime().let {
"${dateFormatter.formatMessageDay(it)} at ${dateFormatter.formatMessageHour(it)} "
}
overlayView?.updateWith("${position + 1} of ${attachments.size}", "${timeLineEvent.senderInfo.displayName} $dateString")
overlayView?.videoControlsGroup?.isVisible = timeLineEvent.root.isVideoMessage()
} else {
overlayView?.updateWith("", "")
}
return overlayView
}
override fun getFileForSharing(position: Int, callback: (File?) -> Unit) {
val item = attachments[position]
fileService.downloadFile(
downloadMode = FileService.DownloadMode.FOR_EXTERNAL_SHARE,
id = item.eventId,
fileName = item.filename,
mimeType = item.mimeType,
url = item.url ?: "",
elementToDecrypt = item.elementToDecrypt,
callback = object : MatrixCallback<File> {
override fun onSuccess(data: File) {
callback(data)
}
override fun onFailure(failure: Throwable) {
callback(null)
}
}
)
}
}

View File

@ -17,17 +17,14 @@
package im.vector.riotx.features.media
import android.content.Context
import android.graphics.drawable.Drawable
import android.view.View
import android.widget.ImageView
import androidx.core.view.isVisible
import com.bumptech.glide.request.target.CustomViewTarget
import com.bumptech.glide.request.transition.Transition
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.events.model.isVideoMessage
import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.file.FileService
import im.vector.matrix.android.api.session.room.Room
import im.vector.matrix.android.api.session.room.model.message.MessageContent
import im.vector.matrix.android.api.session.room.model.message.MessageImageContent
import im.vector.matrix.android.api.session.room.model.message.MessageVideoContent
@ -36,33 +33,18 @@ import im.vector.matrix.android.api.session.room.model.message.getFileUrl
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.internal.crypto.attachments.toElementToDecrypt
import im.vector.riotx.attachmentviewer.AttachmentInfo
import im.vector.riotx.attachmentviewer.AttachmentSourceProvider
import im.vector.riotx.attachmentviewer.ImageLoaderTarget
import im.vector.riotx.attachmentviewer.VideoLoaderTarget
import im.vector.riotx.core.date.VectorDateFormatter
import im.vector.riotx.core.extensions.localDateTime
import java.io.File
import javax.inject.Inject
class RoomAttachmentProvider(
class RoomEventsAttachmentProvider(
private val attachments: List<TimelineEvent>,
private val initialIndex: Int,
private val imageContentRenderer: ImageContentRenderer,
private val videoContentRenderer: VideoContentRenderer,
imageContentRenderer: ImageContentRenderer,
private val dateFormatter: VectorDateFormatter,
private val fileService: FileService
) : AttachmentSourceProvider {
interface InteractionListener {
fun onDismissTapped()
fun onShareTapped()
fun onPlayPause(play: Boolean)
fun videoSeekTo(percent: Int)
}
var interactionListener: InteractionListener? = null
private var overlayView: AttachmentOverlayView? = null
fileService: FileService
) : BaseAttachmentProvider(imageContentRenderer, fileService) {
override fun getItemCount(): Int {
return attachments.size
@ -139,99 +121,8 @@ class RoomAttachmentProvider(
}
}
override fun loadImage(target: ImageLoaderTarget, info: AttachmentInfo.Image) {
(info.data as? ImageContentRenderer.Data)?.let {
imageContentRenderer.render(it, target.contextView(), object : CustomViewTarget<ImageView, Drawable>(target.contextView()) {
override fun onLoadFailed(errorDrawable: Drawable?) {
target.onLoadFailed(info.uid, errorDrawable)
}
override fun onResourceCleared(placeholder: Drawable?) {
target.onResourceCleared(info.uid, placeholder)
}
override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
target.onResourceReady(info.uid, resource)
}
})
}
}
override fun loadImage(target: ImageLoaderTarget, info: AttachmentInfo.AnimatedImage) {
(info.data as? ImageContentRenderer.Data)?.let {
imageContentRenderer.render(it, target.contextView(), object : CustomViewTarget<ImageView, Drawable>(target.contextView()) {
override fun onLoadFailed(errorDrawable: Drawable?) {
target.onLoadFailed(info.uid, errorDrawable)
}
override fun onResourceCleared(placeholder: Drawable?) {
target.onResourceCleared(info.uid, placeholder)
}
override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
target.onResourceReady(info.uid, resource)
}
})
}
}
override fun loadVideo(target: VideoLoaderTarget, info: AttachmentInfo.Video) {
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()) {
override fun onLoadFailed(errorDrawable: Drawable?) {
target.onThumbnailLoadFailed(info.uid, errorDrawable)
}
override fun onResourceCleared(placeholder: Drawable?) {
target.onThumbnailResourceCleared(info.uid, placeholder)
}
override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
target.onThumbnailResourceReady(info.uid, resource)
}
})
target.onVideoFileLoading(info.uid)
fileService.downloadFile(
downloadMode = FileService.DownloadMode.FOR_INTERNAL_USE,
id = data.eventId,
mimeType = data.mimeType,
elementToDecrypt = data.elementToDecrypt,
fileName = data.filename,
url = data.url,
callback = object : MatrixCallback<File> {
override fun onSuccess(data: File) {
target.onVideoFileReady(info.uid, data)
}
override fun onFailure(failure: Throwable) {
target.onVideoFileLoadFailed(info.uid)
}
}
)
}
override fun overlayViewAtPosition(context: Context, position: Int): View? {
if (overlayView == null) {
overlayView = AttachmentOverlayView(context)
overlayView?.onBack = {
interactionListener?.onDismissTapped()
}
overlayView?.onShareCallback = {
interactionListener?.onShareTapped()
}
overlayView?.onPlayPause = { play ->
interactionListener?.onPlayPause(play)
}
overlayView?.videoSeekTo = { percent ->
interactionListener?.videoSeekTo(percent)
}
}
super.overlayViewAtPosition(context, position)
val item = attachments[position]
val dateString = item.root.localDateTime().let {
"${dateFormatter.formatMessageDay(it)} at ${dateFormatter.formatMessageHour(it)} "
@ -241,19 +132,44 @@ class RoomAttachmentProvider(
return overlayView
}
override fun clear(id: String) {
// TODO("Not yet implemented")
override fun getFileForSharing(position: Int, callback: (File?) -> Unit) {
attachments[position].let { timelineEvent ->
val messageContent = timelineEvent.root.getClearContent().toModel<MessageContent>()
as? MessageWithAttachmentContent
?: return@let
fileService.downloadFile(
downloadMode = FileService.DownloadMode.FOR_EXTERNAL_SHARE,
id = timelineEvent.eventId,
fileName = messageContent.body,
mimeType = messageContent.mimeType,
url = messageContent.getFileUrl(),
elementToDecrypt = messageContent.encryptedFileInfo?.toElementToDecrypt(),
callback = object : MatrixCallback<File> {
override fun onSuccess(data: File) {
callback(data)
}
override fun onFailure(failure: Throwable) {
callback(null)
}
}
)
}
}
}
class RoomAttachmentProviderFactory @Inject constructor(
class AttachmentProviderFactory @Inject constructor(
private val imageContentRenderer: ImageContentRenderer,
private val vectorDateFormatter: VectorDateFormatter,
private val videoContentRenderer: VideoContentRenderer,
private val session: Session
) {
fun createProvider(attachments: List<TimelineEvent>, initialIndex: Int): RoomAttachmentProvider {
return RoomAttachmentProvider(attachments, initialIndex, imageContentRenderer, videoContentRenderer, vectorDateFormatter, session.fileService())
fun createProvider(attachments: List<TimelineEvent>, initialIndex: Int): RoomEventsAttachmentProvider {
return RoomEventsAttachmentProvider(attachments, initialIndex, imageContentRenderer, vectorDateFormatter, session.fileService())
}
fun createProvider(attachments: List<AttachmentData>, room: Room?, initialIndex: Int): DataAttachmentRoomProvider {
return DataAttachmentRoomProvider(attachments, room, initialIndex, imageContentRenderer, vectorDateFormatter, session.fileService())
}
}

View File

@ -30,14 +30,6 @@ import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import androidx.transition.Transition
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.file.FileService
import im.vector.matrix.android.api.session.room.model.message.MessageContent
import im.vector.matrix.android.api.session.room.model.message.MessageWithAttachmentContent
import im.vector.matrix.android.api.session.room.model.message.getFileUrl
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.internal.crypto.attachments.toElementToDecrypt
import im.vector.riotx.R
import im.vector.riotx.attachmentviewer.AttachmentCommands
import im.vector.riotx.attachmentviewer.AttachmentViewerActivity
@ -52,11 +44,10 @@ import im.vector.riotx.features.themes.ActivityOtherThemes
import im.vector.riotx.features.themes.ThemeUtils
import kotlinx.android.parcel.Parcelize
import timber.log.Timber
import java.io.File
import javax.inject.Inject
import kotlin.system.measureTimeMillis
class VectorAttachmentViewerActivity : AttachmentViewerActivity(), RoomAttachmentProvider.InteractionListener {
class VectorAttachmentViewerActivity : AttachmentViewerActivity(), BaseAttachmentProvider.InteractionListener {
@Parcelize
data class Args(
@ -69,7 +60,7 @@ class VectorAttachmentViewerActivity : AttachmentViewerActivity(), RoomAttachmen
lateinit var sessionHolder: ActiveSessionHolder
@Inject
lateinit var dataSourceFactory: RoomAttachmentProviderFactory
lateinit var dataSourceFactory: AttachmentProviderFactory
@Inject
lateinit var imageContentRenderer: ImageContentRenderer
@ -78,7 +69,8 @@ class VectorAttachmentViewerActivity : AttachmentViewerActivity(), RoomAttachmen
private var initialIndex = 0
private var isAnimatingOut = false
private var eventList: List<TimelineEvent>? = null
var currentSourceProvider: BaseAttachmentProvider? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -92,13 +84,6 @@ class VectorAttachmentViewerActivity : AttachmentViewerActivity(), RoomAttachmen
ThemeUtils.setActivityTheme(this, getOtherThemes())
val args = args() ?: throw IllegalArgumentException("Missing arguments")
val session = sessionHolder.getSafeActiveSession() ?: return Unit.also { finish() }
val room = args.roomId?.let { session.getRoom(it) }
val events = room?.getAttachmentMessages() ?: emptyList()
eventList = events
val index = events.indexOfFirst { it.eventId == args.eventId }
initialIndex = index
if (savedInstanceState == null && addTransitionListener()) {
args.sharedTransitionName?.let {
@ -127,14 +112,41 @@ class VectorAttachmentViewerActivity : AttachmentViewerActivity(), RoomAttachmen
}
}
val sourceProvider = dataSourceFactory.createProvider(events, index)
sourceProvider.interactionListener = this
setSourceProvider(sourceProvider)
if (savedInstanceState == null) {
pager2.setCurrentItem(index, false)
// The page change listener is not notified of the change...
pager2.post {
onSelectedPositionChanged(index)
val session = sessionHolder.getSafeActiveSession() ?: return Unit.also { finish() }
val room = args.roomId?.let { session.getRoom(it) }
val inMemoryData = intent.getParcelableArrayListExtra<AttachmentData>(EXTRA_IN_MEMORY_DATA)
if (inMemoryData != null) {
val sourceProvider = dataSourceFactory.createProvider(inMemoryData, room, initialIndex)
val index = inMemoryData.indexOfFirst { it.eventId == args.eventId }
initialIndex = index
sourceProvider.interactionListener = this
setSourceProvider(sourceProvider)
this.currentSourceProvider = sourceProvider
if (savedInstanceState == null) {
pager2.setCurrentItem(index, false)
// The page change listener is not notified of the change...
pager2.post {
onSelectedPositionChanged(index)
}
}
} else {
val events = room?.getAttachmentMessages()
?: emptyList()
val index = events.indexOfFirst { it.eventId == args.eventId }
initialIndex = index
val sourceProvider = dataSourceFactory.createProvider(events, index)
sourceProvider.interactionListener = this
setSourceProvider(sourceProvider)
this.currentSourceProvider = sourceProvider
if (savedInstanceState == null) {
pager2.setCurrentItem(index, false)
// The page change listener is not notified of the change...
pager2.post {
onSelectedPositionChanged(index)
}
}
}
@ -228,14 +240,19 @@ class VectorAttachmentViewerActivity : AttachmentViewerActivity(), RoomAttachmen
const val EXTRA_ARGS = "EXTRA_ARGS"
const val EXTRA_IMAGE_DATA = "EXTRA_IMAGE_DATA"
const val EXTRA_IN_MEMORY_DATA = "EXTRA_IN_MEMORY_DATA"
fun newIntent(context: Context,
mediaData: AttachmentData,
roomId: String?,
eventId: String,
inMemoryData: List<AttachmentData>?,
sharedTransitionName: String?) = Intent(context, VectorAttachmentViewerActivity::class.java).also {
it.putExtra(EXTRA_ARGS, Args(roomId, eventId, sharedTransitionName))
it.putExtra(EXTRA_IMAGE_DATA, mediaData)
if (inMemoryData != null) {
it.putParcelableArrayListExtra(EXTRA_IN_MEMORY_DATA, ArrayList(inMemoryData))
}
}
}
@ -252,27 +269,10 @@ class VectorAttachmentViewerActivity : AttachmentViewerActivity(), RoomAttachmen
}
override fun onShareTapped() {
// Share
eventList?.get(currentPosition)?.let { timelineEvent ->
val messageContent = timelineEvent.root.getClearContent().toModel<MessageContent>()
as? MessageWithAttachmentContent
?: return@let
sessionHolder.getSafeActiveSession()?.fileService()?.downloadFile(
downloadMode = FileService.DownloadMode.FOR_EXTERNAL_SHARE,
id = timelineEvent.eventId,
fileName = messageContent.body,
mimeType = messageContent.mimeType,
url = messageContent.getFileUrl(),
elementToDecrypt = messageContent.encryptedFileInfo?.toElementToDecrypt(),
callback = object : MatrixCallback<File> {
override fun onSuccess(data: File) {
if (lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) {
shareMedia(this@VectorAttachmentViewerActivity, data, getMimeTypeFromUri(this@VectorAttachmentViewerActivity, data.toUri()))
}
}
}
)
this.currentSourceProvider?.getFileForSharing(currentPosition) { data ->
if (data != null && lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) {
shareMedia(this@VectorAttachmentViewerActivity, data, getMimeTypeFromUri(this@VectorAttachmentViewerActivity, data.toUri()))
}
}
}
}

View File

@ -246,20 +246,25 @@ class DefaultNavigator @Inject constructor(
}
override fun openImageViewer(activity: Activity,
roomId: String?,
roomId: String,
mediaData: AttachmentData,
view: View,
inMemory: List<AttachmentData>?,
options: ((MutableList<Pair<View, String>>) -> Unit)?) {
VectorAttachmentViewerActivity.newIntent(activity, mediaData, roomId, mediaData.eventId, ViewCompat.getTransitionName(view)).let { intent ->
VectorAttachmentViewerActivity.newIntent(activity,
mediaData,
roomId,
mediaData.eventId,
inMemory,
ViewCompat.getTransitionName(view)).let { intent ->
val pairs = ArrayList<Pair<View, String>>()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
activity.window.decorView.findViewById<View>(android.R.id.statusBarBackground)?.let {
pairs.add(Pair(it, Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME))
}
activity.window.decorView.findViewById<View>(android.R.id.navigationBarBackground)?.let {
pairs.add(Pair(it, Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME))
}
activity.window.decorView.findViewById<View>(android.R.id.statusBarBackground)?.let {
pairs.add(Pair(it, Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME))
}
activity.window.decorView.findViewById<View>(android.R.id.navigationBarBackground)?.let {
pairs.add(Pair(it, Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME))
}
pairs.add(Pair(view, ViewCompat.getTransitionName(view) ?: ""))
options?.invoke(pairs)
@ -284,12 +289,18 @@ class DefaultNavigator @Inject constructor(
}
override fun openVideoViewer(activity: Activity,
roomId: String?, mediaData: VideoContentRenderer.Data,
roomId: String, mediaData: VideoContentRenderer.Data,
view: View,
inMemory: List<AttachmentData>?,
options: ((MutableList<Pair<View, String>>) -> Unit)?) {
// val intent = VideoMediaViewerActivity.newIntent(activity, mediaData)
// activity.startActivity(intent)
VectorAttachmentViewerActivity.newIntent(activity, mediaData, roomId, mediaData.eventId, ViewCompat.getTransitionName(view)).let { intent ->
VectorAttachmentViewerActivity.newIntent(activity,
mediaData,
roomId,
mediaData.eventId,
inMemory,
ViewCompat.getTransitionName(view)).let { intent ->
val pairs = ArrayList<Pair<View, String>>()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
activity.window.decorView.findViewById<View>(android.R.id.statusBarBackground)?.let {

View File

@ -92,13 +92,15 @@ interface Navigator {
fun openRoomWidget(context: Context, roomId: String, widget: Widget)
fun openImageViewer(activity: Activity,
roomId: String?,
roomId: String,
mediaData: AttachmentData,
view: View,
inMemory: List<AttachmentData>? = null,
options: ((MutableList<Pair<View, String>>) -> Unit)?)
fun openVideoViewer(activity: Activity,
roomId: String?, mediaData: VideoContentRenderer.Data,
roomId: String, mediaData: VideoContentRenderer.Data,
view: View,
inMemory: List<AttachmentData>? = null,
options: ((MutableList<Pair<View, String>>) -> Unit)?)
}

View File

@ -20,23 +20,34 @@ import android.os.Bundle
import android.util.DisplayMetrics
import android.view.View
import androidx.core.content.ContextCompat
import androidx.core.util.Pair
import androidx.core.view.ViewCompat
import androidx.recyclerview.widget.GridLayoutManager
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.parentFragmentViewModel
import com.airbnb.mvrx.withState
import com.google.android.material.appbar.AppBarLayout
import im.vector.matrix.android.api.session.room.model.message.MessageImageContent
import im.vector.matrix.android.api.session.room.model.message.MessageVideoContent
import im.vector.matrix.android.api.session.room.model.message.getFileUrl
import im.vector.matrix.android.internal.crypto.attachments.toElementToDecrypt
import im.vector.riotx.R
import im.vector.riotx.core.extensions.cleanup
import im.vector.riotx.core.extensions.trackItemsVisibilityChange
import im.vector.riotx.core.platform.StateView
import im.vector.riotx.core.platform.VectorBaseFragment
import im.vector.riotx.core.utils.DimensionConverter
import im.vector.riotx.features.media.AttachmentData
import im.vector.riotx.features.media.ImageContentRenderer
import im.vector.riotx.features.media.VideoContentRenderer
import im.vector.riotx.features.roomprofile.uploads.RoomUploadsAction
import im.vector.riotx.features.roomprofile.uploads.RoomUploadsFragment
import im.vector.riotx.features.roomprofile.uploads.RoomUploadsViewModel
import im.vector.riotx.features.roomprofile.uploads.RoomUploadsViewState
import kotlinx.android.synthetic.main.fragment_generic_state_view_recycler.*
import kotlinx.android.synthetic.main.fragment_room_uploads.*
import javax.inject.Inject
class RoomUploadsMediaFragment @Inject constructor(
@ -76,13 +87,75 @@ class RoomUploadsMediaFragment @Inject constructor(
controller.listener = null
}
override fun onOpenImageClicked(view: View, mediaData: ImageContentRenderer.Data) {
navigator.openImageViewer(requireActivity(), null, mediaData, view, null)
// It's very strange i can't just access
// the app bar using find by id...
private fun trickFindAppBar() : AppBarLayout? {
return activity?.supportFragmentManager?.fragments
?.filterIsInstance<RoomUploadsFragment>()
?.firstOrNull()
?.roomUploadsAppBar
}
override fun onOpenVideoClicked(view: View, mediaData: VideoContentRenderer.Data) {
// TODO
// navigator.openVideoViewer(requireActivity(), mediaData, null, )
override fun onOpenImageClicked(view: View, mediaData: ImageContentRenderer.Data) = withState(uploadsViewModel) { state ->
val inMemory = getItemsArgs(state)
navigator.openImageViewer(requireActivity(), state.roomId, mediaData, view, inMemory) { pairs ->
trickFindAppBar()?.let {
pairs.add(Pair(it, ViewCompat.getTransitionName(it) ?: ""))
}
}
}
private fun getItemsArgs(state: RoomUploadsViewState): List<AttachmentData> {
return state.mediaEvents.mapNotNull {
when (val content = it.contentWithAttachmentContent) {
is MessageImageContent -> {
ImageContentRenderer.Data(
eventId = it.eventId,
filename = content.body,
mimeType = content.mimeType,
url = content.getFileUrl(),
elementToDecrypt = content.encryptedFileInfo?.toElementToDecrypt(),
maxHeight = -1,
maxWidth = -1,
width = null,
height = null
)
}
is MessageVideoContent -> {
val thumbnailData = ImageContentRenderer.Data(
eventId = it.eventId,
filename = content.body,
mimeType = content.mimeType,
url = content.videoInfo?.thumbnailFile?.url
?: content.videoInfo?.thumbnailUrl,
elementToDecrypt = content.videoInfo?.thumbnailFile?.toElementToDecrypt(),
height = content.videoInfo?.height,
maxHeight = -1,
width = content.videoInfo?.width,
maxWidth = -1
)
VideoContentRenderer.Data(
eventId = it.eventId,
filename = content.body,
mimeType = content.mimeType,
url = content.getFileUrl(),
elementToDecrypt = content.encryptedFileInfo?.toElementToDecrypt(),
thumbnailMediaData = thumbnailData
)
}
else -> null
}
}
}
override fun onOpenVideoClicked(view: View, mediaData: VideoContentRenderer.Data) = withState(uploadsViewModel) { state ->
val inMemory = getItemsArgs(state)
navigator.openVideoViewer(requireActivity(), state.roomId, mediaData, view, inMemory) { pairs ->
trickFindAppBar()?.let {
pairs.add(Pair(it, ViewCompat.getTransitionName(it) ?: ""))
}
}
}
override fun loadMore() {

View File

@ -18,6 +18,7 @@ package im.vector.riotx.features.roomprofile.uploads.media
import android.view.View
import android.widget.ImageView
import androidx.core.view.ViewCompat
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.riotx.R
@ -37,6 +38,7 @@ abstract class UploadsImageItem : VectorEpoxyModel<UploadsImageItem.Holder>() {
super.bind(holder)
holder.view.setOnClickListener { listener?.onItemClicked(holder.imageView, data) }
imageContentRenderer.render(data, holder.imageView, IMAGE_SIZE_DP)
ViewCompat.setTransitionName(holder.imageView, "imagePreview_${id()}")
}
class Holder : VectorEpoxyHolder() {

View File

@ -18,6 +18,7 @@ package im.vector.riotx.features.roomprofile.uploads.media
import android.view.View
import android.widget.ImageView
import androidx.core.view.ViewCompat
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.riotx.R
@ -38,6 +39,7 @@ abstract class UploadsVideoItem : VectorEpoxyModel<UploadsVideoItem.Holder>() {
super.bind(holder)
holder.view.setOnClickListener { listener?.onItemClicked(holder.imageView, data) }
imageContentRenderer.render(data.thumbnailMediaData, holder.imageView, IMAGE_SIZE_DP)
ViewCompat.setTransitionName(holder.imageView, "videoPreview_${id()}")
}
class Holder : VectorEpoxyHolder() {

View File

@ -8,6 +8,8 @@
<com.google.android.material.appbar.AppBarLayout
style="@style/VectorAppBarLayoutStyle"
android:id="@+id/roomUploadsAppBar"
android:transitionName="toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="4dp">