diff --git a/vector/src/main/java/de/spiritcroc/util/ThumbnailGenerationVideoDownloadDecider.kt b/vector/src/main/java/de/spiritcroc/util/ThumbnailGenerationVideoDownloadDecider.kt new file mode 100644 index 0000000000..44efb0612e --- /dev/null +++ b/vector/src/main/java/de/spiritcroc/util/ThumbnailGenerationVideoDownloadDecider.kt @@ -0,0 +1,27 @@ +package de.spiritcroc.util + +import im.vector.app.features.home.room.detail.timeline.item.MessageInformationData +import org.matrix.android.sdk.api.extensions.orFalse +import javax.inject.Inject + +class ThumbnailGenerationVideoDownloadDecider @Inject constructor() /*val context: Context, val vectorPreference: VectorPreference)*/ { + + fun enableVideoDownloadForThumbnailGeneration(informationData: MessageInformationData? = null): Boolean { + // Disable automatic download for public rooms + if (informationData?.isPublic.orFalse()) { + return false + } + + // Disable automatic download for metered connections + /* Since this may change later, better check in VectorGlideModelLoader directly + context.getSystemService()!!.apply { + if (isActiveNetworkMetered) { + return false + } + } + */ + + // Else, enable automatic download + return true + } +} diff --git a/vector/src/main/java/im/vector/app/core/glide/VectorGlideModelLoader.kt b/vector/src/main/java/im/vector/app/core/glide/VectorGlideModelLoader.kt index 2d3429c309..75664e800d 100644 --- a/vector/src/main/java/im/vector/app/core/glide/VectorGlideModelLoader.kt +++ b/vector/src/main/java/im/vector/app/core/glide/VectorGlideModelLoader.kt @@ -17,6 +17,8 @@ package im.vector.app.core.glide import android.content.Context +import android.net.ConnectivityManager +import androidx.core.content.getSystemService import com.bumptech.glide.Priority import com.bumptech.glide.load.DataSource import com.bumptech.glide.load.Options @@ -62,7 +64,7 @@ class VectorGlideModelLoader(private val context: Context) : } class VectorGlideDataFetcher( - context: Context, + private val context: Context, private val data: ImageContentRenderer.Data, private val width: Int, private val height: Int @@ -130,14 +132,26 @@ class VectorGlideDataFetcher( ) } if (result.isFailure && (data.fallbackUrl != null || data.fallbackElementToDecrypt != null)) { - result = runCatching { - fileService.downloadFile( - fileName = data.filename, - mimeType = data.mimeType, - url = data.fallbackUrl, - elementToDecrypt = data.fallbackElementToDecrypt) + // Extract thumbnail from video + val isInCache = fileService.isFileInCache( + fileName = data.filename, + mimeType = data.mimeType, + mxcUrl = data.fallbackUrl, + elementToDecrypt = data.fallbackElementToDecrypt) + if (data.downloadFallbackIfThumbnailMissing || isInCache) { + // Disable automatic download for metered connections + if (isInCache || !context.getSystemService()!!.isActiveNetworkMetered) { + result = runCatching { + fileService.downloadFile( + fileName = data.filename, + mimeType = data.mimeType, + url = data.fallbackUrl, + elementToDecrypt = data.fallbackElementToDecrypt + ) + } + result = result.getOrNull()?.let { thumbnailExtractor.extractThumbnail(it) } ?: result + } } - result = result.getOrNull()?.let { thumbnailExtractor.extractThumbnail(it) } ?: result } withContext(Dispatchers.Main) { result.fold( diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt index f4ac8c69dd..f74a7717d4 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt @@ -77,6 +77,7 @@ import de.spiritcroc.menu.toggleExec import de.spiritcroc.recyclerview.StickyHeaderItemDecoration import de.spiritcroc.recyclerview.widget.BetterLinearLayoutManager import de.spiritcroc.recyclerview.widget.LinearLayoutManager +import de.spiritcroc.util.ThumbnailGenerationVideoDownloadDecider import im.vector.app.BuildConfig import im.vector.app.R import im.vector.app.core.animations.play @@ -283,6 +284,7 @@ class TimelineFragment @Inject constructor( private val notificationDrawerManager: NotificationDrawerManager, private val eventHtmlRenderer: EventHtmlRenderer, private val vectorPreferences: VectorPreferences, + private val generationVideoDownloadDecider: ThumbnailGenerationVideoDownloadDecider, private val bubbleThemeUtils: BubbleThemeUtils, private val threadsManager: ThreadsManager, private val colorProvider: ColorProvider, @@ -1454,7 +1456,7 @@ class TimelineFragment @Inject constructor( views.composerLayout.views.composerRelatedMessageContent.text = (formattedBody ?: nonFormattedBody) // Image Event - val data = event.buildImageContentRendererData(dimensionConverter.dpToPx(66)) + val data = event.buildImageContentRendererData(dimensionConverter.dpToPx(66), generationVideoDownloadDecider.enableVideoDownloadForThumbnailGeneration()) val isImageVisible = if (data != null) { imageContentRenderer.render(data, ImageContentRenderer.Mode.THUMBNAIL, views.composerLayout.views.composerRelatedMessageImage) true diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt index d918703f95..a47fd06f56 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt @@ -17,6 +17,7 @@ package im.vector.app.features.home.room.detail.timeline.action import com.airbnb.epoxy.TypedEpoxyController import com.airbnb.mvrx.Success +import de.spiritcroc.util.ThumbnailGenerationVideoDownloadDecider import im.vector.app.EmojiCompatFontProvider import im.vector.app.R import im.vector.app.core.date.DateFormatKind @@ -66,6 +67,7 @@ class MessageActionsEpoxyController @Inject constructor( private val spanUtils: SpanUtils, private val eventDetailsFormatter: EventDetailsFormatter, private val vectorPreferences: VectorPreferences, + private val thumbnailGenerationVideoDownloadDecider: ThumbnailGenerationVideoDownloadDecider, private val dateFormatter: VectorDateFormatter, private val urlMapProvider: UrlMapProvider, private val locationPinProvider: LocationPinProvider @@ -88,7 +90,7 @@ class MessageActionsEpoxyController @Inject constructor( matrixItem(state.informationData.matrixItem) movementMethod(createLinkMovementMethod(host.listener)) imageContentRenderer(host.imageContentRenderer) - data(state.timelineEvent()?.buildImageContentRendererData(host.dimensionConverter.dpToPx(66))) + data(state.timelineEvent()?.buildImageContentRendererData(host.dimensionConverter.dpToPx(66), host.thumbnailGenerationVideoDownloadDecider.enableVideoDownloadForThumbnailGeneration())) userClicked { host.listener?.didSelectMenuAction(EventSharedAction.OpenUserProfile(state.informationData.senderId)) } bindingOptions(bindingOptions) body(body.toEpoxyCharSequence()) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt index 646a1595ec..98015ea6e2 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt @@ -500,6 +500,7 @@ class MessageItemFactory @Inject constructor( maxWidth = maxWidth, allowNonMxcUrls = informationData.sendState.isSending(), // Video fallback for generating thumbnails + downloadFallbackIfThumbnailMissing = attributes.generateMissingVideoThumbnails, fallbackUrl = messageContent.getFileUrl(), fallbackElementToDecrypt = messageContent.encryptedFileInfo?.toElementToDecrypt() ) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageItemAttributesFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageItemAttributesFactory.kt index ad7482d9a5..8248b4f53a 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageItemAttributesFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageItemAttributesFactory.kt @@ -15,6 +15,7 @@ */ package im.vector.app.features.home.room.detail.timeline.helper +import de.spiritcroc.util.ThumbnailGenerationVideoDownloadDecider import im.vector.app.EmojiCompatFontProvider import im.vector.app.R import im.vector.app.core.resources.StringProvider @@ -36,6 +37,7 @@ class MessageItemAttributesFactory @Inject constructor( private val stringProvider: StringProvider, private val displayableEventFormatter: DisplayableEventFormatter, private val preferencesProvider: UserPreferencesProvider, + private val thumbnailGenerationVideoDownloadDecider: ThumbnailGenerationVideoDownloadDecider, private val emojiCompatFontProvider: EmojiCompatFontProvider ) { @@ -71,7 +73,8 @@ class MessageItemAttributesFactory @Inject constructor( threadDetails = threadDetails, reactionsSummaryEvents = reactionsSummaryEvents, areThreadMessagesEnabled = preferencesProvider.areThreadMessagesEnabled(), - autoplayAnimatedImages = preferencesProvider.autoplayAnimatedImages() + autoplayAnimatedImages = preferencesProvider.autoplayAnimatedImages(), + generateMissingVideoThumbnails = thumbnailGenerationVideoDownloadDecider.enableVideoDownloadForThumbnailGeneration(informationData) ) } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/image/ImageContentRendererFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/image/ImageContentRendererFactory.kt index 71d996d2ec..f1e05c7b5a 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/image/ImageContentRendererFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/image/ImageContentRendererFactory.kt @@ -27,7 +27,7 @@ import org.matrix.android.sdk.api.session.room.model.message.getFileUrl import org.matrix.android.sdk.api.session.room.model.message.getThumbnailUrl import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent -fun TimelineEvent.buildImageContentRendererData(maxHeight: Int): ImageContentRenderer.Data? { +fun TimelineEvent.buildImageContentRendererData(maxHeight: Int, generateMissingVideoThumbnails: Boolean): ImageContentRenderer.Data? { return when { root.isImageMessage() -> root.getClearContent().toModel() ?.let { messageImageContent -> @@ -59,6 +59,7 @@ fun TimelineEvent.buildImageContentRendererData(maxHeight: Int): ImageContentRen maxWidth = maxHeight * 2, allowNonMxcUrls = false, // Video fallback for generating thumbnails + downloadFallbackIfThumbnailMissing = generateMissingVideoThumbnails, fallbackUrl = messageVideoContent.getFileUrl(), fallbackElementToDecrypt = messageVideoContent.encryptedFileInfo?.toElementToDecrypt() ) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageItem.kt index 5f4a65bfd8..52c9d03201 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageItem.kt @@ -207,6 +207,7 @@ abstract class AbsMessageItem( val threadDetails: ThreadDetails? = null, val areThreadMessagesEnabled: Boolean = false, val autoplayAnimatedImages: Boolean = false, + val generateMissingVideoThumbnails: Boolean = false, override val reactionsSummaryEvents: ReactionsSummaryEvents? = null, ) : AbsBaseMessageItem.Attributes { diff --git a/vector/src/main/java/im/vector/app/features/media/ImageContentRenderer.kt b/vector/src/main/java/im/vector/app/features/media/ImageContentRenderer.kt index 2ad4f83d39..a4536fd689 100644 --- a/vector/src/main/java/im/vector/app/features/media/ImageContentRenderer.kt +++ b/vector/src/main/java/im/vector/app/features/media/ImageContentRenderer.kt @@ -85,6 +85,7 @@ class ImageContentRenderer @Inject constructor( // If true will load non mxc url, be careful to set it only for images sent by you override val allowNonMxcUrls: Boolean = false, // Fallback for videos: generate preview from video + val downloadFallbackIfThumbnailMissing: Boolean = false, val fallbackUrl: String? = null, val fallbackElementToDecrypt: ElementToDecrypt? = null, ) : AttachmentData