CFV2 handle keyboard images and gifs.

This commit is contained in:
Alex Hart 2023-07-18 12:51:07 -03:00 committed by Nicholas
parent b8f55f982f
commit f3fb5ccc3b
5 changed files with 98 additions and 32 deletions

View file

@ -1,13 +1,9 @@
package org.thoughtcrime.securesms.conversation.drafts
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Color
import android.net.Uri
import android.text.Spannable
import android.text.SpannableString
import androidx.annotation.WorkerThread
import com.bumptech.glide.load.engine.DiskCacheStrategy
import io.reactivex.rxjava3.core.Maybe
import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.schedulers.Schedulers
@ -34,11 +30,10 @@ import org.thoughtcrime.securesms.database.model.MessageId
import org.thoughtcrime.securesms.database.model.MessageRecord
import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.keyboard.KeyboardUtil
import org.thoughtcrime.securesms.mediasend.Media
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri
import org.thoughtcrime.securesms.mms.GifSlide
import org.thoughtcrime.securesms.mms.GlideApp
import org.thoughtcrime.securesms.mms.GlideRequests
import org.thoughtcrime.securesms.mms.ImageSlide
import org.thoughtcrime.securesms.mms.PartAuthority
import org.thoughtcrime.securesms.mms.QuoteId
@ -53,10 +48,7 @@ import org.thoughtcrime.securesms.util.concurrent.SerialMonoLifoExecutor
import org.thoughtcrime.securesms.util.hasTextSlide
import org.thoughtcrime.securesms.util.requireTextSlide
import java.io.IOException
import java.util.concurrent.ExecutionException
import java.util.concurrent.Executor
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException
class DraftRepository(
private val context: Context = ApplicationDependencies.getApplication(),
@ -96,7 +88,7 @@ class DraftRepository(
}
if (shareMedia != null && shareContentType != null && borderless) {
val details = getKeyboardImageDetails(GlideApp.with(context), shareMedia)
val details = KeyboardUtil.getImageDetails(GlideApp.with(context), shareMedia)
if (details == null || !details.hasTransparency) {
return ShareOrDraftData.SetMedia(shareMedia, shareMediaType!!, null) to null
@ -260,26 +252,6 @@ class DraftRepository(
return ConversationMessageFactory.createWithUnresolvedData(context, messageRecord, threadRecipient)
}
@WorkerThread
private fun getKeyboardImageDetails(glideRequests: GlideRequests, uri: Uri): KeyboardImageDetails? {
return try {
val bitmap: Bitmap = glideRequests.asBitmap()
.load(DecryptableUri(uri))
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.submit()
.get(1000, TimeUnit.MILLISECONDS)
val topLeft = bitmap.getPixel(0, 0)
KeyboardImageDetails(bitmap.width, bitmap.height, Color.alpha(topLeft) < 255)
} catch (e: InterruptedException) {
null
} catch (e: ExecutionException) {
null
} catch (e: TimeoutException) {
null
}
}
data class DatabaseDraft(val drafts: Drafts, val updatedText: CharSequence?)
sealed interface ShareOrDraftData {
@ -292,6 +264,4 @@ class DraftRepository(
data class SetQuote(val quote: ConversationMessage, val draftText: CharSequence?) : ShareOrDraftData
data class SetEditMessage(val messageEdit: ConversationMessage, val draftText: CharSequence?) : ShareOrDraftData
}
data class KeyboardImageDetails(val width: Int, val height: Int, val hasTransparency: Boolean)
}

View file

@ -216,6 +216,7 @@ import org.thoughtcrime.securesms.invites.InviteActions
import org.thoughtcrime.securesms.keyboard.KeyboardPage
import org.thoughtcrime.securesms.keyboard.KeyboardPagerFragment
import org.thoughtcrime.securesms.keyboard.KeyboardPagerViewModel
import org.thoughtcrime.securesms.keyboard.KeyboardUtil
import org.thoughtcrime.securesms.keyboard.emoji.EmojiKeyboardPageFragment
import org.thoughtcrime.securesms.keyboard.emoji.search.EmojiSearchFragment
import org.thoughtcrime.securesms.keyboard.gif.GifKeyboardPageFragment
@ -921,6 +922,7 @@ class ConversationFragment :
initializeInlineSearch()
inputPanel.setListener(InputPanelListener())
inputPanel.setMediaListener(InputPanelMediaListener())
viewModel
.getScheduledMessagesCount()
@ -3598,6 +3600,42 @@ class ConversationFragment :
}
}
private inner class InputPanelMediaListener : InputPanel.MediaListener {
override fun onMediaSelected(uri: Uri, contentType: String?) {
if (MediaUtil.isGif(contentType) || MediaUtil.isImageType(contentType)) {
disposables += viewModel.getKeyboardImageDetails(uri)
.observeOn(AndroidSchedulers.mainThread())
.subscribeBy(
onSuccess = {
sendKeyboardImage(uri, contentType!!, it)
},
onComplete = {
sendKeyboardImage(uri, contentType!!, null)
}
)
} else if (MediaUtil.isVideoType(contentType)) {
setMedia(uri, SlideFactory.MediaType.VIDEO)
} else {
setMedia(uri, SlideFactory.MediaType.AUDIO)
}
}
private fun sendKeyboardImage(uri: Uri, contentType: String, keyboardImageDetails: KeyboardUtil.ImageDetails?) {
if (keyboardImageDetails == null || !keyboardImageDetails.hasTransparency) {
setMedia(uri, requireNotNull(SlideFactory.MediaType.from(contentType)))
return
}
val slide: Slide = when {
MediaUtil.isGif(contentType) -> GifSlide(requireContext(), uri, 0, keyboardImageDetails.width, keyboardImageDetails.height, true, null)
MediaUtil.isImageType(contentType) -> ImageSlide(requireContext(), uri, contentType, 0, keyboardImageDetails.width, keyboardImageDetails.height, true, null, null)
else -> null
} ?: error("Only images are supported!")
sendMessageWithoutComposeInput(slide)
}
}
//endregion
//region Attachment + Media Keyboard

View file

@ -74,9 +74,11 @@ import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.jobs.MultiDeviceViewOnceOpenJob
import org.thoughtcrime.securesms.jobs.ServiceOutageDetectionJob
import org.thoughtcrime.securesms.keyboard.KeyboardUtil
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.linkpreview.LinkPreview
import org.thoughtcrime.securesms.messagerequests.MessageRequestState
import org.thoughtcrime.securesms.mms.GlideApp
import org.thoughtcrime.securesms.mms.GlideRequests
import org.thoughtcrime.securesms.mms.OutgoingMessage
import org.thoughtcrime.securesms.mms.PartAuthority
@ -120,6 +122,15 @@ class ConversationRepository(
private val applicationContext = context.applicationContext
private val oldConversationRepository = org.thoughtcrime.securesms.conversation.ConversationRepository()
/**
* Gets image details for an image sent from the keyboard
*/
fun getKeyboardImageDetails(uri: Uri): Maybe<KeyboardUtil.ImageDetails> {
return MaybeCompat.fromCallable {
KeyboardUtil.getImageDetails(GlideApp.with(applicationContext), uri)
}.subscribeOn(Schedulers.io())
}
/**
* Loads the details necessary to display the conversation thread.
*/

View file

@ -48,6 +48,7 @@ import org.thoughtcrime.securesms.database.model.StoryViewState
import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob
import org.thoughtcrime.securesms.keyboard.KeyboardUtil
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.linkpreview.LinkPreview
import org.thoughtcrime.securesms.messagerequests.MessageRequestRepository
@ -320,6 +321,10 @@ class ConversationViewModel(
}
}
fun getKeyboardImageDetails(uri: Uri): Maybe<KeyboardUtil.ImageDetails> {
return repository.getKeyboardImageDetails(uri)
}
private fun MessageRecord.oldReactionRecord(): ReactionRecord? {
return reactions.firstOrNull { it.author == Recipient.self().id }
}

View file

@ -0,0 +1,42 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.keyboard
import android.graphics.Bitmap
import android.graphics.Color
import android.net.Uri
import androidx.annotation.WorkerThread
import com.bumptech.glide.load.engine.DiskCacheStrategy
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader
import org.thoughtcrime.securesms.mms.GlideRequests
import java.util.concurrent.ExecutionException
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException
object KeyboardUtil {
@WorkerThread
fun getImageDetails(glideRequests: GlideRequests, uri: Uri): ImageDetails? {
return try {
val bitmap: Bitmap = glideRequests.asBitmap()
.load(DecryptableStreamUriLoader.DecryptableUri(uri))
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.submit()
.get(1000, TimeUnit.MILLISECONDS)
val topLeft = bitmap.getPixel(0, 0)
ImageDetails(bitmap.width, bitmap.height, Color.alpha(topLeft) < 255)
} catch (e: InterruptedException) {
null
} catch (e: ExecutionException) {
null
} catch (e: TimeoutException) {
null
}
}
data class ImageDetails(val width: Int, val height: Int, val hasTransparency: Boolean)
}