From 71132306767df19151a5fae7b40aef13b8b700e1 Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Tue, 6 Dec 2022 15:38:32 +0100 Subject: [PATCH 1/2] Activate antivirus for audio and voice messages --- .../timeline/factory/MessageItemFactory.kt | 6 ++ .../detail/timeline/item/MessageAudioItem.kt | 56 ++++++++++++++++++- .../detail/timeline/item/MessageVoiceItem.kt | 56 ++++++++++++++++++- .../layout/item_timeline_event_audio_stub.xml | 12 +++- .../layout/item_timeline_event_voice_stub.xml | 10 ++++ 5 files changed, 137 insertions(+), 3 deletions(-) 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 ab085fea88..74a294915b 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 @@ -289,6 +289,9 @@ class MessageItemFactory @Inject constructor( .contentDownloadStateTrackerBinder(contentDownloadStateTrackerBinder) .highlighted(highlight) .leftGuideline(avatarSizeProvider.leftGuideline) + // Tchap: Use for the Antivirus + .elementToDecrypt(messageContent.encryptedFileInfo?.toElementToDecrypt()) + .contentScannerStateTracker(contentScannerStateTracker) } private fun getAudioFileUrl( @@ -347,6 +350,9 @@ class MessageItemFactory @Inject constructor( .contentDownloadStateTrackerBinder(contentDownloadStateTrackerBinder) .highlighted(highlight) .leftGuideline(avatarSizeProvider.leftGuideline) + // Tchap: Use for the Antivirus + .elementToDecrypt(messageContent.encryptedFileInfo?.toElementToDecrypt()) + .contentScannerStateTracker(contentScannerStateTracker) } private fun buildVerificationRequestMessageItem( diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt index 256019a2cb..05bc03f554 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt @@ -24,6 +24,7 @@ import android.view.ViewGroup import android.widget.ImageButton import android.widget.SeekBar import android.widget.TextView +import androidx.core.content.ContextCompat import androidx.core.view.isVisible import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyModelClass @@ -33,9 +34,12 @@ import im.vector.app.core.epoxy.onClick import im.vector.app.core.utils.TextUtils import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker import im.vector.app.features.home.room.detail.timeline.helper.ContentDownloadStateTrackerBinder +import im.vector.app.features.home.room.detail.timeline.helper.ContentScannerStateTracker import im.vector.app.features.home.room.detail.timeline.helper.ContentUploadStateTrackerBinder import im.vector.app.features.home.room.detail.timeline.style.TimelineMessageLayout import im.vector.app.features.themes.ThemeUtils +import me.gujun.android.span.span +import org.matrix.android.sdk.api.session.crypto.attachments.ElementToDecrypt @EpoxyModelClass abstract class MessageAudioItem : AbsMessageItem() { @@ -71,6 +75,12 @@ abstract class MessageAudioItem : AbsMessageItem() { @EpoxyAttribute lateinit var audioMessagePlaybackTracker: AudioMessagePlaybackTracker + @EpoxyAttribute + var contentScannerStateTracker: ContentScannerStateTracker? = null + + @EpoxyAttribute + var elementToDecrypt: ElementToDecrypt? = null + private var isUserSeeking = false override fun bind(holder: Holder) { @@ -82,6 +92,9 @@ abstract class MessageAudioItem : AbsMessageItem() { bindSeekBar(holder) holder.audioPlaybackControlButton.setOnClickListener { playbackControlButtonClickListener?.invoke(it) } renderStateBasedOnAudioPlayback(holder) + + holder.resetAV() + contentScannerStateTracker?.bind(attributes.informationData.eventId, mxcUrl, elementToDecrypt, holder) } private fun bindUploadState(holder: Holder) { @@ -193,11 +206,12 @@ abstract class MessageAudioItem : AbsMessageItem() { contentUploadStateTrackerBinder.unbind(attributes.informationData.eventId) contentDownloadStateTrackerBinder.unbind(mxcUrl) audioMessagePlaybackTracker.untrack(attributes.informationData.eventId) + contentScannerStateTracker?.unBind(attributes.informationData.eventId) } override fun getViewStubId() = STUB_ID - class Holder : AbsMessageItem.Holder(STUB_ID) { + class Holder : AbsMessageItem.Holder(STUB_ID), ScannableHolder { val rootLayout by bind(R.id.messageRootLayout) val mainLayout by bind(R.id.messageMainInnerLayout) val filenameView by bind(R.id.messageFilenameView) @@ -207,6 +221,46 @@ abstract class MessageAudioItem : AbsMessageItem() { val fileSize by bind(R.id.fileSize) val audioPlaybackDuration by bind(R.id.audioPlaybackDuration) val audioSeekBar by bind(R.id.audioSeekBar) + + private val messageFileAvText by bind(R.id.messageFileAvText) + + fun resetAV() { + messageFileAvText.isVisible = false + } + + override fun mediaScanResult(clean: Boolean) { + if (clean) { + messageFileAvText.text = view.context.getText(R.string.antivirus_clean) + messageFileAvText.isVisible = true + messageFileAvText.setCompoundDrawablesWithIntrinsicBounds( + ContextCompat.getDrawable(view.context, R.drawable.ic_av_checked), + null, + null, + null + ) + } else { + // disable click on infected files + audioPlaybackControlButton.setOnClickListener(null) + audioSeekBar.isEnabled = false + audioSeekBar.setOnSeekBarChangeListener(null) + filenameView.onClick(null) + + messageFileAvText.text = span(view.context.getText(R.string.antivirus_infected)) { + textColor = ThemeUtils.getColor(view.context, R.attr.colorError) + } + messageFileAvText.isVisible = true + messageFileAvText.setCompoundDrawables(null, null, null, null) + filenameView.text = view.context.getString(R.string.tchap_scan_media_untrusted_content_message, filenameView.text) + } + } + + override fun mediaScanInProgress() { + messageFileAvText.text = span(view.context.getText(R.string.antivirus_in_progress)) { + textColor = ThemeUtils.getColor(view.context, R.attr.vctr_notice_secondary) + } + messageFileAvText.isVisible = true + messageFileAvText.setCompoundDrawables(null, null, null, null) + } } companion object { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt index 93e95dd4a5..b21db8e162 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt @@ -24,6 +24,7 @@ import android.view.View import android.view.ViewGroup import android.widget.ImageButton import android.widget.TextView +import androidx.core.content.ContextCompat import androidx.core.view.doOnPreDraw import androidx.core.view.isVisible import com.airbnb.epoxy.EpoxyAttribute @@ -32,10 +33,13 @@ import im.vector.app.R import im.vector.app.core.epoxy.ClickListener import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker import im.vector.app.features.home.room.detail.timeline.helper.ContentDownloadStateTrackerBinder +import im.vector.app.features.home.room.detail.timeline.helper.ContentScannerStateTracker import im.vector.app.features.home.room.detail.timeline.helper.ContentUploadStateTrackerBinder import im.vector.app.features.home.room.detail.timeline.style.TimelineMessageLayout import im.vector.app.features.themes.ThemeUtils import im.vector.app.features.voice.AudioWaveformView +import me.gujun.android.span.span +import org.matrix.android.sdk.api.session.crypto.attachments.ElementToDecrypt @EpoxyModelClass abstract class MessageVoiceItem : AbsMessageItem() { @@ -73,6 +77,12 @@ abstract class MessageVoiceItem : AbsMessageItem() { @EpoxyAttribute lateinit var audioMessagePlaybackTracker: AudioMessagePlaybackTracker + @EpoxyAttribute + var contentScannerStateTracker: ContentScannerStateTracker? = null + + @EpoxyAttribute + var elementToDecrypt: ElementToDecrypt? = null + override fun bind(holder: Holder) { super.bind(holder) renderSendState(holder.voiceLayout, null) @@ -94,6 +104,9 @@ abstract class MessageVoiceItem : AbsMessageItem() { ThemeUtils.getColor(holder.view.context, R.attr.vctr_content_quinary) } holder.voicePlaybackLayout.backgroundTintList = ColorStateList.valueOf(backgroundTint) + + holder.resetAV() + contentScannerStateTracker?.bind(attributes.informationData.eventId, mxcUrl, elementToDecrypt, holder) } private fun onWaveformViewReady(holder: Holder) { @@ -169,13 +182,54 @@ abstract class MessageVoiceItem : AbsMessageItem() { override fun getViewStubId() = STUB_ID - class Holder : AbsMessageItem.Holder(STUB_ID) { + class Holder : AbsMessageItem.Holder(STUB_ID), ScannableHolder { val voicePlaybackLayout by bind(R.id.voicePlaybackLayout) val voiceLayout by bind(R.id.voiceLayout) val voicePlaybackControlButton by bind(R.id.voicePlaybackControlButton) val voicePlaybackTime by bind(R.id.voicePlaybackTime) val voicePlaybackWaveform by bind(R.id.voicePlaybackWaveform) val progressLayout by bind(R.id.messageFileUploadProgressLayout) + + private val messageFileAvText by bind(R.id.messageFileAvText) + + fun resetAV() { + messageFileAvText.isVisible = false + } + + override fun mediaScanResult(clean: Boolean) { + if (clean) { + messageFileAvText.text = view.context.getText(R.string.antivirus_clean) + messageFileAvText.isVisible = true + messageFileAvText.setCompoundDrawablesWithIntrinsicBounds( + ContextCompat.getDrawable(view.context, R.drawable.ic_av_checked), + null, + null, + null + ) + } else { + // disable click on infected files + voicePlaybackWaveform.setOnTouchListener(null) + voicePlaybackWaveform.setOnLongClickListener(null) + voicePlaybackControlButton.setOnClickListener(null) + + voicePlaybackControlButton.setImageResource(R.drawable.ic_cross) + voicePlaybackControlButton.contentDescription = view.context.getString(R.string.tchap_scan_media_error_file_is_infected) + + messageFileAvText.text = span(view.context.getText(R.string.antivirus_infected)) { + textColor = ThemeUtils.getColor(view.context, R.attr.colorError) + } + messageFileAvText.isVisible = true + messageFileAvText.setCompoundDrawables(null, null, null, null) + } + } + + override fun mediaScanInProgress() { + messageFileAvText.text = span(view.context.getText(R.string.antivirus_in_progress)) { + textColor = ThemeUtils.getColor(view.context, R.attr.vctr_notice_secondary) + } + messageFileAvText.isVisible = true + messageFileAvText.setCompoundDrawables(null, null, null, null) + } } companion object { diff --git a/vector/src/main/res/layout/item_timeline_event_audio_stub.xml b/vector/src/main/res/layout/item_timeline_event_audio_stub.xml index 2a6fbf5a9e..288373a9f7 100644 --- a/vector/src/main/res/layout/item_timeline_event_audio_stub.xml +++ b/vector/src/main/res/layout/item_timeline_event_audio_stub.xml @@ -94,6 +94,16 @@ + + - \ No newline at end of file + diff --git a/vector/src/main/res/layout/item_timeline_event_voice_stub.xml b/vector/src/main/res/layout/item_timeline_event_voice_stub.xml index 0fad714bd4..8331465a8a 100644 --- a/vector/src/main/res/layout/item_timeline_event_voice_stub.xml +++ b/vector/src/main/res/layout/item_timeline_event_voice_stub.xml @@ -55,6 +55,16 @@ + + Date: Tue, 6 Dec 2022 15:41:24 +0100 Subject: [PATCH 2/2] Changelog --- changelog.d/780.misc | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/780.misc diff --git a/changelog.d/780.misc b/changelog.d/780.misc new file mode 100644 index 0000000000..1a732b4fc3 --- /dev/null +++ b/changelog.d/780.misc @@ -0,0 +1 @@ +Activate the antivirus for audio files