kopia lustrzana https://github.com/ryukoposting/Signal-Android
Add initial support for rendering link previews in text story previews.
rodzic
d504bd593a
commit
da1ac5358f
|
@ -276,7 +276,7 @@ public class QuoteView extends FrameLayout implements RecipientForeverObserver {
|
||||||
if (!TextUtils.isEmpty(body) || !attachments.containsMediaSlide()) {
|
if (!TextUtils.isEmpty(body) || !attachments.containsMediaSlide()) {
|
||||||
if (isTextStory && body != null) {
|
if (isTextStory && body != null) {
|
||||||
try {
|
try {
|
||||||
bodyView.setText(StoryTextPostModel.parseFrom(body.toString()).getText());
|
bodyView.setText(StoryTextPostModel.parseFrom(body.toString(), id, author.getId()).getText());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.w(TAG, "Could not parse body of text post.", e);
|
Log.w(TAG, "Could not parse body of text post.", e);
|
||||||
bodyView.setText("");
|
bodyView.setText("");
|
||||||
|
@ -326,7 +326,7 @@ public class QuoteView extends FrameLayout implements RecipientForeverObserver {
|
||||||
|
|
||||||
private void setQuoteAttachment(@NonNull GlideRequests glideRequests, @NonNull CharSequence body, @NonNull SlideDeck slideDeck) {
|
private void setQuoteAttachment(@NonNull GlideRequests glideRequests, @NonNull CharSequence body, @NonNull SlideDeck slideDeck) {
|
||||||
if (!attachments.containsMediaSlide() && isStoryReply()) {
|
if (!attachments.containsMediaSlide() && isStoryReply()) {
|
||||||
StoryTextPostModel model = StoryTextPostModel.parseFrom(body.toString());
|
StoryTextPostModel model = StoryTextPostModel.parseFrom(body.toString(), id, author.getId());
|
||||||
attachmentVideoOverlayView.setVisibility(GONE);
|
attachmentVideoOverlayView.setVisibility(GONE);
|
||||||
attachmentContainerView.setVisibility(GONE);
|
attachmentContainerView.setVisibility(GONE);
|
||||||
thumbnailView.setVisibility(VISIBLE);
|
thumbnailView.setVisibility(VISIBLE);
|
||||||
|
|
|
@ -14,6 +14,8 @@ import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel
|
||||||
import org.thoughtcrime.securesms.mms.GlideApp
|
import org.thoughtcrime.securesms.mms.GlideApp
|
||||||
import org.thoughtcrime.securesms.mms.ImageSlide
|
import org.thoughtcrime.securesms.mms.ImageSlide
|
||||||
import org.thoughtcrime.securesms.util.Util
|
import org.thoughtcrime.securesms.util.Util
|
||||||
|
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture
|
||||||
|
import org.thoughtcrime.securesms.util.concurrent.SettableFuture
|
||||||
import org.thoughtcrime.securesms.util.visible
|
import org.thoughtcrime.securesms.util.visible
|
||||||
import java.text.DateFormat
|
import java.text.DateFormat
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
|
@ -33,7 +35,9 @@ class StoryLinkPreviewView @JvmOverloads constructor(
|
||||||
private val title: TextView = findViewById(R.id.link_preview_title)
|
private val title: TextView = findViewById(R.id.link_preview_title)
|
||||||
private val url: TextView = findViewById(R.id.link_preview_url)
|
private val url: TextView = findViewById(R.id.link_preview_url)
|
||||||
|
|
||||||
fun bind(linkPreview: LinkPreview?, hiddenVisibility: Int = View.INVISIBLE) {
|
fun bind(linkPreview: LinkPreview?, hiddenVisibility: Int = View.INVISIBLE): ListenableFuture<Boolean> {
|
||||||
|
var listenableFuture: ListenableFuture<Boolean>? = null
|
||||||
|
|
||||||
if (linkPreview != null) {
|
if (linkPreview != null) {
|
||||||
visibility = View.VISIBLE
|
visibility = View.VISIBLE
|
||||||
isClickable = true
|
isClickable = true
|
||||||
|
@ -43,7 +47,7 @@ class StoryLinkPreviewView @JvmOverloads constructor(
|
||||||
|
|
||||||
val imageSlide: ImageSlide? = linkPreview.thumbnail.map { ImageSlide(image.context, it) }.orElse(null)
|
val imageSlide: ImageSlide? = linkPreview.thumbnail.map { ImageSlide(image.context, it) }.orElse(null)
|
||||||
if (imageSlide != null) {
|
if (imageSlide != null) {
|
||||||
image.setImageResource(GlideApp.with(image), imageSlide, false, false)
|
listenableFuture = image.setImageResource(GlideApp.with(image), imageSlide, false, false)
|
||||||
image.visible = true
|
image.visible = true
|
||||||
} else {
|
} else {
|
||||||
image.visible = false
|
image.visible = false
|
||||||
|
@ -56,6 +60,8 @@ class StoryLinkPreviewView @JvmOverloads constructor(
|
||||||
visibility = hiddenVisibility
|
visibility = hiddenVisibility
|
||||||
isClickable = false
|
isClickable = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return listenableFuture ?: SettableFuture(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bind(linkPreviewState: LinkPreviewViewModel.LinkPreviewState, hiddenVisibility: Int = View.INVISIBLE) {
|
fun bind(linkPreviewState: LinkPreviewViewModel.LinkPreviewState, hiddenVisibility: Int = View.INVISIBLE) {
|
||||||
|
|
|
@ -9,8 +9,13 @@ import com.bumptech.glide.load.Options
|
||||||
import com.bumptech.glide.load.ResourceDecoder
|
import com.bumptech.glide.load.ResourceDecoder
|
||||||
import com.bumptech.glide.load.engine.Resource
|
import com.bumptech.glide.load.engine.Resource
|
||||||
import com.bumptech.glide.load.resource.SimpleResource
|
import com.bumptech.glide.load.resource.SimpleResource
|
||||||
|
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||||
|
import org.thoughtcrime.securesms.database.model.MessageRecord
|
||||||
|
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
||||||
import org.thoughtcrime.securesms.database.model.databaseprotos.StoryTextPost
|
import org.thoughtcrime.securesms.database.model.databaseprotos.StoryTextPost
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||||
|
import org.thoughtcrime.securesms.recipients.Recipient
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||||
import org.thoughtcrime.securesms.util.Base64
|
import org.thoughtcrime.securesms.util.Base64
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
|
|
||||||
|
@ -18,19 +23,36 @@ import java.security.MessageDigest
|
||||||
* Glide model to render a StoryTextPost as a bitmap
|
* Glide model to render a StoryTextPost as a bitmap
|
||||||
*/
|
*/
|
||||||
data class StoryTextPostModel(
|
data class StoryTextPostModel(
|
||||||
private val storyTextPost: StoryTextPost
|
private val storyTextPost: StoryTextPost,
|
||||||
|
private val storySentAtMillis: Long,
|
||||||
|
private val storyAuthor: RecipientId
|
||||||
) : Key {
|
) : Key {
|
||||||
|
|
||||||
override fun updateDiskCacheKey(messageDigest: MessageDigest) {
|
override fun updateDiskCacheKey(messageDigest: MessageDigest) {
|
||||||
messageDigest.update(storyTextPost.toByteArray())
|
messageDigest.update(storyTextPost.toByteArray())
|
||||||
|
messageDigest.update(storySentAtMillis.toString().toByteArray())
|
||||||
|
messageDigest.update(storyAuthor.serialize().toByteArray())
|
||||||
}
|
}
|
||||||
|
|
||||||
val text: String = storyTextPost.body
|
val text: String = storyTextPost.body
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
|
fun parseFrom(messageRecord: MessageRecord): StoryTextPostModel {
|
||||||
|
return parseFrom(
|
||||||
|
messageRecord.body,
|
||||||
|
messageRecord.timestamp,
|
||||||
|
if (messageRecord.isOutgoing) Recipient.self().id else messageRecord.individualRecipient.id
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun parseFrom(body: String): StoryTextPostModel {
|
fun parseFrom(body: String, storySentAtMillis: Long, storyAuthor: RecipientId): StoryTextPostModel {
|
||||||
return StoryTextPostModel(StoryTextPost.parseFrom(Base64.decode(body)))
|
return StoryTextPostModel(
|
||||||
|
storyTextPost = StoryTextPost.parseFrom(Base64.decode(body)),
|
||||||
|
storySentAtMillis = storySentAtMillis,
|
||||||
|
storyAuthor = storyAuthor
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,13 +66,13 @@ data class StoryTextPostModel(
|
||||||
override fun handles(source: StoryTextPostModel, options: Options): Boolean = true
|
override fun handles(source: StoryTextPostModel, options: Options): Boolean = true
|
||||||
|
|
||||||
override fun decode(source: StoryTextPostModel, width: Int, height: Int, options: Options): Resource<Bitmap> {
|
override fun decode(source: StoryTextPostModel, width: Int, height: Int, options: Options): Resource<Bitmap> {
|
||||||
|
val message = SignalDatabase.mmsSms.getMessageFor(source.storySentAtMillis, source.storyAuthor)
|
||||||
val view = StoryTextPostView(ApplicationDependencies.getApplication())
|
val view = StoryTextPostView(ApplicationDependencies.getApplication())
|
||||||
|
|
||||||
view.measure(View.MeasureSpec.makeMeasureSpec(RENDER_WIDTH, View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(RENDER_HEIGHT, View.MeasureSpec.EXACTLY))
|
|
||||||
view.layout(0, 0, view.measuredWidth, view.measuredHeight)
|
|
||||||
|
|
||||||
view.bindFromStoryTextPost(source.storyTextPost)
|
view.bindFromStoryTextPost(source.storyTextPost)
|
||||||
|
view.bindLinkPreview((message as? MmsMessageRecord)?.linkPreviews?.firstOrNull())
|
||||||
|
|
||||||
|
view.invalidate()
|
||||||
view.measure(View.MeasureSpec.makeMeasureSpec(RENDER_WIDTH, View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(RENDER_HEIGHT, View.MeasureSpec.EXACTLY))
|
view.measure(View.MeasureSpec.makeMeasureSpec(RENDER_WIDTH, View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(RENDER_HEIGHT, View.MeasureSpec.EXACTLY))
|
||||||
view.layout(0, 0, view.measuredWidth, view.measuredHeight)
|
view.layout(0, 0, view.measuredWidth, view.measuredHeight)
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ import org.thoughtcrime.securesms.mediasend.v2.text.TextAlignment
|
||||||
import org.thoughtcrime.securesms.mediasend.v2.text.TextStoryPostCreationState
|
import org.thoughtcrime.securesms.mediasend.v2.text.TextStoryPostCreationState
|
||||||
import org.thoughtcrime.securesms.mediasend.v2.text.TextStoryScale
|
import org.thoughtcrime.securesms.mediasend.v2.text.TextStoryScale
|
||||||
import org.thoughtcrime.securesms.util.ViewUtil
|
import org.thoughtcrime.securesms.util.ViewUtil
|
||||||
|
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture
|
||||||
import org.thoughtcrime.securesms.util.visible
|
import org.thoughtcrime.securesms.util.visible
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
|
@ -160,8 +161,8 @@ class StoryTextPostView @JvmOverloads constructor(
|
||||||
postAdjustLinkPreviewTranslationY()
|
postAdjustLinkPreviewTranslationY()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bindLinkPreview(linkPreview: LinkPreview?) {
|
fun bindLinkPreview(linkPreview: LinkPreview?): ListenableFuture<Boolean> {
|
||||||
linkPreviewView.bind(linkPreview, View.GONE)
|
return linkPreviewView.bind(linkPreview, View.GONE)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bindLinkPreviewState(linkPreviewState: LinkPreviewViewModel.LinkPreviewState, hiddenVisibility: Int) {
|
fun bindLinkPreviewState(linkPreviewState: LinkPreviewViewModel.LinkPreviewState, hiddenVisibility: Int) {
|
||||||
|
|
|
@ -9,12 +9,10 @@ import org.thoughtcrime.securesms.avatar.view.AvatarView
|
||||||
import org.thoughtcrime.securesms.components.ThumbnailView
|
import org.thoughtcrime.securesms.components.ThumbnailView
|
||||||
import org.thoughtcrime.securesms.components.settings.PreferenceModel
|
import org.thoughtcrime.securesms.components.settings.PreferenceModel
|
||||||
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord
|
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord
|
||||||
import org.thoughtcrime.securesms.database.model.databaseprotos.StoryTextPost
|
|
||||||
import org.thoughtcrime.securesms.mms.GlideApp
|
import org.thoughtcrime.securesms.mms.GlideApp
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient
|
import org.thoughtcrime.securesms.recipients.Recipient
|
||||||
import org.thoughtcrime.securesms.stories.StoryTextPostModel
|
import org.thoughtcrime.securesms.stories.StoryTextPostModel
|
||||||
import org.thoughtcrime.securesms.stories.dialogs.StoryContextMenu
|
import org.thoughtcrime.securesms.stories.dialogs.StoryContextMenu
|
||||||
import org.thoughtcrime.securesms.util.Base64
|
|
||||||
import org.thoughtcrime.securesms.util.DateUtils
|
import org.thoughtcrime.securesms.util.DateUtils
|
||||||
import org.thoughtcrime.securesms.util.SpanUtil
|
import org.thoughtcrime.securesms.util.SpanUtil
|
||||||
import org.thoughtcrime.securesms.util.adapter.mapping.LayoutFactory
|
import org.thoughtcrime.securesms.util.adapter.mapping.LayoutFactory
|
||||||
|
@ -111,7 +109,7 @@ object StoriesLandingItem {
|
||||||
avatarView.setStoryRingFromState(model.data.storyViewState)
|
avatarView.setStoryRingFromState(model.data.storyViewState)
|
||||||
|
|
||||||
if (record.storyType.isTextStory) {
|
if (record.storyType.isTextStory) {
|
||||||
storyPreview.setImageResource(GlideApp.with(storyPreview), StoryTextPostModel(StoryTextPost.parseFrom(Base64.decode(record.body))), 0, 0)
|
storyPreview.setImageResource(GlideApp.with(storyPreview), StoryTextPostModel.parseFrom(record), 0, 0)
|
||||||
} else if (record.slideDeck.thumbnailSlide != null) {
|
} else if (record.slideDeck.thumbnailSlide != null) {
|
||||||
storyPreview.setImageResource(GlideApp.with(storyPreview), record.slideDeck.thumbnailSlide!!, false, true)
|
storyPreview.setImageResource(GlideApp.with(storyPreview), record.slideDeck.thumbnailSlide!!, false, true)
|
||||||
} else {
|
} else {
|
||||||
|
@ -122,7 +120,7 @@ object StoriesLandingItem {
|
||||||
val secondaryRecord = model.data.secondaryStory.messageRecord as MediaMmsMessageRecord
|
val secondaryRecord = model.data.secondaryStory.messageRecord as MediaMmsMessageRecord
|
||||||
|
|
||||||
if (secondaryRecord.storyType.isTextStory) {
|
if (secondaryRecord.storyType.isTextStory) {
|
||||||
storyMulti.setImageResource(GlideApp.with(storyPreview), StoryTextPostModel(StoryTextPost.parseFrom(Base64.decode(secondaryRecord.body))), 0, 0)
|
storyMulti.setImageResource(GlideApp.with(storyPreview), StoryTextPostModel.parseFrom(secondaryRecord), 0, 0)
|
||||||
} else {
|
} else {
|
||||||
storyMulti.setImageResource(GlideApp.with(storyPreview), secondaryRecord.slideDeck.thumbnailSlide!!, false, true)
|
storyMulti.setImageResource(GlideApp.with(storyPreview), secondaryRecord.slideDeck.thumbnailSlide!!, false, true)
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,11 +11,9 @@ import org.thoughtcrime.securesms.components.menu.SignalContextMenu
|
||||||
import org.thoughtcrime.securesms.components.settings.PreferenceModel
|
import org.thoughtcrime.securesms.components.settings.PreferenceModel
|
||||||
import org.thoughtcrime.securesms.conversation.ConversationMessage
|
import org.thoughtcrime.securesms.conversation.ConversationMessage
|
||||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
||||||
import org.thoughtcrime.securesms.database.model.databaseprotos.StoryTextPost
|
|
||||||
import org.thoughtcrime.securesms.mms.GlideApp
|
import org.thoughtcrime.securesms.mms.GlideApp
|
||||||
import org.thoughtcrime.securesms.mms.Slide
|
import org.thoughtcrime.securesms.mms.Slide
|
||||||
import org.thoughtcrime.securesms.stories.StoryTextPostModel
|
import org.thoughtcrime.securesms.stories.StoryTextPostModel
|
||||||
import org.thoughtcrime.securesms.util.Base64
|
|
||||||
import org.thoughtcrime.securesms.util.DateUtils
|
import org.thoughtcrime.securesms.util.DateUtils
|
||||||
import org.thoughtcrime.securesms.util.SpanUtil
|
import org.thoughtcrime.securesms.util.SpanUtil
|
||||||
import org.thoughtcrime.securesms.util.adapter.mapping.LayoutFactory
|
import org.thoughtcrime.securesms.util.adapter.mapping.LayoutFactory
|
||||||
|
@ -103,7 +101,7 @@ object MyStoriesItem {
|
||||||
|
|
||||||
@Suppress("CascadeIf")
|
@Suppress("CascadeIf")
|
||||||
if (record.storyType.isTextStory) {
|
if (record.storyType.isTextStory) {
|
||||||
storyPreview.setImageResource(GlideApp.with(storyPreview), StoryTextPostModel(StoryTextPost.parseFrom(Base64.decode(record.body))), 0, 0)
|
storyPreview.setImageResource(GlideApp.with(storyPreview), StoryTextPostModel.parseFrom(record), 0, 0)
|
||||||
} else if (thumbnail != null) {
|
} else if (thumbnail != null) {
|
||||||
storyPreview.setImageResource(GlideApp.with(itemView), thumbnail, false, true)
|
storyPreview.setImageResource(GlideApp.with(itemView), thumbnail, false, true)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -21,10 +21,13 @@ class StoryViewerActivity : PassphraseRequiredActivity() {
|
||||||
|
|
||||||
if (savedInstanceState == null) {
|
if (savedInstanceState == null) {
|
||||||
supportFragmentManager.beginTransaction()
|
supportFragmentManager.beginTransaction()
|
||||||
.replace(R.id.fragment_container, StoryViewerFragment.create(
|
.replace(
|
||||||
intent.getParcelableExtra(ARG_START_RECIPIENT_ID)!!,
|
R.id.fragment_container,
|
||||||
intent.getLongExtra(ARG_START_STORY_ID, -1L)
|
StoryViewerFragment.create(
|
||||||
))
|
intent.getParcelableExtra(ARG_START_RECIPIENT_ID)!!,
|
||||||
|
intent.getLongExtra(ARG_START_STORY_ID, -1L)
|
||||||
|
)
|
||||||
|
)
|
||||||
.commit()
|
.commit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Ładowanie…
Reference in New Issue