diff --git a/attachment-viewer/build.gradle b/attachment-viewer/build.gradle
index 6b64e661fa..3a5c3298d4 100644
--- a/attachment-viewer/build.gradle
+++ b/attachment-viewer/build.gradle
@@ -58,9 +58,7 @@ android {
 }
 
 dependencies {
-//    implementation 'com.github.MikeOrtiz:TouchImageView:3.0.2'
     implementation 'com.github.chrisbanes:PhotoView:2.0.0'
-    implementation "com.github.bumptech.glide:glide:4.10.0"
 
     implementation 'io.reactivex.rxjava2:rxkotlin:2.3.0'
     implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
diff --git a/attachment-viewer/src/main/java/im/vector/riotx/attachmentviewer/AnimatedImageViewHolder.kt b/attachment-viewer/src/main/java/im/vector/riotx/attachmentviewer/AnimatedImageViewHolder.kt
index 9f512e78be..f00a4eff30 100644
--- a/attachment-viewer/src/main/java/im/vector/riotx/attachmentviewer/AnimatedImageViewHolder.kt
+++ b/attachment-viewer/src/main/java/im/vector/riotx/attachmentviewer/AnimatedImageViewHolder.kt
@@ -16,16 +16,9 @@
 
 package im.vector.riotx.attachmentviewer
 
-import android.graphics.drawable.Animatable
-import android.graphics.drawable.Drawable
 import android.view.View
 import android.widget.ImageView
-import android.widget.LinearLayout
 import android.widget.ProgressBar
-import androidx.core.view.isVisible
-import androidx.core.view.updateLayoutParams
-import com.bumptech.glide.request.target.CustomViewTarget
-import com.bumptech.glide.request.transition.Transition
 
 class AnimatedImageViewHolder constructor(itemView: View) :
         BaseViewHolder(itemView) {
@@ -33,34 +26,5 @@ class AnimatedImageViewHolder constructor(itemView: View) :
     val touchImageView: ImageView = itemView.findViewById(R.id.imageView)
     val imageLoaderProgress: ProgressBar = itemView.findViewById(R.id.imageLoaderProgress)
 
-    val customTargetView = object : CustomViewTarget<ImageView, Drawable>(touchImageView) {
-
-        override fun onResourceLoading(placeholder: Drawable?) {
-            imageLoaderProgress.isVisible = true
-        }
-
-        override fun onLoadFailed(errorDrawable: Drawable?) {
-            imageLoaderProgress.isVisible = false
-        }
-
-        override fun onResourceCleared(placeholder: Drawable?) {
-            touchImageView.setImageDrawable(placeholder)
-        }
-
-        override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
-            imageLoaderProgress.isVisible = false
-            // Glide mess up the view size :/
-            touchImageView.updateLayoutParams {
-                width = LinearLayout.LayoutParams.MATCH_PARENT
-                height = LinearLayout.LayoutParams.MATCH_PARENT
-            }
-            touchImageView.setImageDrawable(resource)
-            if (resource is Animatable) {
-                resource.start()
-            }
-        }
-    }
-
-    override fun bind(attachmentInfo: AttachmentInfo) {
-    }
+    internal val target = DefaultImageLoaderTarget(this, this.touchImageView)
 }
diff --git a/attachment-viewer/src/main/java/im/vector/riotx/attachmentviewer/AttachmentSourceProvider.kt b/attachment-viewer/src/main/java/im/vector/riotx/attachmentviewer/AttachmentSourceProvider.kt
index 930fc62658..ce725afec2 100644
--- a/attachment-viewer/src/main/java/im/vector/riotx/attachmentviewer/AttachmentSourceProvider.kt
+++ b/attachment-viewer/src/main/java/im/vector/riotx/attachmentviewer/AttachmentSourceProvider.kt
@@ -19,15 +19,12 @@ package im.vector.riotx.attachmentviewer
 import android.content.Context
 import android.view.View
 
-sealed class AttachmentInfo {
-    data class Image(val url: String, val data: Any?) : AttachmentInfo()
-    data class AnimatedImage(val url: String, val data: Any?) : AttachmentInfo()
-    data class Video(val url: String, val data: Any, val thumbnail: Image?) : AttachmentInfo()
-    data class Audio(val url: String, val data: Any) : AttachmentInfo()
-    data class File(val url: String, val data: Any) : AttachmentInfo()
-
-    fun bind() {
-    }
+sealed class AttachmentInfo(open val uid: String) {
+    data class Image(override val uid: String, val url: String, val data: Any?) : AttachmentInfo(uid)
+    data class AnimatedImage(override val uid: String, val url: String, val data: Any?) : AttachmentInfo(uid)
+    data class Video(override val uid: String, val url: String, val data: Any, val thumbnail: Image?) : AttachmentInfo(uid)
+    data class Audio(override val uid: String, val url: String, val data: Any) : AttachmentInfo(uid)
+    data class File(override val uid: String, val url: String, val data: Any) : AttachmentInfo(uid)
 }
 
 interface AttachmentSourceProvider {
@@ -36,11 +33,13 @@ interface AttachmentSourceProvider {
 
     fun getAttachmentInfoAt(position: Int): AttachmentInfo
 
-    fun loadImage(holder: ZoomableImageViewHolder, info: AttachmentInfo.Image)
+    fun loadImage(target: ImageLoaderTarget, info: AttachmentInfo.Image)
 
-    fun loadImage(holder: AnimatedImageViewHolder, info: AttachmentInfo.AnimatedImage)
+    fun loadImage(target: ImageLoaderTarget, info: AttachmentInfo.AnimatedImage)
 
-    fun loadVideo(holder: VideoViewHolder, info: AttachmentInfo.Video)
+    fun loadVideo(target: VideoLoaderTarget, info: AttachmentInfo.Video)
 
-    fun overlayViewAtPosition(context: Context, position: Int) : View?
+    fun overlayViewAtPosition(context: Context, position: Int): View?
+
+    fun clear(id: String)
 }
diff --git a/attachment-viewer/src/main/java/im/vector/riotx/attachmentviewer/AttachmentsAdapter.kt b/attachment-viewer/src/main/java/im/vector/riotx/attachmentviewer/AttachmentsAdapter.kt
index b0cb5193e8..2f453b58a8 100644
--- a/attachment-viewer/src/main/java/im/vector/riotx/attachmentviewer/AttachmentsAdapter.kt
+++ b/attachment-viewer/src/main/java/im/vector/riotx/attachmentviewer/AttachmentsAdapter.kt
@@ -24,8 +24,10 @@ import androidx.recyclerview.widget.RecyclerView
 abstract class BaseViewHolder constructor(itemView: View) :
         RecyclerView.ViewHolder(itemView) {
 
-    open fun bind(attachmentInfo: AttachmentInfo) {}
-    open fun onRecycled() {}
+    open fun onRecycled() {
+        boundResourceUid = null
+    }
+
     open fun onAttached() {}
     open fun onDetached() {}
     open fun entersBackground() {}
@@ -33,16 +35,17 @@ abstract class BaseViewHolder constructor(itemView: View) :
     open fun onSelected(selected: Boolean) {}
 
     open fun handleCommand(commands: AttachmentCommands) {}
-}
 
-class AttachmentViewHolder constructor(itemView: View) :
-        BaseViewHolder(itemView) {
+    var boundResourceUid: String? = null
 
-    override fun bind(attachmentInfo: AttachmentInfo) {
+    open fun bind(attachmentInfo: AttachmentInfo) {
+        boundResourceUid = attachmentInfo.uid
     }
 }
 
-// class AttachmentsAdapter(fragmentManager: FragmentManager, lifecycle: Lifecycle) : FragmentStateAdapter(fragmentManager, lifecycle) {
+class AttachmentViewHolder constructor(itemView: View) :
+        BaseViewHolder(itemView)
+
 class AttachmentsAdapter() : RecyclerView.Adapter<BaseViewHolder>() {
 
     var attachmentSourceProvider: AttachmentSourceProvider? = null
@@ -65,21 +68,21 @@ class AttachmentsAdapter() : RecyclerView.Adapter<BaseViewHolder>() {
         val inflater = LayoutInflater.from(parent.context)
         val itemView = inflater.inflate(viewType, parent, false)
         return when (viewType) {
-            R.layout.item_image_attachment -> ZoomableImageViewHolder(itemView)
+            R.layout.item_image_attachment          -> ZoomableImageViewHolder(itemView)
             R.layout.item_animated_image_attachment -> AnimatedImageViewHolder(itemView)
-            R.layout.item_video_attachment -> VideoViewHolder(itemView)
-            else                           -> AttachmentViewHolder(itemView)
+            R.layout.item_video_attachment          -> VideoViewHolder(itemView)
+            else                                    -> AttachmentViewHolder(itemView)
         }
     }
 
     override fun getItemViewType(position: Int): Int {
         val info = attachmentSourceProvider!!.getAttachmentInfoAt(position)
         return when (info) {
-            is AttachmentInfo.Image -> R.layout.item_image_attachment
-            is AttachmentInfo.Video -> R.layout.item_video_attachment
+            is AttachmentInfo.Image         -> R.layout.item_image_attachment
+            is AttachmentInfo.Video         -> R.layout.item_video_attachment
             is AttachmentInfo.AnimatedImage -> R.layout.item_animated_image_attachment
-            is AttachmentInfo.Audio -> TODO()
-            is AttachmentInfo.File  -> TODO()
+            is AttachmentInfo.Audio         -> TODO()
+            is AttachmentInfo.File          -> TODO()
         }
     }
 
@@ -91,16 +94,17 @@ class AttachmentsAdapter() : RecyclerView.Adapter<BaseViewHolder>() {
         attachmentSourceProvider?.getAttachmentInfoAt(position)?.let {
             holder.bind(it)
             when (it) {
-                is AttachmentInfo.Image -> {
-                    attachmentSourceProvider?.loadImage(holder as ZoomableImageViewHolder, it)
+                is AttachmentInfo.Image         -> {
+                    attachmentSourceProvider?.loadImage((holder as ZoomableImageViewHolder).target, it)
                 }
                 is AttachmentInfo.AnimatedImage -> {
-                    attachmentSourceProvider?.loadImage(holder as AnimatedImageViewHolder, it)
+                    attachmentSourceProvider?.loadImage((holder as AnimatedImageViewHolder).target, it)
                 }
-                is AttachmentInfo.Video -> {
-                    attachmentSourceProvider?.loadVideo(holder as VideoViewHolder, it)
+                is AttachmentInfo.Video         -> {
+                    attachmentSourceProvider?.loadVideo((holder as VideoViewHolder).target, it)
+                }
+                else                            -> {
                 }
-                else                    -> {}
             }
         }
     }
@@ -134,35 +138,4 @@ class AttachmentsAdapter() : RecyclerView.Adapter<BaseViewHolder>() {
         val holder = recyclerView?.findViewHolderForAdapterPosition(position) as? BaseViewHolder
         holder?.entersForeground()
     }
-//    override fun getItemCount(): Int {
-//        return 8
-//    }
-//
-//    override fun createFragment(position: Int): Fragment {
-//        // Return a NEW fragment instance in createFragment(int)
-//        val fragment = DemoObjectFragment()
-//        fragment.arguments = Bundle().apply {
-//            // Our object is just an integer :-P
-//            putInt(ARG_OBJECT, position + 1)
-//        }
-//        return fragment
-//    }
 }
-
-// private const val ARG_OBJECT = "object"
-//
-// // Instances of this class are fragments representing a single
-// // object in our collection.
-// class DemoObjectFragment : Fragment() {
-//
-//    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
-//        return inflater.inflate(R.layout.view_image_attachment, container, false)
-//    }
-//
-//    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
-//        arguments?.takeIf { it.containsKey(ARG_OBJECT) }?.apply {
-//            val textView: TextView = view.findViewById(R.id.testPage)
-//            textView.text = getInt(ARG_OBJECT).toString()
-//        }
-//    }
-// }
diff --git a/attachment-viewer/src/main/java/im/vector/riotx/attachmentviewer/ImageLoaderTarget.kt b/attachment-viewer/src/main/java/im/vector/riotx/attachmentviewer/ImageLoaderTarget.kt
new file mode 100644
index 0000000000..bb59c9e01e
--- /dev/null
+++ b/attachment-viewer/src/main/java/im/vector/riotx/attachmentviewer/ImageLoaderTarget.kt
@@ -0,0 +1,103 @@
+/*
+ * 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.attachmentviewer
+
+import android.graphics.drawable.Animatable
+import android.graphics.drawable.Drawable
+import android.widget.ImageView
+import android.widget.LinearLayout
+import androidx.core.view.isVisible
+import androidx.core.view.updateLayoutParams
+
+interface ImageLoaderTarget {
+
+    fun contextView(): ImageView
+
+    fun onResourceLoading(uid: String, placeholder: Drawable?)
+
+    fun onLoadFailed(uid: String, errorDrawable: Drawable?)
+
+    fun onResourceCleared(uid: String, placeholder: Drawable?)
+
+    fun onResourceReady(uid: String, resource: Drawable)
+}
+
+internal class DefaultImageLoaderTarget(val holder: AnimatedImageViewHolder, private val contextView: ImageView)
+    : ImageLoaderTarget {
+    override fun contextView(): ImageView {
+        return contextView
+    }
+
+    override fun onResourceLoading(uid: String, placeholder: Drawable?) {
+        if (holder.boundResourceUid != uid) return
+        holder.imageLoaderProgress.isVisible = true
+    }
+
+    override fun onLoadFailed(uid: String, errorDrawable: Drawable?) {
+        if (holder.boundResourceUid != uid) return
+        holder.imageLoaderProgress.isVisible = false
+    }
+
+    override fun onResourceCleared(uid: String, placeholder: Drawable?) {
+        if (holder.boundResourceUid != uid) return
+        holder.touchImageView.setImageDrawable(placeholder)
+    }
+
+    override fun onResourceReady(uid: String, resource: Drawable) {
+        if (holder.boundResourceUid != uid) return
+        holder.imageLoaderProgress.isVisible = false
+        // Glide mess up the view size :/
+        holder.touchImageView.updateLayoutParams {
+            width = LinearLayout.LayoutParams.MATCH_PARENT
+            height = LinearLayout.LayoutParams.MATCH_PARENT
+        }
+        holder.touchImageView.setImageDrawable(resource)
+        if (resource is Animatable) {
+            resource.start()
+        }
+    }
+
+    internal class ZoomableImageTarget(val holder: ZoomableImageViewHolder, private val contextView: ImageView) : ImageLoaderTarget {
+        override fun contextView() = contextView
+
+        override fun onResourceLoading(uid: String, placeholder: Drawable?) {
+            if (holder.boundResourceUid != uid) return
+            holder.imageLoaderProgress.isVisible = true
+        }
+
+        override fun onLoadFailed(uid: String, errorDrawable: Drawable?) {
+            if (holder.boundResourceUid != uid) return
+            holder.imageLoaderProgress.isVisible = false
+        }
+
+        override fun onResourceCleared(uid: String, placeholder: Drawable?) {
+            if (holder.boundResourceUid != uid) return
+            holder.touchImageView.setImageDrawable(placeholder)
+        }
+
+        override fun onResourceReady(uid: String, resource: Drawable) {
+            if (holder.boundResourceUid != uid) return
+            holder.imageLoaderProgress.isVisible = false
+            // Glide mess up the view size :/
+            holder.touchImageView.updateLayoutParams {
+                width = LinearLayout.LayoutParams.MATCH_PARENT
+                height = LinearLayout.LayoutParams.MATCH_PARENT
+            }
+            holder.touchImageView.setImageDrawable(resource)
+        }
+    }
+}
diff --git a/attachment-viewer/src/main/java/im/vector/riotx/attachmentviewer/VideoLoaderTarget.kt b/attachment-viewer/src/main/java/im/vector/riotx/attachmentviewer/VideoLoaderTarget.kt
new file mode 100644
index 0000000000..548c6431e5
--- /dev/null
+++ b/attachment-viewer/src/main/java/im/vector/riotx/attachmentviewer/VideoLoaderTarget.kt
@@ -0,0 +1,76 @@
+/*
+ * 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.attachmentviewer
+
+import android.graphics.drawable.Drawable
+import android.widget.ImageView
+import androidx.core.view.isVisible
+import java.io.File
+
+interface VideoLoaderTarget {
+    fun contextView(): ImageView
+
+    fun onThumbnailResourceLoading(uid: String, placeholder: Drawable?)
+
+    fun onThumbnailLoadFailed(uid: String, errorDrawable: Drawable?)
+
+    fun onThumbnailResourceCleared(uid: String, placeholder: Drawable?)
+
+    fun onThumbnailResourceReady(uid: String, resource: Drawable)
+
+    fun onVideoFileLoading(uid: String)
+    fun onVideoFileLoadFailed(uid: String)
+    fun onVideoFileReady(uid: String, file: File)
+}
+
+internal class DefaultVideoLoaderTarget(val holder: VideoViewHolder, private val contextView: ImageView) : VideoLoaderTarget {
+    override fun contextView(): ImageView = contextView
+
+    override fun onThumbnailResourceLoading(uid: String, placeholder: Drawable?) {
+    }
+
+    override fun onThumbnailLoadFailed(uid: String, errorDrawable: Drawable?) {
+    }
+
+    override fun onThumbnailResourceCleared(uid: String, placeholder: Drawable?) {
+    }
+
+    override fun onThumbnailResourceReady(uid: String, resource: Drawable) {
+        if (holder.boundResourceUid != uid) return
+        holder.thumbnailImage.setImageDrawable(resource)
+    }
+
+    override fun onVideoFileLoading(uid: String) {
+        if (holder.boundResourceUid != uid) return
+        holder.thumbnailImage.isVisible = true
+        holder.loaderProgressBar.isVisible = true
+        holder.videoView.isVisible = false
+    }
+
+    override fun onVideoFileLoadFailed(uid: String) {
+        if (holder.boundResourceUid != uid) return
+        holder.videoFileLoadError()
+    }
+
+    override fun onVideoFileReady(uid: String, file: File) {
+        if (holder.boundResourceUid != uid) return
+        holder.thumbnailImage.isVisible = false
+        holder.loaderProgressBar.isVisible = false
+        holder.videoView.isVisible = true
+        holder.videoReady(file)
+    }
+}
diff --git a/attachment-viewer/src/main/java/im/vector/riotx/attachmentviewer/VideoViewHolder.kt b/attachment-viewer/src/main/java/im/vector/riotx/attachmentviewer/VideoViewHolder.kt
index 5718147bab..2b417baecc 100644
--- a/attachment-viewer/src/main/java/im/vector/riotx/attachmentviewer/VideoViewHolder.kt
+++ b/attachment-viewer/src/main/java/im/vector/riotx/attachmentviewer/VideoViewHolder.kt
@@ -44,38 +44,13 @@ class VideoViewHolder constructor(itemView: View) :
 
     var eventListener: WeakReference<AttachmentEventListener>? = null
 
-//    interface Target {
-//        fun onResourceLoading(progress: Int, total: Int)
-//        fun onLoadFailed()
-//        fun onResourceReady(file: File)
-//        fun onThumbnailReady(thumbnail: Drawable?)
-//    }
-
-    init {
-    }
-
     val thumbnailImage: ImageView = itemView.findViewById(R.id.videoThumbnailImage)
     val videoView: VideoView = itemView.findViewById(R.id.videoView)
     val loaderProgressBar: ProgressBar = itemView.findViewById(R.id.videoLoaderProgress)
     val videoControlIcon: ImageView = itemView.findViewById(R.id.videoControlIcon)
     val errorTextView: TextView = itemView.findViewById(R.id.videoMediaViewerErrorView)
 
-//    val videoTarget = object : Target {
-//        override fun onResourceLoading(progress: Int, total: Int) {
-//            videoView.isVisible = false
-//            loaderProgressBar.isVisible = true
-//        }
-//
-//        override fun onLoadFailed() {
-//            loaderProgressBar.isVisible = false
-//        }
-//
-//        override fun onResourceReady(file: File) {
-//        }
-//
-//        override fun onThumbnailReady(thumbnail: Drawable?) {
-//        }
-//    }
+    internal val target = DefaultVideoLoaderTarget(this, thumbnailImage)
 
     override fun onRecycled() {
         super.onRecycled()
@@ -91,6 +66,9 @@ class VideoViewHolder constructor(itemView: View) :
         }
     }
 
+    fun videoFileLoadError() {
+    }
+
     override fun entersBackground() {
         if (videoView.isPlaying) {
             progress = videoView.currentPosition
@@ -162,7 +140,7 @@ class VideoViewHolder constructor(itemView: View) :
                 wasPaused = true
                 videoView.pause()
             }
-            is AttachmentCommands.SeekTo -> {
+            is AttachmentCommands.SeekTo  -> {
                 val duration = videoView.duration
                 if (duration > 0) {
                     val seekDuration = duration * (commands.percentProgress / 100f)
@@ -173,6 +151,7 @@ class VideoViewHolder constructor(itemView: View) :
     }
 
     override fun bind(attachmentInfo: AttachmentInfo) {
+        super.bind(attachmentInfo)
         progress = 0
         wasPaused = false
     }
diff --git a/attachment-viewer/src/main/java/im/vector/riotx/attachmentviewer/ZoomableImageViewHolder.kt b/attachment-viewer/src/main/java/im/vector/riotx/attachmentviewer/ZoomableImageViewHolder.kt
index 00a8ad275a..aeaf612bbc 100644
--- a/attachment-viewer/src/main/java/im/vector/riotx/attachmentviewer/ZoomableImageViewHolder.kt
+++ b/attachment-viewer/src/main/java/im/vector/riotx/attachmentviewer/ZoomableImageViewHolder.kt
@@ -16,15 +16,9 @@
 
 package im.vector.riotx.attachmentviewer
 
-import android.graphics.drawable.Drawable
 import android.util.Log
 import android.view.View
-import android.widget.LinearLayout
 import android.widget.ProgressBar
-import androidx.core.view.isVisible
-import androidx.core.view.updateLayoutParams
-import com.bumptech.glide.request.target.CustomViewTarget
-import com.bumptech.glide.request.transition.Transition
 import com.github.chrisbanes.photoview.PhotoView
 
 class ZoomableImageViewHolder constructor(itemView: View) :
@@ -45,31 +39,5 @@ class ZoomableImageViewHolder constructor(itemView: View) :
         touchImageView.setAllowParentInterceptOnEdge(true)
     }
 
-    val customTargetView = object : CustomViewTarget<PhotoView, Drawable>(touchImageView) {
-
-        override fun onResourceLoading(placeholder: Drawable?) {
-            imageLoaderProgress.isVisible = true
-        }
-
-        override fun onLoadFailed(errorDrawable: Drawable?) {
-            imageLoaderProgress.isVisible = false
-        }
-
-        override fun onResourceCleared(placeholder: Drawable?) {
-            touchImageView.setImageDrawable(placeholder)
-        }
-
-        override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
-            imageLoaderProgress.isVisible = false
-            // Glide mess up the view size :/
-            touchImageView.updateLayoutParams {
-                width = LinearLayout.LayoutParams.MATCH_PARENT
-                height = LinearLayout.LayoutParams.MATCH_PARENT
-            }
-            touchImageView.setImageDrawable(resource)
-        }
-    }
-
-    override fun bind(attachmentInfo: AttachmentInfo) {
-    }
+    internal val target = DefaultImageLoaderTarget.ZoomableImageTarget(this, touchImageView)
 }
diff --git a/vector/src/main/java/im/vector/riotx/features/media/RoomAttachmentProvider.kt b/vector/src/main/java/im/vector/riotx/features/media/RoomAttachmentProvider.kt
index 4e30e0179a..f7299bf714 100644
--- a/vector/src/main/java/im/vector/riotx/features/media/RoomAttachmentProvider.kt
+++ b/vector/src/main/java/im/vector/riotx/features/media/RoomAttachmentProvider.kt
@@ -35,11 +35,10 @@ import im.vector.matrix.android.api.session.room.model.message.MessageWithAttach
 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.AnimatedImageViewHolder
 import im.vector.riotx.attachmentviewer.AttachmentInfo
 import im.vector.riotx.attachmentviewer.AttachmentSourceProvider
-import im.vector.riotx.attachmentviewer.VideoViewHolder
-import im.vector.riotx.attachmentviewer.ZoomableImageViewHolder
+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
@@ -86,13 +85,15 @@ class RoomAttachmentProvider(
                 )
                 if (content.mimeType == "image/gif") {
                     AttachmentInfo.AnimatedImage(
-                            content.url ?: "",
-                            data
+                            uid = it.eventId,
+                            url = content.url ?: "",
+                            data = data
                     )
                 } else {
                     AttachmentInfo.Image(
-                            content.url ?: "",
-                            data
+                            uid = it.eventId,
+                            url = content.url ?: "",
+                            data = data
                     )
                 }
             } else if (content is MessageVideoContent) {
@@ -117,9 +118,11 @@ class RoomAttachmentProvider(
                         thumbnailMediaData = thumbnailData
                 )
                 AttachmentInfo.Video(
-                        content.getFileUrl() ?: "",
-                        data,
-                        AttachmentInfo.Image(
+                        uid = it.eventId,
+                        url = content.getFileUrl() ?: "",
+                        data = data,
+                        thumbnail = AttachmentInfo.Image(
+                                uid = it.eventId,
                                 url = content.videoInfo?.thumbnailFile?.url
                                         ?: content.videoInfo?.thumbnailUrl ?: "",
                                 data = thumbnailData
@@ -128,49 +131,72 @@ class RoomAttachmentProvider(
                 )
             } else {
                 AttachmentInfo.Image(
-                        "",
-                        null
+                        uid = it.eventId,
+                        url = "",
+                        data = null
                 )
             }
         }
     }
 
-    override fun loadImage(holder: ZoomableImageViewHolder, info: AttachmentInfo.Image) {
+    override fun loadImage(target: ImageLoaderTarget, info: AttachmentInfo.Image) {
         (info.data as? ImageContentRenderer.Data)?.let {
-            imageContentRenderer.render(it, holder.touchImageView, holder.customTargetView as CustomViewTarget<*, Drawable>)
+            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(holder: AnimatedImageViewHolder, info: AttachmentInfo.AnimatedImage) {
+    override fun loadImage(target: ImageLoaderTarget, info: AttachmentInfo.AnimatedImage) {
         (info.data as? ImageContentRenderer.Data)?.let {
-            imageContentRenderer.render(it, holder.touchImageView, holder.customTargetView as CustomViewTarget<*, Drawable>)
+            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(holder: VideoViewHolder, info: AttachmentInfo.Video) {
+    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, holder.thumbnailImage, object : CustomViewTarget<ImageView, Drawable>(holder.thumbnailImage) {
+        imageContentRenderer.render(data.thumbnailMediaData, target.contextView(), object : CustomViewTarget<ImageView, Drawable>(target.contextView()) {
             override fun onLoadFailed(errorDrawable: Drawable?) {
-                holder.thumbnailImage.setImageDrawable(errorDrawable)
+                target.onThumbnailLoadFailed(info.uid, errorDrawable)
             }
 
             override fun onResourceCleared(placeholder: Drawable?) {
+                target.onThumbnailResourceCleared(info.uid, placeholder)
             }
 
             override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
-                holder.thumbnailImage.setImageDrawable(resource)
+                target.onThumbnailResourceReady(info.uid, resource)
             }
         })
 
-        holder.thumbnailImage.isVisible = false
-        holder.loaderProgressBar.isVisible = false
-        holder.videoView.isVisible = false
-
+        target.onVideoFileLoading(info.uid)
         fileService.downloadFile(
                 downloadMode = FileService.DownloadMode.FOR_INTERNAL_USE,
                 id = data.eventId,
@@ -180,11 +206,11 @@ class RoomAttachmentProvider(
                 url = data.url,
                 callback = object : MatrixCallback<File> {
                     override fun onSuccess(data: File) {
-                        holder.videoReady(data)
+                        target.onVideoFileReady(info.uid, data)
                     }
 
                     override fun onFailure(failure: Throwable) {
-                        holder.videoView.isVisible = false
+                        target.onVideoFileLoadFailed(info.uid)
                     }
                 }
         )
@@ -214,6 +240,10 @@ class RoomAttachmentProvider(
         overlayView?.videoControlsGroup?.isVisible = item.root.isVideoMessage()
         return overlayView
     }
+
+    override fun clear(id: String) {
+        // TODO("Not yet implemented")
+    }
 }
 
 class RoomAttachmentProviderFactory @Inject constructor(