From e62785ecae3d9f39abbae918b76ecc2ce8624951 Mon Sep 17 00:00:00 2001 From: jubb Date: Wed, 23 Jun 2021 11:54:17 +1000 Subject: [PATCH 01/20] feat: set visible thread to prevent notifications in ConversationActivityV2.kt --- .../conversation/v2/ConversationActivityV2.kt | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index 26f6a0689c..70211300f8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -22,7 +22,7 @@ import kotlinx.android.synthetic.main.view_input_bar_recording.* import kotlinx.android.synthetic.main.view_input_bar_recording.view.* import network.loki.messenger.R import org.session.libsession.messaging.mentions.MentionsManager -import org.session.libsession.utilities.Util +import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.conversation.v2.input_bar.InputBarButton import org.thoughtcrime.securesms.conversation.v2.input_bar.InputBarDelegate @@ -100,6 +100,16 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe restoreDraftIfNeeded() } + override fun onResume() { + super.onResume() + ApplicationContext.getInstance(this).messageNotifier.setVisibleThread(threadID) + } + + override fun onPause() { + super.onPause() + ApplicationContext.getInstance(this).messageNotifier.setVisibleThread(-1) + } + private fun setUpRecyclerView() { conversationRecyclerView.adapter = adapter val layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, true) From 277c74185180ce45376675543300e6ee603c139a Mon Sep 17 00:00:00 2001 From: jubb Date: Thu, 24 Jun 2021 16:15:13 +1000 Subject: [PATCH 02/20] feat: AlbumThumbnailView.kt view visible and binding to thumbnail slides --- .../components/AlbumThumbnailView.java | 3 +- .../components/ConversationItemThumbnail.java | 1 - .../components/v2/AlbumThumbnailView.kt | 99 ++++++++++ .../components/v2/ThumbnailDimensDelegate.kt | 91 +++++++++ .../securesms/components/v2/ThumbnailView.kt | 174 ++++++++++++++++++ .../v2/messages/VisibleMessageContentView.kt | 12 +- app/src/main/res/layout/album_thumbnail_1.xml | 19 ++ app/src/main/res/layout/album_thumbnail_2.xml | 4 +- app/src/main/res/layout/album_thumbnail_3.xml | 6 +- app/src/main/res/layout/album_thumbnail_4.xml | 8 +- app/src/main/res/layout/album_thumbnail_5.xml | 10 +- .../main/res/layout/album_thumbnail_many.xml | 10 +- 12 files changed, 409 insertions(+), 28 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/components/v2/AlbumThumbnailView.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/components/v2/ThumbnailDimensDelegate.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/components/v2/ThumbnailView.kt create mode 100644 app/src/main/res/layout/album_thumbnail_1.xml diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/AlbumThumbnailView.java b/app/src/main/java/org/thoughtcrime/securesms/components/AlbumThumbnailView.java index 9f472fb069..6c8c881264 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/AlbumThumbnailView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/AlbumThumbnailView.java @@ -11,6 +11,8 @@ import android.widget.FrameLayout; import android.widget.TextView; import network.loki.messenger.R; + +import org.thoughtcrime.securesms.components.v2.ThumbnailView; import org.thoughtcrime.securesms.mms.GlideRequests; import org.thoughtcrime.securesms.mms.Slide; @@ -149,7 +151,6 @@ public class AlbumThumbnailView extends FrameLayout { private void setSlide(@NonNull GlideRequests glideRequests, @NonNull Slide slide, @IdRes int id) { ThumbnailView cell = findViewById(id); cell.setImageResource(glideRequests, slide, false, false); - cell.setLoadIndicatorVisibile(slide.isInProgress()); cell.setThumbnailClickListener(defaultThumbnailClickListener); cell.setOnLongClickListener(defaultLongClickListener); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemThumbnail.java b/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemThumbnail.java index 379b5c77a7..3afd68db1b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemThumbnail.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemThumbnail.java @@ -72,7 +72,6 @@ public class ConversationItemThumbnail extends FrameLayout { } } - @SuppressWarnings("SuspiciousNameCombination") @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/v2/AlbumThumbnailView.kt b/app/src/main/java/org/thoughtcrime/securesms/components/v2/AlbumThumbnailView.kt new file mode 100644 index 0000000000..0031033f39 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/v2/AlbumThumbnailView.kt @@ -0,0 +1,99 @@ +package org.thoughtcrime.securesms.components.v2 + +import android.content.Context +import android.graphics.Canvas +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.FrameLayout +import kotlinx.android.synthetic.main.album_thumbnail_view.view.* +import network.loki.messenger.R +import org.thoughtcrime.securesms.components.CornerMask +import org.thoughtcrime.securesms.database.model.MmsMessageRecord +import org.thoughtcrime.securesms.mms.GlideRequests + +class AlbumThumbnailView: FrameLayout { + + // region Lifecycle + constructor(context: Context) : super(context) { initialize() } + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { initialize() } + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { initialize() } + + private val albumCellContainer by lazy { album_cell_container } + private lateinit var cornerMask: CornerMask + + private fun initialize() { + LayoutInflater.from(context).inflate(R.layout.album_thumbnail_view, this) + cornerMask = CornerMask(this) + cornerMask.setRadius(80) + } + + override fun dispatchDraw(canvas: Canvas?) { + super.dispatchDraw(canvas) + cornerMask.mask(canvas) + } + + // endregion + + // region Interaction + + fun bind(glideRequests: GlideRequests, message: MmsMessageRecord, isStart: Boolean, isEnd: Boolean) { + // TODO: optimize for same size + val slides = message.slideDeck.thumbnailSlides + if (slides.isEmpty()) { + // this should never be encountered because it's checked by parent + return + } + calculateRadius(isStart, isEnd, message.isOutgoing) + albumCellContainer.removeAllViews() + LayoutInflater.from(context).inflate(layoutRes(slides.size), albumCellContainer) + // iterate + slides.take(5).forEachIndexed { position, slide -> + getThumbnailView(position).setImageResource(glideRequests, slide, showControls = false, isPreview = false) + } + } + + // endregion + + + fun layoutRes(slideCount: Int) = when (slideCount) { + 1 -> R.layout.album_thumbnail_1 // single + 2 -> R.layout.album_thumbnail_2// two sidebyside + 3 -> R.layout.album_thumbnail_3// three stacked + 4 -> R.layout.album_thumbnail_4// four square + 5 -> R.layout.album_thumbnail_5// + else -> R.layout.album_thumbnail_many// five or more + } + + fun getThumbnailView(position: Int): ThumbnailView = when (position) { + 0 -> albumCellContainer.findViewById(R.id.album_cell_container).findViewById(R.id.album_cell_1) + 1 -> albumCellContainer.findViewById(R.id.album_cell_container).findViewById(R.id.album_cell_2) + 2 -> albumCellContainer.findViewById(R.id.album_cell_container).findViewById(R.id.album_cell_3) + 3 -> albumCellContainer.findViewById(R.id.album_cell_container).findViewById(R.id.album_cell_4) + 4 -> albumCellContainer.findViewById(R.id.album_cell_container).findViewById(R.id.album_cell_5) + else -> throw Exception("Can't get thumbnail view for non-existent thumbnail at position: $position") + } + + fun calculateRadius(isStart: Boolean, isEnd: Boolean, outgoing: Boolean) { + val roundedDimen = context.resources.getDimension(R.dimen.message_corner_radius).toInt() + val collapsedDimen = context.resources.getDimension(R.dimen.message_corner_collapse_radius).toInt() + val (startTop, endTop, startBottom, endBottom) = when { + // single message, consistent dimen + isStart && isEnd -> intArrayOf(roundedDimen, roundedDimen, roundedDimen, roundedDimen) + // start of message cluster, collapsed BL + isStart -> intArrayOf(roundedDimen, roundedDimen, collapsedDimen, roundedDimen) + // end of message cluster, collapsed TL + isEnd -> intArrayOf(collapsedDimen, roundedDimen, roundedDimen, roundedDimen) + // else in the middle, no rounding left side + else -> intArrayOf(collapsedDimen, roundedDimen, collapsedDimen, roundedDimen) + } + // TL, TR, BR, BL (CW direction) + cornerMask.setRadii( + if (!outgoing) startTop else endTop, // TL + if (!outgoing) endTop else startTop, // TR + if (!outgoing) endBottom else startBottom, // BR + if (!outgoing) startBottom else endBottom // BL + ) + } + +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/v2/ThumbnailDimensDelegate.kt b/app/src/main/java/org/thoughtcrime/securesms/components/v2/ThumbnailDimensDelegate.kt new file mode 100644 index 0000000000..c4c7246d02 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/v2/ThumbnailDimensDelegate.kt @@ -0,0 +1,91 @@ +package org.thoughtcrime.securesms.components.v2 + +class ThumbnailDimensDelegate { + + companion object { + // dimens array constants + private const val WIDTH = 0 + private const val HEIGHT = 1 + private const val DIMENS_ARRAY_SIZE = 2 + + // bounds array constants + private const val MIN_WIDTH = 0 + private const val MIN_HEIGHT = 1 + private const val MAX_WIDTH = 2 + private const val MAX_HEIGHT = 3 + private const val BOUNDS_ARRAY_SIZE = 4 + + // const zero int array + private val EMPTY_DIMENS = intArrayOf(0,0) + + } + + private val measured: IntArray = IntArray(DIMENS_ARRAY_SIZE) + private val dimens: IntArray = IntArray(DIMENS_ARRAY_SIZE) + private val bounds: IntArray = IntArray(BOUNDS_ARRAY_SIZE) + + fun resourceSize(): IntArray { + if (dimens.all { it == 0 }) { + // dimens are (0, 0), don't go any further + return EMPTY_DIMENS + } + + val naturalWidth = dimens[WIDTH].toDouble() + val naturalHeight = dimens[HEIGHT].toDouble() + val minWidth = dimens[MIN_WIDTH] + val maxWidth = dimens[MAX_WIDTH] + val minHeight = dimens[MIN_HEIGHT] + val maxHeight = dimens[MAX_HEIGHT] + + // calculate actual measured + var measuredWidth: Double = naturalWidth + var measuredHeight: Double = naturalHeight + + val widthInBounds = measuredWidth >= minWidth && measuredWidth <= maxWidth + val heightInBounds = measuredHeight >= minHeight && measuredHeight <= maxHeight + + if (!widthInBounds || !heightInBounds) { + val minWidthRatio: Double = naturalWidth / minWidth + val maxWidthRatio: Double = naturalWidth / maxWidth + val minHeightRatio: Double = naturalHeight / minHeight + val maxHeightRatio: Double = naturalHeight / maxHeight + if (maxWidthRatio > 1 || maxHeightRatio > 1) { + if (maxWidthRatio >= maxHeightRatio) { + measuredWidth /= maxWidthRatio + measuredHeight /= maxWidthRatio + } else { + measuredWidth /= maxHeightRatio + measuredHeight /= maxHeightRatio + } + measuredWidth = Math.max(measuredWidth, minWidth.toDouble()) + measuredHeight = Math.max(measuredHeight, minHeight.toDouble()) + } else if (minWidthRatio < 1 || minHeightRatio < 1) { + if (minWidthRatio <= minHeightRatio) { + measuredWidth /= minWidthRatio + measuredHeight /= minWidthRatio + } else { + measuredWidth /= minHeightRatio + measuredHeight /= minHeightRatio + } + measuredWidth = Math.min(measuredWidth, maxWidth.toDouble()) + measuredHeight = Math.min(measuredHeight, maxHeight.toDouble()) + } + } + measured[WIDTH] = measuredWidth.toInt() + measured[HEIGHT] = measuredHeight.toInt() + return measured + } + + fun setBounds(minWidth: Int, minHeight: Int, maxWidth: Int, maxHeight: Int) { + bounds[MIN_WIDTH] = minWidth + bounds[MIN_HEIGHT] = minHeight + bounds[MAX_WIDTH] = maxWidth + bounds[MAX_HEIGHT] = maxHeight + } + + fun setDimens(width: Int, height: Int) { + dimens[WIDTH] = width + dimens[HEIGHT] = height + } + +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/v2/ThumbnailView.kt b/app/src/main/java/org/thoughtcrime/securesms/components/v2/ThumbnailView.kt new file mode 100644 index 0000000000..89af2faad9 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/v2/ThumbnailView.kt @@ -0,0 +1,174 @@ +package org.thoughtcrime.securesms.components.v2 + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.drawable.Drawable +import android.util.AttributeSet +import android.widget.FrameLayout +import android.widget.ProgressBar +import androidx.core.view.isVisible +import com.bumptech.glide.load.engine.DiskCacheStrategy +import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions +import com.bumptech.glide.request.RequestOptions +import kotlinx.android.synthetic.main.thumbnail_view.view.* +import network.loki.messenger.R +import org.session.libsession.messaging.sending_receiving.attachments.AttachmentTransferProgress +import org.session.libsession.utilities.Util.equals +import org.session.libsession.utilities.ViewUtil +import org.thoughtcrime.securesms.components.TransferControlView +import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri +import org.thoughtcrime.securesms.mms.GlideRequest +import org.thoughtcrime.securesms.mms.GlideRequests +import org.thoughtcrime.securesms.mms.Slide +import org.thoughtcrime.securesms.mms.SlideClickListener + +class ThumbnailView: FrameLayout { + + companion object { + private const val WIDTH = 0 + private const val HEIGHT = 1 + } + + // region Lifecycle + constructor(context: Context) : super(context) { initialize(null) } + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { initialize(attrs) } + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { initialize(attrs) } + + private val image by lazy { thumbnail_image } + private val playOverlay by lazy { play_overlay } + private val captionIcon by lazy { thumbnail_caption_icon } + val loadIndicator: ProgressBar by lazy { thumbnail_load_indicator } + private val transferControls by lazy { ViewUtil.inflateStub(this, R.id.transfer_controls_stub) } + + private val dimensDelegate = ThumbnailDimensDelegate() + + var thumbnailClickListener: SlideClickListener? = null + + private var slide: Slide? = null + + private fun initialize(attrs: AttributeSet?) { + inflate(context, R.layout.thumbnail_view, this) + if (attrs != null) { + val typedArray = context.theme.obtainStyledAttributes(attrs, R.styleable.ThumbnailView, 0, 0) + + dimensDelegate.setBounds(typedArray.getDimensionPixelSize(R.styleable.ConversationItemThumbnail_conversationThumbnail_minWidth, 0), + typedArray.getDimensionPixelSize(R.styleable.ConversationItemThumbnail_conversationThumbnail_minHeight, 0), + typedArray.getDimensionPixelSize(R.styleable.ConversationItemThumbnail_conversationThumbnail_maxWidth, 0), + typedArray.getDimensionPixelSize(R.styleable.ConversationItemThumbnail_conversationThumbnail_maxHeight, 0)) + + typedArray.recycle() + } + } + + // region Lifecycle + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + val adjustedDimens = dimensDelegate.resourceSize() + if (adjustedDimens[WIDTH] == 0 && adjustedDimens[HEIGHT] == 0) { + return super.onMeasure(widthMeasureSpec, heightMeasureSpec) + } + + val finalWidth: Int = adjustedDimens[WIDTH] + paddingLeft + paddingRight + val finalHeight: Int = adjustedDimens[HEIGHT] + paddingTop + paddingBottom + + super.onMeasure( + MeasureSpec.makeMeasureSpec(finalWidth, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(finalHeight, MeasureSpec.EXACTLY) + ) + } + + private fun getDefaultWidth() = maxOf(layoutParams?.width ?: 0, 0) + private fun getDefaultHeight() = maxOf(layoutParams?.height ?: 0, 0) + + // endregion + + // region Interaction + fun setImageResource(glide: GlideRequests, slide: Slide, showControls: Boolean, isPreview: Boolean) { + return setImageResource(glide, slide, showControls, isPreview, 0, 0) + } + + fun setImageResource(glide: GlideRequests, slide: Slide, + showControls: Boolean, isPreview: Boolean, + naturalWidth: Int, naturalHeight: Int) { + + val currentSlide = this.slide + + if (showControls) { + transferControls.setSlide(slide) +// transferControls.setDownloadClickListener() TODO: re-add this + } else { + transferControls.isVisible = false + } + + playOverlay.isVisible = (slide.thumbnailUri != null && slide.hasPlayOverlay() && + (slide.transferState == AttachmentTransferProgress.TRANSFER_PROGRESS_DONE || isPreview)) + + if (equals(currentSlide, slide)) { + // don't re-load slide + return + } + + + if (currentSlide != null && currentSlide.fastPreflightId != null && currentSlide.fastPreflightId == slide.fastPreflightId) { + // not reloading slide for fast preflight + this.slide = slide + } + + this.slide = slide + + captionIcon.isVisible = slide.caption.isPresent + loadIndicator.isVisible = slide.isInProgress + + dimensDelegate.setDimens(naturalWidth, naturalHeight) + invalidate() + + when { + slide.thumbnailUri != null -> { + buildThumbnailGlideRequest(glide, slide).into(image) + } + slide.hasPlaceholder() -> { + buildPlaceholderGlideRequest(glide, slide).into(image) + } + else -> { + glide.clear(image) + } + } + } + + fun buildThumbnailGlideRequest(glide: GlideRequests, slide: Slide): GlideRequest { + + val dimens = dimensDelegate.resourceSize() + + val request = glide.load(DecryptableUri(slide.thumbnailUri!!)) + .diskCacheStrategy(DiskCacheStrategy.RESOURCE) + .let { request -> + if (dimens[WIDTH] == 0 || dimens[HEIGHT] == 0) { + request.override(getDefaultWidth(), getDefaultHeight()) + } else { + request.override(dimens[WIDTH], dimens[HEIGHT]) + } + } + .transition(DrawableTransitionOptions.withCrossFade()) + .centerCrop() + + return if (slide.isInProgress) request else request.apply(RequestOptions.errorOf(R.drawable.ic_missing_thumbnail_picture)) + } + + fun buildPlaceholderGlideRequest(glide: GlideRequests, slide: Slide): GlideRequest { + + val dimens = dimensDelegate.resourceSize() + + return glide.asBitmap() + .load(slide.getPlaceholderRes(context.theme)) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .let { request -> + if (dimens[WIDTH] == 0 || dimens[HEIGHT] == 0) { + request.override(getDefaultWidth(), getDefaultHeight()) + } else { + request.override(dimens[WIDTH], dimens[HEIGHT]) + } + } + .fitCenter() + } + // endregion + +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt index b085171a0d..c40599630c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt @@ -1,11 +1,9 @@ package org.thoughtcrime.securesms.conversation.v2.messages import android.content.Context -import android.content.res.ColorStateList import android.graphics.drawable.Drawable import android.text.util.Linkify import android.util.AttributeSet -import android.util.Log import android.util.TypedValue import android.view.LayoutInflater import android.widget.LinearLayout @@ -17,11 +15,10 @@ import androidx.core.graphics.BlendModeColorFilterCompat import androidx.core.graphics.BlendModeCompat import kotlinx.android.synthetic.main.view_visible_message_content.view.* import network.loki.messenger.R -import org.session.libsession.messaging.utilities.UpdateMessageData -import org.session.libsession.messaging.utilities.UpdateMessageData.Companion.fromJSON import org.session.libsession.utilities.ThemeUtil import org.session.libsession.utilities.ViewUtil import org.session.libsession.utilities.recipients.Recipient +import org.thoughtcrime.securesms.components.v2.AlbumThumbnailView import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.database.model.MmsMessageRecord import org.thoughtcrime.securesms.loki.utilities.UiMode @@ -89,9 +86,10 @@ class VisibleMessageContentView : LinearLayout { documentView.bind(message, VisibleMessageContentView.getTextColor(context, message)) mainContainer.addView(documentView) } else if (message is MmsMessageRecord && message.slideDeck.asAttachments().isNotEmpty()) { - val dummyTextView = TextView(context) - dummyTextView.text = "asifuygaihsfo" - mainContainer.addView(dummyTextView) + val albumThumbnailView = AlbumThumbnailView(context) + mainContainer.addView(albumThumbnailView) + // isStart and isEnd of cluster needed for calculating the mask for full bubble image groups + albumThumbnailView.bind(glide, message, isStartOfMessageCluster, isEndOfMessageCluster) } else if (message.isOpenGroupInvitation) { val openGroupInvitationView = OpenGroupInvitationView(context) openGroupInvitationView.bind(message, VisibleMessageContentView.getTextColor(context, message)) diff --git a/app/src/main/res/layout/album_thumbnail_1.xml b/app/src/main/res/layout/album_thumbnail_1.xml new file mode 100644 index 0000000000..3946b674ec --- /dev/null +++ b/app/src/main/res/layout/album_thumbnail_1.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/album_thumbnail_2.xml b/app/src/main/res/layout/album_thumbnail_2.xml index 3fad6e678e..a1c132c272 100644 --- a/app/src/main/res/layout/album_thumbnail_2.xml +++ b/app/src/main/res/layout/album_thumbnail_2.xml @@ -7,13 +7,13 @@ android:layout_width="@dimen/album_total_width" android:layout_height="@dimen/album_2_total_height"> - - - - - - - - - - - - - - - - - - - Date: Thu, 24 Jun 2021 16:19:40 +1000 Subject: [PATCH 03/20] refactor: move new thumbnailview to utilities package --- .../components/v2/{ => utilities}/ThumbnailView.kt | 2 +- .../v2/AlbumThumbnailView.kt | 3 ++- .../v2/utilities}/ThumbnailDimensDelegate.kt | 2 +- app/src/main/res/layout/album_thumbnail_1.xml | 2 +- app/src/main/res/layout/album_thumbnail_2.xml | 4 ++-- app/src/main/res/layout/album_thumbnail_3.xml | 6 +++--- app/src/main/res/layout/album_thumbnail_4.xml | 8 ++++---- app/src/main/res/layout/album_thumbnail_5.xml | 10 +++++----- app/src/main/res/layout/album_thumbnail_many.xml | 10 +++++----- 9 files changed, 24 insertions(+), 23 deletions(-) rename app/src/main/java/org/thoughtcrime/securesms/components/v2/{ => utilities}/ThumbnailView.kt (99%) rename app/src/main/java/org/thoughtcrime/securesms/{components => conversation}/v2/AlbumThumbnailView.kt (97%) rename app/src/main/java/org/thoughtcrime/securesms/{components/v2 => conversation/v2/utilities}/ThumbnailDimensDelegate.kt (98%) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/v2/ThumbnailView.kt b/app/src/main/java/org/thoughtcrime/securesms/components/v2/utilities/ThumbnailView.kt similarity index 99% rename from app/src/main/java/org/thoughtcrime/securesms/components/v2/ThumbnailView.kt rename to app/src/main/java/org/thoughtcrime/securesms/components/v2/utilities/ThumbnailView.kt index 89af2faad9..69fe44ea2a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/v2/ThumbnailView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/v2/utilities/ThumbnailView.kt @@ -1,4 +1,4 @@ -package org.thoughtcrime.securesms.components.v2 +package org.thoughtcrime.securesms.conversation.v2.utilities import android.content.Context import android.graphics.Bitmap diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/v2/AlbumThumbnailView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt similarity index 97% rename from app/src/main/java/org/thoughtcrime/securesms/components/v2/AlbumThumbnailView.kt rename to app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt index 0031033f39..a00cf92673 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/v2/AlbumThumbnailView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt @@ -1,4 +1,4 @@ -package org.thoughtcrime.securesms.components.v2 +package org.thoughtcrime.securesms.conversation.v2 import android.content.Context import android.graphics.Canvas @@ -9,6 +9,7 @@ import android.widget.FrameLayout import kotlinx.android.synthetic.main.album_thumbnail_view.view.* import network.loki.messenger.R import org.thoughtcrime.securesms.components.CornerMask +import org.thoughtcrime.securesms.conversation.v2.utilities.ThumbnailView import org.thoughtcrime.securesms.database.model.MmsMessageRecord import org.thoughtcrime.securesms.mms.GlideRequests diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/v2/ThumbnailDimensDelegate.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ThumbnailDimensDelegate.kt similarity index 98% rename from app/src/main/java/org/thoughtcrime/securesms/components/v2/ThumbnailDimensDelegate.kt rename to app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ThumbnailDimensDelegate.kt index c4c7246d02..fb50d8de62 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/v2/ThumbnailDimensDelegate.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ThumbnailDimensDelegate.kt @@ -1,4 +1,4 @@ -package org.thoughtcrime.securesms.components.v2 +package org.thoughtcrime.securesms.conversation.v2.utilities class ThumbnailDimensDelegate { diff --git a/app/src/main/res/layout/album_thumbnail_1.xml b/app/src/main/res/layout/album_thumbnail_1.xml index 3946b674ec..c323b9e1d1 100644 --- a/app/src/main/res/layout/album_thumbnail_1.xml +++ b/app/src/main/res/layout/album_thumbnail_1.xml @@ -6,7 +6,7 @@ android:layout_width="@dimen/media_bubble_default_dimens" android:layout_height="@dimen/media_bubble_default_dimens"> - - - - - - - - - - - - - - - - - - - - Date: Fri, 25 Jun 2021 09:57:58 +1000 Subject: [PATCH 04/20] feat: adding text and shade, reverting other components to use old ThumbnailView --- .../components/AlbumThumbnailView.java | 4 +- .../components/ConversationItemThumbnail.java | 4 - .../components/OutlinedThumbnailView.java | 1 - .../conversation/v2/AlbumThumbnailView.kt | 23 +++--- .../conversation/v2/ConversationActivityV2.kt | 2 - .../v2/messages/VisibleMessageContentView.kt | 3 +- .../v2/utilities/KThumbnailView.kt} | 78 +++++++++++++++---- .../securesms/mms/AttachmentManager.java | 2 +- app/src/main/res/layout/album_thumbnail_1.xml | 10 +-- app/src/main/res/layout/album_thumbnail_2.xml | 4 +- app/src/main/res/layout/album_thumbnail_3.xml | 6 +- app/src/main/res/layout/album_thumbnail_4.xml | 8 +- app/src/main/res/layout/album_thumbnail_5.xml | 10 +-- .../main/res/layout/album_thumbnail_many.xml | 10 +-- .../main/res/layout/album_thumbnail_view.xml | 36 ++++++++- 15 files changed, 137 insertions(+), 64 deletions(-) rename app/src/main/java/org/thoughtcrime/securesms/{components/v2/utilities/ThumbnailView.kt => conversation/v2/utilities/KThumbnailView.kt} (73%) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/AlbumThumbnailView.java b/app/src/main/java/org/thoughtcrime/securesms/components/AlbumThumbnailView.java index 4529ef1fc4..8274e463fa 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/AlbumThumbnailView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/AlbumThumbnailView.java @@ -53,8 +53,8 @@ public class AlbumThumbnailView extends FrameLayout { private void initialize() { inflate(getContext(), R.layout.album_thumbnail_view, this); - albumCellContainer = findViewById(R.id.album_cell_container); - transferControls = new Stub<>(findViewById(R.id.album_transfer_controls_stub)); + albumCellContainer = findViewById(R.id.albumCellContainer); + transferControls = new Stub<>(findViewById(R.id.albumTransferControlsStub)); } public void setSlides(@NonNull GlideRequests glideRequests, @NonNull List slides, boolean showControls) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemThumbnail.java b/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemThumbnail.java index 53ef48b174..af9e766416 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemThumbnail.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemThumbnail.java @@ -65,10 +65,6 @@ public class ConversationItemThumbnail extends FrameLayout { if (attrs != null) { TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.ConversationItemThumbnail, 0, 0); - thumbnail.setBounds(typedArray.getDimensionPixelSize(R.styleable.ConversationItemThumbnail_conversationThumbnail_minWidth, 0), - typedArray.getDimensionPixelSize(R.styleable.ConversationItemThumbnail_conversationThumbnail_maxWidth, 0), - typedArray.getDimensionPixelSize(R.styleable.ConversationItemThumbnail_conversationThumbnail_minHeight, 0), - typedArray.getDimensionPixelSize(R.styleable.ConversationItemThumbnail_conversationThumbnail_maxHeight, 0)); typedArray.recycle(); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/OutlinedThumbnailView.java b/app/src/main/java/org/thoughtcrime/securesms/components/OutlinedThumbnailView.java index df28984ab0..71bf8a2804 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/OutlinedThumbnailView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/OutlinedThumbnailView.java @@ -29,7 +29,6 @@ public class OutlinedThumbnailView extends ThumbnailView { outliner = new Outliner(); outliner.setColor(ThemeUtil.getThemedColor(getContext(), R.attr.conversation_item_image_outline_color)); - setRadius(0); setWillNotDraw(false); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt index a00cf92673..ecd23e0b03 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt @@ -6,9 +6,11 @@ import android.util.AttributeSet import android.view.LayoutInflater import android.view.ViewGroup import android.widget.FrameLayout +import androidx.core.view.isVisible import kotlinx.android.synthetic.main.album_thumbnail_view.view.* import network.loki.messenger.R import org.thoughtcrime.securesms.components.CornerMask +import org.thoughtcrime.securesms.conversation.v2.utilities.KThumbnailView import org.thoughtcrime.securesms.conversation.v2.utilities.ThumbnailView import org.thoughtcrime.securesms.database.model.MmsMessageRecord import org.thoughtcrime.securesms.mms.GlideRequests @@ -20,13 +22,10 @@ class AlbumThumbnailView: FrameLayout { constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { initialize() } constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { initialize() } - private val albumCellContainer by lazy { album_cell_container } - private lateinit var cornerMask: CornerMask + private val cornerMask by lazy { CornerMask(this) } private fun initialize() { LayoutInflater.from(context).inflate(R.layout.album_thumbnail_view, this) - cornerMask = CornerMask(this) - cornerMask.setRadius(80) } override fun dispatchDraw(canvas: Canvas?) { @@ -50,8 +49,10 @@ class AlbumThumbnailView: FrameLayout { LayoutInflater.from(context).inflate(layoutRes(slides.size), albumCellContainer) // iterate slides.take(5).forEachIndexed { position, slide -> - getThumbnailView(position).setImageResource(glideRequests, slide, showControls = false, isPreview = false) + val imageResource = getThumbnailView(position).setImageResource(glideRequests, slide, showControls = false, isPreview = false) } + albumCellBodyParent.isVisible = message.body.isNotEmpty() + albumCellBodyText.text = message.body } // endregion @@ -66,12 +67,12 @@ class AlbumThumbnailView: FrameLayout { else -> R.layout.album_thumbnail_many// five or more } - fun getThumbnailView(position: Int): ThumbnailView = when (position) { - 0 -> albumCellContainer.findViewById(R.id.album_cell_container).findViewById(R.id.album_cell_1) - 1 -> albumCellContainer.findViewById(R.id.album_cell_container).findViewById(R.id.album_cell_2) - 2 -> albumCellContainer.findViewById(R.id.album_cell_container).findViewById(R.id.album_cell_3) - 3 -> albumCellContainer.findViewById(R.id.album_cell_container).findViewById(R.id.album_cell_4) - 4 -> albumCellContainer.findViewById(R.id.album_cell_container).findViewById(R.id.album_cell_5) + fun getThumbnailView(position: Int): KThumbnailView = when (position) { + 0 -> albumCellContainer.findViewById(R.id.albumCellContainer).findViewById(R.id.album_cell_1) + 1 -> albumCellContainer.findViewById(R.id.albumCellContainer).findViewById(R.id.album_cell_2) + 2 -> albumCellContainer.findViewById(R.id.albumCellContainer).findViewById(R.id.album_cell_3) + 3 -> albumCellContainer.findViewById(R.id.albumCellContainer).findViewById(R.id.album_cell_4) + 4 -> albumCellContainer.findViewById(R.id.albumCellContainer).findViewById(R.id.album_cell_5) else -> throw Exception("Can't get thumbnail view for non-existent thumbnail at position: $position") } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index 8f57a7a97e..f2368c8fc6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -7,7 +7,6 @@ import android.database.Cursor import android.graphics.Rect import android.graphics.Typeface import android.os.Bundle -import android.util.Log import android.util.TypedValue import android.view.* import android.widget.RelativeLayout @@ -31,7 +30,6 @@ import org.session.libsession.messaging.mentions.MentionsManager import org.session.libsession.messaging.open_groups.OpenGroupAPIV2 import org.session.libsession.utilities.TextSecurePreferences import org.thoughtcrime.securesms.ApplicationContext -import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.conversation.v2.dialogs.* import org.thoughtcrime.securesms.conversation.v2.input_bar.InputBarButton diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt index c949163a7c..6cd959c5ed 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt @@ -19,7 +19,7 @@ import network.loki.messenger.R import org.session.libsession.utilities.ThemeUtil import org.session.libsession.utilities.ViewUtil import org.session.libsession.utilities.recipients.Recipient -import org.thoughtcrime.securesms.components.v2.AlbumThumbnailView +import org.thoughtcrime.securesms.conversation.v2.AlbumThumbnailView import org.thoughtcrime.securesms.components.emoji.EmojiTextView import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.database.model.MmsMessageRecord @@ -87,6 +87,7 @@ class VisibleMessageContentView : LinearLayout { val albumThumbnailView = AlbumThumbnailView(context) mainContainer.addView(albumThumbnailView) // isStart and isEnd of cluster needed for calculating the mask for full bubble image groups + // bind after add view because views are inflated and calculated during bind albumThumbnailView.bind(glide, message, isStartOfMessageCluster, isEndOfMessageCluster) } else if (message.isOpenGroupInvitation) { val openGroupInvitationView = OpenGroupInvitationView(context) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/v2/utilities/ThumbnailView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/KThumbnailView.kt similarity index 73% rename from app/src/main/java/org/thoughtcrime/securesms/components/v2/utilities/ThumbnailView.kt rename to app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/KThumbnailView.kt index 69fe44ea2a..ceec1ae8f8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/v2/utilities/ThumbnailView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/KThumbnailView.kt @@ -3,11 +3,14 @@ package org.thoughtcrime.securesms.conversation.v2.utilities import android.content.Context import android.graphics.Bitmap import android.graphics.drawable.Drawable +import android.net.Uri import android.util.AttributeSet import android.widget.FrameLayout import android.widget.ProgressBar import androidx.core.view.isVisible import com.bumptech.glide.load.engine.DiskCacheStrategy +import com.bumptech.glide.load.resource.bitmap.CenterCrop +import com.bumptech.glide.load.resource.bitmap.RoundedCorners import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions import com.bumptech.glide.request.RequestOptions import kotlinx.android.synthetic.main.thumbnail_view.view.* @@ -15,14 +18,15 @@ import network.loki.messenger.R import org.session.libsession.messaging.sending_receiving.attachments.AttachmentTransferProgress import org.session.libsession.utilities.Util.equals import org.session.libsession.utilities.ViewUtil +import org.session.libsignal.utilities.ListenableFuture +import org.session.libsignal.utilities.SettableFuture +import org.thoughtcrime.securesms.components.GlideBitmapListeningTarget +import org.thoughtcrime.securesms.components.GlideDrawableListeningTarget import org.thoughtcrime.securesms.components.TransferControlView +import org.thoughtcrime.securesms.mms.* import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri -import org.thoughtcrime.securesms.mms.GlideRequest -import org.thoughtcrime.securesms.mms.GlideRequests -import org.thoughtcrime.securesms.mms.Slide -import org.thoughtcrime.securesms.mms.SlideClickListener -class ThumbnailView: FrameLayout { +open class KThumbnailView: FrameLayout { companion object { private const val WIDTH = 0 @@ -45,16 +49,19 @@ class ThumbnailView: FrameLayout { var thumbnailClickListener: SlideClickListener? = null private var slide: Slide? = null + private var radius: Int = 0 private fun initialize(attrs: AttributeSet?) { inflate(context, R.layout.thumbnail_view, this) if (attrs != null) { val typedArray = context.theme.obtainStyledAttributes(attrs, R.styleable.ThumbnailView, 0, 0) - dimensDelegate.setBounds(typedArray.getDimensionPixelSize(R.styleable.ConversationItemThumbnail_conversationThumbnail_minWidth, 0), - typedArray.getDimensionPixelSize(R.styleable.ConversationItemThumbnail_conversationThumbnail_minHeight, 0), - typedArray.getDimensionPixelSize(R.styleable.ConversationItemThumbnail_conversationThumbnail_maxWidth, 0), - typedArray.getDimensionPixelSize(R.styleable.ConversationItemThumbnail_conversationThumbnail_maxHeight, 0)) + dimensDelegate.setBounds(typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_minWidth, 0), + typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_minHeight, 0), + typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_maxWidth, 0), + typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_maxHeight, 0)) + + radius = typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_thumbnail_radius, 0) typedArray.recycle() } @@ -82,13 +89,13 @@ class ThumbnailView: FrameLayout { // endregion // region Interaction - fun setImageResource(glide: GlideRequests, slide: Slide, showControls: Boolean, isPreview: Boolean) { + fun setImageResource(glide: GlideRequests, slide: Slide, showControls: Boolean, isPreview: Boolean): ListenableFuture { return setImageResource(glide, slide, showControls, isPreview, 0, 0) } fun setImageResource(glide: GlideRequests, slide: Slide, showControls: Boolean, isPreview: Boolean, - naturalWidth: Int, naturalHeight: Int) { + naturalWidth: Int, naturalHeight: Int): ListenableFuture { val currentSlide = this.slide @@ -104,7 +111,7 @@ class ThumbnailView: FrameLayout { if (equals(currentSlide, slide)) { // don't re-load slide - return + return SettableFuture(false) } @@ -121,17 +128,21 @@ class ThumbnailView: FrameLayout { dimensDelegate.setDimens(naturalWidth, naturalHeight) invalidate() + val result = SettableFuture() + when { slide.thumbnailUri != null -> { - buildThumbnailGlideRequest(glide, slide).into(image) + buildThumbnailGlideRequest(glide, slide).into(GlideDrawableListeningTarget(image, result)) } slide.hasPlaceholder() -> { - buildPlaceholderGlideRequest(glide, slide).into(image) + buildPlaceholderGlideRequest(glide, slide).into(GlideBitmapListeningTarget(image, result)) } else -> { glide.clear(image) + result.set(false) } } + return result } fun buildThumbnailGlideRequest(glide: GlideRequests, slide: Slide): GlideRequest { @@ -169,6 +180,45 @@ class ThumbnailView: FrameLayout { } .fitCenter() } + + open fun clear(glideRequests: GlideRequests) { + glideRequests.clear(image) + transferControls.clear() + slide = null + } + + fun showProgressSpinner() { + transferControls.showProgressSpinner() + } + + fun setImageResource(glideRequests: GlideRequests, uri: Uri): ListenableFuture { + val future = SettableFuture() + + transferControls.isVisible = false + + var request: GlideRequest = glideRequests.load(DecryptableUri(uri)) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .transition(DrawableTransitionOptions.withCrossFade()) + + request = if (radius > 0) { + request.transforms(CenterCrop(), RoundedCorners(radius)) + } else { + request.transforms(CenterCrop()) + } + + request.into(GlideDrawableListeningTarget(image, future)) + + return future + } + + fun showDownloadText(showDownloadText: Boolean) { + transferControls.setShowDownloadText(showDownloadText) + } + + fun setDownloadClickListener(listener: SlidesClickedListener) { + + } + // endregion } \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/AttachmentManager.java b/app/src/main/java/org/thoughtcrime/securesms/mms/AttachmentManager.java index e2b7980ab6..153f07c168 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/AttachmentManager.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/AttachmentManager.java @@ -83,7 +83,7 @@ public class AttachmentManager { private final @NonNull AttachmentListener attachmentListener; private RemovableEditableMediaView removableMediaView; - private ThumbnailView thumbnail; + private ThumbnailView thumbnail; private MessageAudioView audioView; private DocumentView documentView; diff --git a/app/src/main/res/layout/album_thumbnail_1.xml b/app/src/main/res/layout/album_thumbnail_1.xml index c323b9e1d1..cf0f5d4892 100644 --- a/app/src/main/res/layout/album_thumbnail_1.xml +++ b/app/src/main/res/layout/album_thumbnail_1.xml @@ -6,14 +6,14 @@ android:layout_width="@dimen/media_bubble_default_dimens" android:layout_height="@dimen/media_bubble_default_dimens"> - \ No newline at end of file diff --git a/app/src/main/res/layout/album_thumbnail_2.xml b/app/src/main/res/layout/album_thumbnail_2.xml index 6ce179ea6a..9ac5866113 100644 --- a/app/src/main/res/layout/album_thumbnail_2.xml +++ b/app/src/main/res/layout/album_thumbnail_2.xml @@ -7,13 +7,13 @@ android:layout_width="@dimen/album_total_width" android:layout_height="@dimen/album_2_total_height"> - - - - - - - - - - - - - - - - - - - + /> + + + + + + + + \ No newline at end of file From 21835800ff208e253ccb3d17a47dc47028e922a1 Mon Sep 17 00:00:00 2001 From: jubb Date: Fri, 25 Jun 2021 12:18:52 +1000 Subject: [PATCH 05/20] feat: wiring up click listeners in AlbumThumbnailView.kt --- .../components/AlbumThumbnailView.java | 18 +++---- .../conversation/v2/AlbumThumbnailView.kt | 53 +++++++++++++++++-- .../v2/messages/VisibleMessageContentView.kt | 18 ++++++- .../v2/utilities/KThumbnailView.kt | 19 +++++-- .../main/res/layout/album_thumbnail_view.xml | 38 ++++++++++--- 5 files changed, 120 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/AlbumThumbnailView.java b/app/src/main/java/org/thoughtcrime/securesms/components/AlbumThumbnailView.java index 8274e463fa..df88367710 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/AlbumThumbnailView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/AlbumThumbnailView.java @@ -1,27 +1,27 @@ package org.thoughtcrime.securesms.components; import android.content.Context; -import androidx.annotation.ColorInt; -import androidx.annotation.IdRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import android.util.AttributeSet; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.TextView; -import network.loki.messenger.R; +import androidx.annotation.ColorInt; +import androidx.annotation.IdRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; -import org.thoughtcrime.securesms.conversation.v2.utilities.ThumbnailView; +import org.session.libsession.utilities.Stub; +import org.thoughtcrime.securesms.conversation.v2.utilities.KThumbnailView; import org.thoughtcrime.securesms.mms.GlideRequests; - import org.thoughtcrime.securesms.mms.Slide; import org.thoughtcrime.securesms.mms.SlideClickListener; import org.thoughtcrime.securesms.mms.SlidesClickedListener; -import org.session.libsession.utilities.Stub; import java.util.List; +import network.loki.messenger.R; + public class AlbumThumbnailView extends FrameLayout { private @Nullable SlideClickListener thumbnailClickListener; @@ -149,7 +149,7 @@ public class AlbumThumbnailView extends FrameLayout { } private void setSlide(@NonNull GlideRequests glideRequests, @NonNull Slide slide, @IdRes int id) { - ThumbnailView cell = findViewById(id); + KThumbnailView cell = findViewById(id); cell.setImageResource(glideRequests, slide, false, false); cell.setThumbnailClickListener(defaultThumbnailClickListener); cell.setOnLongClickListener(defaultLongClickListener); diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt index ecd23e0b03..013f21fe59 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt @@ -4,6 +4,7 @@ import android.content.Context import android.graphics.Canvas import android.util.AttributeSet import android.view.LayoutInflater +import android.view.View import android.view.ViewGroup import android.widget.FrameLayout import androidx.core.view.isVisible @@ -11,21 +12,27 @@ import kotlinx.android.synthetic.main.album_thumbnail_view.view.* import network.loki.messenger.R import org.thoughtcrime.securesms.components.CornerMask import org.thoughtcrime.securesms.conversation.v2.utilities.KThumbnailView -import org.thoughtcrime.securesms.conversation.v2.utilities.ThumbnailView import org.thoughtcrime.securesms.database.model.MmsMessageRecord import org.thoughtcrime.securesms.mms.GlideRequests +import org.thoughtcrime.securesms.mms.Slide +import org.thoughtcrime.securesms.mms.SlideClickListener +import org.thoughtcrime.securesms.mms.SlidesClickedListener -class AlbumThumbnailView: FrameLayout { +class AlbumThumbnailView: FrameLayout, SlideClickListener, SlidesClickedListener, View.OnClickListener { // region Lifecycle constructor(context: Context) : super(context) { initialize() } constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { initialize() } constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { initialize() } + private var slideClickListener: ((Slide) -> Unit)? = null + private var downloadClickListener: ((Slide) -> Unit)? = null + private var readMoreListener: (() -> Unit)? = null private val cornerMask by lazy { CornerMask(this) } private fun initialize() { LayoutInflater.from(context).inflate(R.layout.album_thumbnail_view, this) + albumCellBodyTextReadMore.setOnClickListener(this) } override fun dispatchDraw(canvas: Canvas?) { @@ -37,7 +44,34 @@ class AlbumThumbnailView: FrameLayout { // region Interaction - fun bind(glideRequests: GlideRequests, message: MmsMessageRecord, isStart: Boolean, isEnd: Boolean) { + override fun onClick(v: View?) { + // clicked the view or one of its children + if (v === albumCellBodyTextReadMore) { + readMoreListener?.invoke() + } + } + + override fun onClick(v: View?, slide: Slide?) { + // slide thumbnail clicked + if (slide==null) return + slideClickListener?.invoke(slide) + } + + override fun onClick(v: View?, slides: MutableList?) { + // slide download clicked + if (slides.isNullOrEmpty()) return + slides.firstOrNull().let { slide -> + if (slide == null) return@let + downloadClickListener?.invoke(slide) + } + } + + fun bind(glideRequests: GlideRequests, message: MmsMessageRecord, + clickListener: (Slide)->Unit, downloadClickListener: (Slide)->Unit, readMoreListener: ()->Unit, + isStart: Boolean, isEnd: Boolean) { + this.slideClickListener = clickListener + this.downloadClickListener = downloadClickListener + this.readMoreListener = readMoreListener // TODO: optimize for same size val slides = message.slideDeck.thumbnailSlides if (slides.isEmpty()) { @@ -49,10 +83,21 @@ class AlbumThumbnailView: FrameLayout { LayoutInflater.from(context).inflate(layoutRes(slides.size), albumCellContainer) // iterate slides.take(5).forEachIndexed { position, slide -> - val imageResource = getThumbnailView(position).setImageResource(glideRequests, slide, showControls = false, isPreview = false) + val thumbnailView = getThumbnailView(position) + thumbnailView.thumbnailClickListener = this + thumbnailView.setImageResource(glideRequests, slide, showControls = false, isPreview = false) + thumbnailView.setDownloadClickListener(this) } albumCellBodyParent.isVisible = message.body.isNotEmpty() albumCellBodyText.text = message.body + post { + // post to await layout of text + albumCellBodyText.layout?.let { layout -> + val maxEllipsis = (0 until layout.lineCount).maxByOrNull { lineNum -> layout.getEllipsisCount(lineNum) } ?: 0 + // show read more text if at least one line is ellipsized + albumCellBodyTextReadMore.isVisible = maxEllipsis > 0 + } + } } // endregion diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt index 6cd959c5ed..3ab7ad065f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt @@ -4,6 +4,7 @@ import android.content.Context import android.graphics.drawable.Drawable import android.text.util.Linkify import android.util.AttributeSet +import android.util.Log import android.util.TypedValue import android.view.LayoutInflater import android.widget.LinearLayout @@ -88,7 +89,22 @@ class VisibleMessageContentView : LinearLayout { mainContainer.addView(albumThumbnailView) // isStart and isEnd of cluster needed for calculating the mask for full bubble image groups // bind after add view because views are inflated and calculated during bind - albumThumbnailView.bind(glide, message, isStartOfMessageCluster, isEndOfMessageCluster) + albumThumbnailView.bind( + glideRequests = glide, + message = message, + isStart = isStartOfMessageCluster, + isEnd = isEndOfMessageCluster, + clickListener = { slide -> + Log.d("Loki-UI","clicked to display the slide $slide") + }, + downloadClickListener = { slide -> + // trigger download of content? + Log.d("Loki-UI","clicked to download the slide $slide") + }, + readMoreListener = { + Log.d("Loki-UI", "clicked to read more the message $message") + } + ) } else if (message.isOpenGroupInvitation) { val openGroupInvitationView = OpenGroupInvitationView(context) openGroupInvitationView.bind(message, VisibleMessageContentView.getTextColor(context, message)) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/KThumbnailView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/KThumbnailView.kt index ceec1ae8f8..e0320c234e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/KThumbnailView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/KThumbnailView.kt @@ -5,6 +5,7 @@ import android.graphics.Bitmap import android.graphics.drawable.Drawable import android.net.Uri import android.util.AttributeSet +import android.view.View import android.widget.FrameLayout import android.widget.ProgressBar import androidx.core.view.isVisible @@ -26,7 +27,7 @@ import org.thoughtcrime.securesms.components.TransferControlView import org.thoughtcrime.securesms.mms.* import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri -open class KThumbnailView: FrameLayout { +open class KThumbnailView: FrameLayout, View.OnClickListener { companion object { private const val WIDTH = 0 @@ -65,9 +66,9 @@ open class KThumbnailView: FrameLayout { typedArray.recycle() } + setOnClickListener(this) } - // region Lifecycle override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { val adjustedDimens = dimensDelegate.resourceSize() if (adjustedDimens[WIDTH] == 0 && adjustedDimens[HEIGHT] == 0) { @@ -85,10 +86,16 @@ open class KThumbnailView: FrameLayout { private fun getDefaultWidth() = maxOf(layoutParams?.width ?: 0, 0) private fun getDefaultHeight() = maxOf(layoutParams?.height ?: 0, 0) - // endregion // region Interaction + + override fun onClick(v: View?) { + if (v === this) { + thumbnailClickListener?.onClick(v, slide) + } + } + fun setImageResource(glide: GlideRequests, slide: Slide, showControls: Boolean, isPreview: Boolean): ListenableFuture { return setImageResource(glide, slide, showControls, isPreview, 0, 0) } @@ -216,7 +223,11 @@ open class KThumbnailView: FrameLayout { } fun setDownloadClickListener(listener: SlidesClickedListener) { - + transferControls.setDownloadClickListener { + slide?.let { slide -> + listener.onClick(it, listOf(slide)) + } + } } // endregion diff --git a/app/src/main/res/layout/album_thumbnail_view.xml b/app/src/main/res/layout/album_thumbnail_view.xml index eb262f7e2d..5920486972 100644 --- a/app/src/main/res/layout/album_thumbnail_view.xml +++ b/app/src/main/res/layout/album_thumbnail_view.xml @@ -1,6 +1,7 @@ - @@ -17,7 +18,8 @@ android:layout_gravity="center" android:layout="@layout/transfer_controls_stub" /> - + android:layout_height="0dp" + app:layout_constraintTop_toTopOf="@+id/albumCellBodyTextParent" + app:layout_constraintBottom_toBottomOf="parent" + /> - + + \ No newline at end of file From ce098fe91815dbcf2d8a5172987721b617ca7d8b Mon Sep 17 00:00:00 2001 From: jubb Date: Fri, 25 Jun 2021 14:43:22 +1000 Subject: [PATCH 06/20] fix: change the content click to be hit-rect based to determine child object intersection for views with multiple content objects --- .../conversation/v2/AlbumThumbnailView.kt | 78 ++++++++++--------- .../conversation/v2/ConversationActivityV2.kt | 8 +- .../conversation/v2/ConversationAdapter.kt | 5 +- .../v2/messages/VisibleMessageContentView.kt | 25 +++--- .../v2/messages/VisibleMessageView.kt | 8 +- .../v2/utilities/KThumbnailView.kt | 10 +-- 6 files changed, 66 insertions(+), 68 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt index 013f21fe59..d8136c9f12 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt @@ -2,11 +2,12 @@ package org.thoughtcrime.securesms.conversation.v2 import android.content.Context import android.graphics.Canvas +import android.graphics.Rect import android.util.AttributeSet import android.view.LayoutInflater -import android.view.View import android.view.ViewGroup import android.widget.FrameLayout +import androidx.core.view.children import androidx.core.view.isVisible import kotlinx.android.synthetic.main.album_thumbnail_view.view.* import network.loki.messenger.R @@ -15,24 +16,33 @@ import org.thoughtcrime.securesms.conversation.v2.utilities.KThumbnailView import org.thoughtcrime.securesms.database.model.MmsMessageRecord import org.thoughtcrime.securesms.mms.GlideRequests import org.thoughtcrime.securesms.mms.Slide -import org.thoughtcrime.securesms.mms.SlideClickListener -import org.thoughtcrime.securesms.mms.SlidesClickedListener -class AlbumThumbnailView: FrameLayout, SlideClickListener, SlidesClickedListener, View.OnClickListener { +class AlbumThumbnailView : FrameLayout { // region Lifecycle - constructor(context: Context) : super(context) { initialize() } - constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { initialize() } - constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { initialize() } + constructor(context: Context) : super(context) { + initialize() + } + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { + initialize() + } + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { + initialize() + } - private var slideClickListener: ((Slide) -> Unit)? = null - private var downloadClickListener: ((Slide) -> Unit)? = null - private var readMoreListener: (() -> Unit)? = null private val cornerMask by lazy { CornerMask(this) } + private var slides: List = listOf() + + sealed class Hit { + object ReadMoreHit : Hit() + data class SlideHit(val slide: Slide) : Hit() + data class DownloadHit(val slide: Slide) : Hit() + } private fun initialize() { LayoutInflater.from(context).inflate(R.layout.album_thumbnail_view, this) - albumCellBodyTextReadMore.setOnClickListener(this) } override fun dispatchDraw(canvas: Canvas?) { @@ -44,36 +54,31 @@ class AlbumThumbnailView: FrameLayout, SlideClickListener, SlidesClickedListener // region Interaction - override fun onClick(v: View?) { - // clicked the view or one of its children - if (v === albumCellBodyTextReadMore) { - readMoreListener?.invoke() + fun calculateHitObject(hitRect: Rect): Hit? { + // Z-check in specific order + val testRect = Rect() + // test "Read More" + albumCellBodyTextReadMore.getHitRect(testRect) + if (Rect.intersects(hitRect, testRect)) { + return Hit.ReadMoreHit } - } - - override fun onClick(v: View?, slide: Slide?) { - // slide thumbnail clicked - if (slide==null) return - slideClickListener?.invoke(slide) - } - - override fun onClick(v: View?, slides: MutableList?) { - // slide download clicked - if (slides.isNullOrEmpty()) return - slides.firstOrNull().let { slide -> - if (slide == null) return@let - downloadClickListener?.invoke(slide) + // test each album child + albumCellContainer.findViewById(R.id.album_thumbnail_root)?.children?.forEachIndexed { index, child -> + child.getHitRect(testRect) + if (Rect.intersects(hitRect, testRect)) { + // hit intersects with this particular child + slides.getOrNull(index)?.let { slide -> + return Hit.SlideHit(slide) + } + } } + return null } fun bind(glideRequests: GlideRequests, message: MmsMessageRecord, - clickListener: (Slide)->Unit, downloadClickListener: (Slide)->Unit, readMoreListener: ()->Unit, isStart: Boolean, isEnd: Boolean) { - this.slideClickListener = clickListener - this.downloadClickListener = downloadClickListener - this.readMoreListener = readMoreListener // TODO: optimize for same size - val slides = message.slideDeck.thumbnailSlides + slides = message.slideDeck.thumbnailSlides if (slides.isEmpty()) { // this should never be encountered because it's checked by parent return @@ -84,16 +89,15 @@ class AlbumThumbnailView: FrameLayout, SlideClickListener, SlidesClickedListener // iterate slides.take(5).forEachIndexed { position, slide -> val thumbnailView = getThumbnailView(position) - thumbnailView.thumbnailClickListener = this thumbnailView.setImageResource(glideRequests, slide, showControls = false, isPreview = false) - thumbnailView.setDownloadClickListener(this) } albumCellBodyParent.isVisible = message.body.isNotEmpty() albumCellBodyText.text = message.body post { // post to await layout of text albumCellBodyText.layout?.let { layout -> - val maxEllipsis = (0 until layout.lineCount).maxByOrNull { lineNum -> layout.getEllipsisCount(lineNum) } ?: 0 + val maxEllipsis = (0 until layout.lineCount).maxByOrNull { lineNum -> layout.getEllipsisCount(lineNum) } + ?: 0 // show read more text if at least one line is ellipsized albumCellBodyTextReadMore.isVisible = maxEllipsis > 0 } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index 6bebd88c2b..a424a5d5d9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -76,8 +76,8 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe val adapter = ConversationAdapter( this, cursor, - onItemPress = { message, position, view -> - handlePress(message, position, view) + onItemPress = { message, position, view, rect -> + handlePress(message, position, view, rect) }, onItemSwipeToReply = { message, position -> handleSwipeToReply(message, position) @@ -452,7 +452,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe } // `position` is the adapter position; not the visual position - private fun handlePress(message: MessageRecord, position: Int, view: VisibleMessageView) { + private fun handlePress(message: MessageRecord, position: Int, view: VisibleMessageView, hitRect: Rect) { val actionMode = this.actionMode if (actionMode != null) { adapter.toggleSelection(message, position) @@ -467,7 +467,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe // We have to use onContentClick (rather than a click listener directly on // the view) so as to not interfere with all the other gestures. Do not add // onClickListeners directly to message content views. - view.onContentClick() + view.onContentClick(hitRect) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapter.kt index 63306b0c4f..2a409ab0e8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapter.kt @@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.conversation.v2 import android.content.Context import android.database.Cursor +import android.graphics.Rect import android.view.ViewGroup import androidx.core.view.isVisible import androidx.recyclerview.widget.RecyclerView.ViewHolder @@ -13,7 +14,7 @@ import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.mms.GlideRequests -class ConversationAdapter(context: Context, cursor: Cursor, private val onItemPress: (MessageRecord, Int, VisibleMessageView) -> Unit, +class ConversationAdapter(context: Context, cursor: Cursor, private val onItemPress: (MessageRecord, Int, VisibleMessageView, Rect) -> Unit, private val onItemSwipeToReply: (MessageRecord, Int) -> Unit, private val onItemLongPress: (MessageRecord, Int) -> Unit, private val glide: GlideRequests) : CursorRecyclerViewAdapter(context, cursor) { @@ -68,7 +69,7 @@ class ConversationAdapter(context: Context, cursor: Cursor, private val onItemPr view.messageTimestampTextView.isVisible = isSelected val position = viewHolder.adapterPosition view.bind(message, getMessageBefore(position, cursor), getMessageAfter(position, cursor), glide) - view.onPress = { onItemPress(message, viewHolder.adapterPosition, view) } + view.onPress = { x, y -> onItemPress(message, viewHolder.adapterPosition, view, Rect(x,y,x,y)) } view.onSwipeToReply = { onItemSwipeToReply(message, viewHolder.adapterPosition) } view.onLongPress = { onItemLongPress(message, viewHolder.adapterPosition) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt index 3ab7ad065f..b32be1a2e3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt @@ -1,6 +1,7 @@ package org.thoughtcrime.securesms.conversation.v2.messages import android.content.Context +import android.graphics.Rect import android.graphics.drawable.Drawable import android.text.util.Linkify import android.util.AttributeSet @@ -30,7 +31,7 @@ import org.thoughtcrime.securesms.mms.GlideRequests import kotlin.math.roundToInt class VisibleMessageContentView : LinearLayout { - var onContentClick: (() -> Unit)? = null + var onContentClick: ((Rect) -> Unit)? = null // region Lifecycle constructor(context: Context) : super(context) { initialize() } @@ -93,18 +94,18 @@ class VisibleMessageContentView : LinearLayout { glideRequests = glide, message = message, isStart = isStartOfMessageCluster, - isEnd = isEndOfMessageCluster, - clickListener = { slide -> - Log.d("Loki-UI","clicked to display the slide $slide") - }, - downloadClickListener = { slide -> - // trigger download of content? - Log.d("Loki-UI","clicked to download the slide $slide") - }, - readMoreListener = { - Log.d("Loki-UI", "clicked to read more the message $message") - } + isEnd = isEndOfMessageCluster ) + onContentClick = { + when (val hitObject = albumThumbnailView.calculateHitObject(it)) { + is AlbumThumbnailView.Hit.SlideHit -> Log.d("Loki-UI", "clicked display slide ${hitObject.slide}")// open the slide preview + is AlbumThumbnailView.Hit.DownloadHit -> Log.d("Loki-UI", "clicked display download") + AlbumThumbnailView.Hit.ReadMoreHit -> Log.d("Loki-UI", "clicked the read more display") + else -> { + Log.d("Loki-UI", "DIDN'T click anything important") + } + } + } } else if (message.isOpenGroupInvitation) { val openGroupInvitationView = OpenGroupInvitationView(context) openGroupInvitationView.bind(message, VisibleMessageContentView.getTextColor(context, message)) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt index 3d665c5cbd..4e4add768c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt @@ -50,7 +50,7 @@ class VisibleMessageView : LinearLayout { private var onDownTimestamp = 0L var snIsSelected = false set(value) { field = value; handleIsSelectedChanged()} - var onPress: (() -> Unit)? = null + var onPress: ((x: Int, y: Int) -> Unit)? = null var onSwipeToReply: (() -> Unit)? = null var onLongPress: (() -> Unit)? = null @@ -272,7 +272,7 @@ class VisibleMessageView : LinearLayout { onSwipeToReply?.invoke() } else if ((Date().time - onDownTimestamp) < VisibleMessageView.longPressDurationThreshold) { longPressCallback?.let { gestureHandler.removeCallbacks(it) } - onPress?.invoke() + onPress?.invoke(event.x.toInt(), event.y.toInt()) } resetPosition() } @@ -297,8 +297,8 @@ class VisibleMessageView : LinearLayout { onLongPress?.invoke() } - fun onContentClick() { - messageContentView.onContentClick?.invoke() + fun onContentClick(hitRect: Rect) { + messageContentView.onContentClick?.invoke(hitRect) } // endregion } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/KThumbnailView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/KThumbnailView.kt index e0320c234e..df6f4d600d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/KThumbnailView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/KThumbnailView.kt @@ -27,7 +27,7 @@ import org.thoughtcrime.securesms.components.TransferControlView import org.thoughtcrime.securesms.mms.* import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri -open class KThumbnailView: FrameLayout, View.OnClickListener { +open class KThumbnailView: FrameLayout { companion object { private const val WIDTH = 0 @@ -66,7 +66,6 @@ open class KThumbnailView: FrameLayout, View.OnClickListener { typedArray.recycle() } - setOnClickListener(this) } override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { @@ -89,13 +88,6 @@ open class KThumbnailView: FrameLayout, View.OnClickListener { // endregion // region Interaction - - override fun onClick(v: View?) { - if (v === this) { - thumbnailClickListener?.onClick(v, slide) - } - } - fun setImageResource(glide: GlideRequests, slide: Slide, showControls: Boolean, isPreview: Boolean): ListenableFuture { return setImageResource(glide, slide, showControls, isPreview, 0, 0) } From bb0075423786efb836a55d144bad9aad829d02a1 Mon Sep 17 00:00:00 2001 From: jubb Date: Fri, 25 Jun 2021 14:47:02 +1000 Subject: [PATCH 07/20] refactor: change line to have small spacing margin --- app/src/main/res/layout/album_thumbnail_view.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/layout/album_thumbnail_view.xml b/app/src/main/res/layout/album_thumbnail_view.xml index 5920486972..e9ca4e20e3 100644 --- a/app/src/main/res/layout/album_thumbnail_view.xml +++ b/app/src/main/res/layout/album_thumbnail_view.xml @@ -44,6 +44,7 @@ android:layout_marginStart="@dimen/small_spacing" android:layout_width="@dimen/accent_line_thickness" android:layout_height="match_parent" + android:layout_marginVertical="@dimen/small_spacing" android:background="@color/accent"/> Date: Fri, 25 Jun 2021 16:30:23 +1000 Subject: [PATCH 08/20] refactor: use activity dispatcher --- .../conversation/v2/AlbumThumbnailView.kt | 43 +++++++++++++------ .../conversation/v2/ConversationActivityV2.kt | 28 +++++++++--- .../conversation/v2/ConversationAdapter.kt | 2 +- .../v2/messages/VisibleMessageContentView.kt | 13 +----- .../v2/messages/VisibleMessageView.kt | 8 ++-- .../loki/utilities/ActivityUtilities.kt | 13 ++++++ 6 files changed, 72 insertions(+), 35 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt index d8136c9f12..56da118f3b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt @@ -1,6 +1,7 @@ package org.thoughtcrime.securesms.conversation.v2 import android.content.Context +import android.content.Intent import android.graphics.Canvas import android.graphics.Rect import android.util.AttributeSet @@ -11,9 +12,12 @@ import androidx.core.view.children import androidx.core.view.isVisible import kotlinx.android.synthetic.main.album_thumbnail_view.view.* import network.loki.messenger.R +import org.thoughtcrime.securesms.MediaPreviewActivity import org.thoughtcrime.securesms.components.CornerMask import org.thoughtcrime.securesms.conversation.v2.utilities.KThumbnailView import org.thoughtcrime.securesms.database.model.MmsMessageRecord +import org.thoughtcrime.securesms.loki.utilities.ActivityDispatcher +import org.thoughtcrime.securesms.longmessage.LongMessageActivity import org.thoughtcrime.securesms.mms.GlideRequests import org.thoughtcrime.securesms.mms.Slide @@ -35,12 +39,6 @@ class AlbumThumbnailView : FrameLayout { private val cornerMask by lazy { CornerMask(this) } private var slides: List = listOf() - sealed class Hit { - object ReadMoreHit : Hit() - data class SlideHit(val slide: Slide) : Hit() - data class DownloadHit(val slide: Slide) : Hit() - } - private fun initialize() { LayoutInflater.from(context).inflate(R.layout.album_thumbnail_view, this) } @@ -54,25 +52,42 @@ class AlbumThumbnailView : FrameLayout { // region Interaction - fun calculateHitObject(hitRect: Rect): Hit? { + fun calculateHitObject(rawRect: Rect, mms: MmsMessageRecord) { // Z-check in specific order val testRect = Rect() // test "Read More" - albumCellBodyTextReadMore.getHitRect(testRect) - if (Rect.intersects(hitRect, testRect)) { - return Hit.ReadMoreHit + albumCellBodyTextReadMore.getGlobalVisibleRect(testRect) + if (Rect.intersects(rawRect, testRect)) { + // dispatch to activity view + ActivityDispatcher.get(context)?.dispatchIntent { context -> + LongMessageActivity.getIntent(context, mms.recipient.address, mms.getId(), true) + } + return } // test each album child albumCellContainer.findViewById(R.id.album_thumbnail_root)?.children?.forEachIndexed { index, child -> - child.getHitRect(testRect) - if (Rect.intersects(hitRect, testRect)) { + child.getGlobalVisibleRect(testRect) + if (Rect.intersects(rawRect, testRect)) { // hit intersects with this particular child slides.getOrNull(index)?.let { slide -> - return Hit.SlideHit(slide) + // dispatch to view image + if (MediaPreviewActivity.isContentTypeSupported(slide.contentType) && slide.uri != null) { + ActivityDispatcher.get(context)?.dispatchIntent { context -> + Intent(context, MediaPreviewActivity::class.java).apply { + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + setDataAndType(slide.uri, slide.contentType) + putExtra(MediaPreviewActivity.ADDRESS_EXTRA, mms.recipient.address) + putExtra(MediaPreviewActivity.OUTGOING_EXTRA, mms.isOutgoing) + putExtra(MediaPreviewActivity.DATE_EXTRA, mms.timestamp) + putExtra(MediaPreviewActivity.SIZE_EXTRA, slide.asAttachment().size) + putExtra(MediaPreviewActivity.CAPTION_EXTRA, slide.caption.orNull()) + putExtra(MediaPreviewActivity.LEFT_IS_RECENT_EXTRA, false) + } + } + } } } } - return null } fun bind(glideRequests: GlideRequests, message: MmsMessageRecord, diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index a424a5d5d9..52038d7316 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -2,6 +2,9 @@ package org.thoughtcrime.securesms.conversation.v2 import android.animation.FloatEvaluator import android.animation.ValueAnimator +import android.app.Activity +import android.content.Context +import android.content.Intent import android.content.res.Resources import android.database.Cursor import android.graphics.Rect @@ -46,6 +49,9 @@ import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel.LinkPreviewState +import org.thoughtcrime.securesms.loki.utilities.ActivityDispatcher +import org.thoughtcrime.securesms.loki.utilities.push +import org.thoughtcrime.securesms.loki.utilities.show import org.thoughtcrime.securesms.loki.utilities.toPx import org.thoughtcrime.securesms.mms.GlideApp import org.thoughtcrime.securesms.util.DateUtils @@ -57,7 +63,7 @@ import kotlin.math.* // price we pay is a bit of back and forth between the input bar and the conversation activity. class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDelegate, - InputBarRecordingViewDelegate, ConversationRecyclerViewDelegate { + InputBarRecordingViewDelegate, ConversationRecyclerViewDelegate, ActivityDispatcher { private val scrollButtonFullVisibilityThreshold by lazy { toPx(120.0f, resources) } private val scrollButtonNoVisibilityThreshold by lazy { toPx(20.0f, resources) } private val screenWidth = Resources.getSystem().displayMetrics.widthPixels @@ -76,8 +82,8 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe val adapter = ConversationAdapter( this, cursor, - onItemPress = { message, position, view, rect -> - handlePress(message, position, view, rect) + onItemPress = { message, position, view, rawRect -> + handlePress(message, position, view, rawRect) }, onItemSwipeToReply = { message, position -> handleSwipeToReply(message, position) @@ -138,6 +144,18 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe ApplicationContext.getInstance(this).messageNotifier.setVisibleThread(-1) } + override fun getSystemService(name: String): Any? { + if (name == ActivityDispatcher.SERVICE) { + return this + } + return super.getSystemService(name) + } + + override fun dispatchIntent(body: (Context) -> Intent) { + val intent = body(this) + push(intent, false) + } + private fun setUpRecyclerView() { conversationRecyclerView.adapter = adapter val layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, true) @@ -452,7 +470,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe } // `position` is the adapter position; not the visual position - private fun handlePress(message: MessageRecord, position: Int, view: VisibleMessageView, hitRect: Rect) { + private fun handlePress(message: MessageRecord, position: Int, view: VisibleMessageView, rawRect: Rect) { val actionMode = this.actionMode if (actionMode != null) { adapter.toggleSelection(message, position) @@ -467,7 +485,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe // We have to use onContentClick (rather than a click listener directly on // the view) so as to not interfere with all the other gestures. Do not add // onClickListeners directly to message content views. - view.onContentClick(hitRect) + view.onContentClick(rawRect) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapter.kt index 2a409ab0e8..c5c0d1e028 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapter.kt @@ -69,7 +69,7 @@ class ConversationAdapter(context: Context, cursor: Cursor, private val onItemPr view.messageTimestampTextView.isVisible = isSelected val position = viewHolder.adapterPosition view.bind(message, getMessageBefore(position, cursor), getMessageAfter(position, cursor), glide) - view.onPress = { x, y -> onItemPress(message, viewHolder.adapterPosition, view, Rect(x,y,x,y)) } + view.onPress = { rawX, rawY -> onItemPress(message, viewHolder.adapterPosition, view, Rect(rawX, rawY, rawX, rawY)) } view.onSwipeToReply = { onItemSwipeToReply(message, viewHolder.adapterPosition) } view.onLongPress = { onItemLongPress(message, viewHolder.adapterPosition) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt index b32be1a2e3..0a9c2decb9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt @@ -31,7 +31,7 @@ import org.thoughtcrime.securesms.mms.GlideRequests import kotlin.math.roundToInt class VisibleMessageContentView : LinearLayout { - var onContentClick: ((Rect) -> Unit)? = null + var onContentClick: ((rawRect: Rect) -> Unit)? = null // region Lifecycle constructor(context: Context) : super(context) { initialize() } @@ -96,16 +96,7 @@ class VisibleMessageContentView : LinearLayout { isStart = isStartOfMessageCluster, isEnd = isEndOfMessageCluster ) - onContentClick = { - when (val hitObject = albumThumbnailView.calculateHitObject(it)) { - is AlbumThumbnailView.Hit.SlideHit -> Log.d("Loki-UI", "clicked display slide ${hitObject.slide}")// open the slide preview - is AlbumThumbnailView.Hit.DownloadHit -> Log.d("Loki-UI", "clicked display download") - AlbumThumbnailView.Hit.ReadMoreHit -> Log.d("Loki-UI", "clicked the read more display") - else -> { - Log.d("Loki-UI", "DIDN'T click anything important") - } - } - } + onContentClick = { albumThumbnailView.calculateHitObject(it, message) } } else if (message.isOpenGroupInvitation) { val openGroupInvitationView = OpenGroupInvitationView(context) openGroupInvitationView.bind(message, VisibleMessageContentView.getTextColor(context, message)) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt index 4e4add768c..e0ad3930ab 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt @@ -50,7 +50,7 @@ class VisibleMessageView : LinearLayout { private var onDownTimestamp = 0L var snIsSelected = false set(value) { field = value; handleIsSelectedChanged()} - var onPress: ((x: Int, y: Int) -> Unit)? = null + var onPress: ((rawX: Int, rawY: Int) -> Unit)? = null var onSwipeToReply: (() -> Unit)? = null var onLongPress: (() -> Unit)? = null @@ -272,7 +272,7 @@ class VisibleMessageView : LinearLayout { onSwipeToReply?.invoke() } else if ((Date().time - onDownTimestamp) < VisibleMessageView.longPressDurationThreshold) { longPressCallback?.let { gestureHandler.removeCallbacks(it) } - onPress?.invoke(event.x.toInt(), event.y.toInt()) + onPress?.invoke(event.rawX.toInt(), event.rawY.toInt()) } resetPosition() } @@ -297,8 +297,8 @@ class VisibleMessageView : LinearLayout { onLongPress?.invoke() } - fun onContentClick(hitRect: Rect) { - messageContentView.onContentClick?.invoke(hitRect) + fun onContentClick(rawRect: Rect) { + messageContentView.onContentClick?.invoke(rawRect) } // endregion } diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/ActivityUtilities.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/ActivityUtilities.kt index 6fd9250e0d..16bc9b1df8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/ActivityUtilities.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/ActivityUtilities.kt @@ -1,6 +1,10 @@ package org.thoughtcrime.securesms.loki.utilities +import android.annotation.SuppressLint +import android.app.Activity +import android.content.Context import android.content.Intent +import android.os.Bundle import android.view.View import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.Toolbar @@ -52,4 +56,13 @@ fun AppCompatActivity.show(intent: Intent, isForResult: Boolean = false) { startActivity(intent) } overridePendingTransition(R.anim.slide_from_bottom, R.anim.fade_scale_out) +} + +interface ActivityDispatcher { + companion object { + const val SERVICE = "ActivityDispatcher_SERVICE" + @SuppressLint("WrongConstant") + fun get(context: Context) = context.getSystemService(SERVICE) as? ActivityDispatcher + } + fun dispatchIntent(body: (Context)->Intent) } \ No newline at end of file From f59b3a3b610353a7c128cd69d8b97c42086e7859 Mon Sep 17 00:00:00 2001 From: jubb Date: Mon, 28 Jun 2021 09:59:33 +1000 Subject: [PATCH 09/20] refactor: remove unused KThumbnailView functions, extract intent creation to MediaPreviewActivity --- .../securesms/MediaPreviewActivity.java | 18 +++++++++++ .../conversation/v2/AlbumThumbnailView.kt | 32 +++++++------------ .../conversation/v2/ConversationActivityV2.kt | 4 +-- .../v2/utilities/KThumbnailView.kt | 16 ---------- .../loki/utilities/ActivityUtilities.kt | 2 +- 5 files changed, 33 insertions(+), 39 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java b/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java index 3f8f03fa51..def84b57f0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java @@ -64,10 +64,12 @@ import org.session.libsignal.utilities.Log; import org.thoughtcrime.securesms.components.MediaView; import org.thoughtcrime.securesms.database.MediaDatabase.MediaRecord; import org.thoughtcrime.securesms.database.loaders.PagingMediaLoader; +import org.thoughtcrime.securesms.database.model.MmsMessageRecord; import org.thoughtcrime.securesms.mediapreview.MediaPreviewViewModel; import org.thoughtcrime.securesms.mediapreview.MediaRailAdapter; import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.mms.GlideRequests; +import org.thoughtcrime.securesms.mms.Slide; import org.thoughtcrime.securesms.permissions.Permissions; import org.thoughtcrime.securesms.util.AttachmentUtil; import org.thoughtcrime.securesms.util.DateUtils; @@ -116,6 +118,22 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im private int restartItem = -1; + public static Intent getPreviewIntent(Context context, Slide slide, MmsMessageRecord mms) { + Intent previewIntent = null; + if (MediaPreviewActivity.isContentTypeSupported(slide.getContentType()) && slide.getUri() != null) { + previewIntent = new Intent(context, MediaPreviewActivity.class); + previewIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + .setDataAndType(slide.getUri(), slide.getContentType()) + .putExtra(ADDRESS_EXTRA, mms.getRecipient().getAddress()) + .putExtra(OUTGOING_EXTRA, mms.isOutgoing()) + .putExtra(DATE_EXTRA, mms.getTimestamp()) + .putExtra(SIZE_EXTRA, slide.asAttachment().getSize()) + .putExtra(CAPTION_EXTRA, slide.getCaption().orNull()) + .putExtra(LEFT_IS_RECENT_EXTRA, false); + } + return previewIntent; + } + @SuppressWarnings("ConstantConditions") @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt index 56da118f3b..d895e50743 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt @@ -38,6 +38,7 @@ class AlbumThumbnailView : FrameLayout { private val cornerMask by lazy { CornerMask(this) } private var slides: List = listOf() + private var slideSize: Int = 0 private fun initialize() { LayoutInflater.from(context).inflate(R.layout.album_thumbnail_view, this) @@ -69,22 +70,9 @@ class AlbumThumbnailView : FrameLayout { child.getGlobalVisibleRect(testRect) if (Rect.intersects(rawRect, testRect)) { // hit intersects with this particular child - slides.getOrNull(index)?.let { slide -> - // dispatch to view image - if (MediaPreviewActivity.isContentTypeSupported(slide.contentType) && slide.uri != null) { - ActivityDispatcher.get(context)?.dispatchIntent { context -> - Intent(context, MediaPreviewActivity::class.java).apply { - addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - setDataAndType(slide.uri, slide.contentType) - putExtra(MediaPreviewActivity.ADDRESS_EXTRA, mms.recipient.address) - putExtra(MediaPreviewActivity.OUTGOING_EXTRA, mms.isOutgoing) - putExtra(MediaPreviewActivity.DATE_EXTRA, mms.timestamp) - putExtra(MediaPreviewActivity.SIZE_EXTRA, slide.asAttachment().size) - putExtra(MediaPreviewActivity.CAPTION_EXTRA, slide.caption.orNull()) - putExtra(MediaPreviewActivity.LEFT_IS_RECENT_EXTRA, false) - } - } - } + val slide = slides.getOrNull(index) ?: return + ActivityDispatcher.get(context)?.dispatchIntent { context -> + MediaPreviewActivity.getPreviewIntent(context, slide, mms) } } } @@ -92,16 +80,20 @@ class AlbumThumbnailView : FrameLayout { fun bind(glideRequests: GlideRequests, message: MmsMessageRecord, isStart: Boolean, isEnd: Boolean) { - // TODO: optimize for same size slides = message.slideDeck.thumbnailSlides if (slides.isEmpty()) { // this should never be encountered because it's checked by parent return } calculateRadius(isStart, isEnd, message.isOutgoing) - albumCellContainer.removeAllViews() - LayoutInflater.from(context).inflate(layoutRes(slides.size), albumCellContainer) - // iterate + + // recreate cell views if different size to what we have already (for recycling) + if (slides.size != this.slideSize) { + albumCellContainer.removeAllViews() + LayoutInflater.from(context).inflate(layoutRes(slides.size), albumCellContainer) + this.slideSize = slides.size + } + // iterate binding slides.take(5).forEachIndexed { position, slide -> val thumbnailView = getThumbnailView(position) thumbnailView.setImageResource(glideRequests, slide, showControls = false, isPreview = false) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index 52038d7316..336645dfbb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -151,8 +151,8 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe return super.getSystemService(name) } - override fun dispatchIntent(body: (Context) -> Intent) { - val intent = body(this) + override fun dispatchIntent(body: (Context) -> Intent?) { + val intent = body(this) ?: return push(intent, false) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/KThumbnailView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/KThumbnailView.kt index df6f4d600d..a8d20736ab 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/KThumbnailView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/KThumbnailView.kt @@ -186,10 +186,6 @@ open class KThumbnailView: FrameLayout { slide = null } - fun showProgressSpinner() { - transferControls.showProgressSpinner() - } - fun setImageResource(glideRequests: GlideRequests, uri: Uri): ListenableFuture { val future = SettableFuture() @@ -210,18 +206,6 @@ open class KThumbnailView: FrameLayout { return future } - fun showDownloadText(showDownloadText: Boolean) { - transferControls.setShowDownloadText(showDownloadText) - } - - fun setDownloadClickListener(listener: SlidesClickedListener) { - transferControls.setDownloadClickListener { - slide?.let { slide -> - listener.onClick(it, listOf(slide)) - } - } - } - // endregion } \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/ActivityUtilities.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/ActivityUtilities.kt index 16bc9b1df8..4986a1ce36 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/ActivityUtilities.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/ActivityUtilities.kt @@ -64,5 +64,5 @@ interface ActivityDispatcher { @SuppressLint("WrongConstant") fun get(context: Context) = context.getSystemService(SERVICE) as? ActivityDispatcher } - fun dispatchIntent(body: (Context)->Intent) + fun dispatchIntent(body: (Context)->Intent?) } \ No newline at end of file From 2c8141c8bb73325da038c9d582bf106b9af8c4c1 Mon Sep 17 00:00:00 2001 From: jubb Date: Mon, 28 Jun 2021 10:42:34 +1000 Subject: [PATCH 10/20] fix: don't let the attachment open to preview if in progress or pending download --- .../securesms/conversation/v2/AlbumThumbnailView.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt index d895e50743..3df431f446 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt @@ -71,6 +71,9 @@ class AlbumThumbnailView : FrameLayout { if (Rect.intersects(rawRect, testRect)) { // hit intersects with this particular child val slide = slides.getOrNull(index) ?: return + // only open to downloaded images + if (slide.isInProgress || slide.isPendingDownload) return + ActivityDispatcher.get(context)?.dispatchIntent { context -> MediaPreviewActivity.getPreviewIntent(context, slide, mms) } From ddc75859aac11fd8736017d933cf2e01125f029c Mon Sep 17 00:00:00 2001 From: jubb Date: Mon, 28 Jun 2021 10:47:21 +1000 Subject: [PATCH 11/20] fix: re-add radius to ThumbnailView --- .../securesms/conversation/v2/AlbumThumbnailView.kt | 2 +- .../securesms/conversation/v2/utilities/ThumbnailView.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt index 3df431f446..aaee530245 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt @@ -72,7 +72,7 @@ class AlbumThumbnailView : FrameLayout { // hit intersects with this particular child val slide = slides.getOrNull(index) ?: return // only open to downloaded images - if (slide.isInProgress || slide.isPendingDownload) return + if (slide.isInProgress) return ActivityDispatcher.get(context)?.dispatchIntent { context -> MediaPreviewActivity.getPreviewIntent(context, slide, mms) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ThumbnailView.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ThumbnailView.java index d0d7c8a605..79d0099998 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ThumbnailView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ThumbnailView.java @@ -97,6 +97,7 @@ public class ThumbnailView extends FrameLayout { bounds[MAX_WIDTH] = typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_maxWidth, 0); bounds[MIN_HEIGHT] = typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_minHeight, 0); bounds[MAX_HEIGHT] = typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_maxHeight, 0); + radius = typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_thumbnail_radius, 0); typedArray.recycle(); } else { radius = 0; From d8a2e3450c4a0c6823de5f263f4aa64308d2dc7d Mon Sep 17 00:00:00 2001 From: jubb Date: Mon, 28 Jun 2021 11:29:27 +1000 Subject: [PATCH 12/20] feat: progress bar is linear similar to iOS --- app/src/main/res/layout/thumbnail_view.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/res/layout/thumbnail_view.xml b/app/src/main/res/layout/thumbnail_view.xml index a22164f376..6f5942faca 100644 --- a/app/src/main/res/layout/thumbnail_view.xml +++ b/app/src/main/res/layout/thumbnail_view.xml @@ -23,14 +23,14 @@ android:visibility="gone" /> Date: Mon, 28 Jun 2021 12:12:34 +1000 Subject: [PATCH 13/20] refactor: simplify the long message activity --- .../conversation/v2/ConversationActivityV2.kt | 5 ---- .../longmessage/LongMessageActivity.java | 29 ++++--------------- .../main/res/layout/longmessage_activity.xml | 21 +++++++------- 3 files changed, 16 insertions(+), 39 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index 0d70f24e0a..df917e50cb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -2,10 +2,8 @@ package org.thoughtcrime.securesms.conversation.v2 import android.animation.FloatEvaluator import android.animation.ValueAnimator -import android.app.Activity import android.content.Context import android.content.Intent -import android.content.Intent import android.content.res.Resources import android.database.Cursor import android.graphics.Rect @@ -45,7 +43,6 @@ import org.session.libsession.messaging.open_groups.OpenGroupAPIV2 import org.session.libsession.messaging.sending_receiving.MessageSender import org.session.libsession.messaging.sending_receiving.attachments.Attachment import org.session.libsession.utilities.MediaTypes -import org.session.libsession.utilities.ServiceUtil import org.session.libsession.utilities.TextSecurePreferences import org.session.libsignal.utilities.ListenableFuture import org.thoughtcrime.securesms.ApplicationContext @@ -71,13 +68,11 @@ import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel.LinkPreviewState import org.thoughtcrime.securesms.loki.utilities.ActivityDispatcher import org.thoughtcrime.securesms.loki.utilities.push -import org.thoughtcrime.securesms.loki.utilities.show import org.thoughtcrime.securesms.loki.utilities.toPx import org.thoughtcrime.securesms.mediasend.Media import org.thoughtcrime.securesms.mediasend.MediaSendActivity import org.thoughtcrime.securesms.mms.* import org.thoughtcrime.securesms.notifications.MarkReadReceiver -import org.thoughtcrime.securesms.providers.BlobProvider import org.thoughtcrime.securesms.util.DateUtils import org.thoughtcrime.securesms.util.MediaUtil import java.util.* diff --git a/app/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessageActivity.java b/app/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessageActivity.java index d3c683dd46..03af13d6b2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessageActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessageActivity.java @@ -43,8 +43,8 @@ public class LongMessageActivity extends PassphraseRequiredActionBarActivity { private static final int MAX_DISPLAY_LENGTH = 64 * 1024; - private Stub sentBubble; - private Stub receivedBubble; + private TextView textBody; + private ConversationItemFooter footerText; private LongMessageViewModel viewModel; @@ -60,9 +60,8 @@ public class LongMessageActivity extends PassphraseRequiredActionBarActivity { protected void onCreate(Bundle savedInstanceState, boolean ready) { super.onCreate(savedInstanceState, ready); setContentView(R.layout.longmessage_activity); - - sentBubble = new Stub<>(findViewById(R.id.longmessage_sent_stub)); - receivedBubble = new Stub<>(findViewById(R.id.longmessage_received_stub)); + textBody = findViewById(R.id.longmessage_text); + footerText = findViewById(R.id.longmessage_footer); initViewModel(getIntent().getLongExtra(KEY_MESSAGE_ID, -1), getIntent().getBooleanExtra(KEY_IS_MMS, false)); } @@ -102,27 +101,11 @@ public class LongMessageActivity extends PassphraseRequiredActionBarActivity { getSupportActionBar().setTitle(getString(R.string.LongMessageActivity_message_from_s, name)); } - ViewGroup bubble; - - if (message.get().getMessageRecord().isOutgoing()) { - bubble = sentBubble.get(); - bubble.getBackground().setColorFilter(ThemeUtil.getThemedColor(this, R.attr.message_sent_background_color), PorterDuff.Mode.MULTIPLY); - } else { - bubble = receivedBubble.get(); - bubble.getBackground().setColorFilter(ThemeUtil.getThemedColor(this, R.attr.message_received_background_color), PorterDuff.Mode.MULTIPLY); - } - - TextView text = bubble.findViewById(R.id.longmessage_text); - ConversationItemFooter footer = bubble.findViewById(R.id.longmessage_footer); - String trimmedBody = getTrimmedBody(message.get().getFullBody()); SpannableString styledBody = linkifyMessageBody(new SpannableString(trimmedBody)); - bubble.setVisibility(View.VISIBLE); - text.setText(styledBody); - text.setMovementMethod(LinkMovementMethod.getInstance()); - text.setTextSize(TypedValue.COMPLEX_UNIT_SP, TextSecurePreferences.getMessageBodyTextSize(this)); - footer.setMessageRecord(message.get().getMessageRecord(), Locale.getDefault()); + textBody.setText(styledBody); + footerText.setMessageRecord(message.get().getMessageRecord(), Locale.getDefault()); }); } diff --git a/app/src/main/res/layout/longmessage_activity.xml b/app/src/main/res/layout/longmessage_activity.xml index 49f0641ab5..382b0c6bcf 100644 --- a/app/src/main/res/layout/longmessage_activity.xml +++ b/app/src/main/res/layout/longmessage_activity.xml @@ -4,23 +4,22 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - - + android:layout_height="wrap_content"/> - + - + \ No newline at end of file From 2e48e5f93ed31205adc868c4c960a003dcd0bf11 Mon Sep 17 00:00:00 2001 From: jubb Date: Mon, 28 Jun 2021 13:39:06 +1000 Subject: [PATCH 14/20] refactor: re-add link movement to text body, remove conversation footer from longmessage_activity.xml --- .../securesms/longmessage/LongMessageActivity.java | 4 +--- app/src/main/res/layout/longmessage_activity.xml | 6 +----- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessageActivity.java b/app/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessageActivity.java index 03af13d6b2..e5430fa78c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessageActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessageActivity.java @@ -44,7 +44,6 @@ public class LongMessageActivity extends PassphraseRequiredActionBarActivity { private static final int MAX_DISPLAY_LENGTH = 64 * 1024; private TextView textBody; - private ConversationItemFooter footerText; private LongMessageViewModel viewModel; @@ -61,7 +60,6 @@ public class LongMessageActivity extends PassphraseRequiredActionBarActivity { super.onCreate(savedInstanceState, ready); setContentView(R.layout.longmessage_activity); textBody = findViewById(R.id.longmessage_text); - footerText = findViewById(R.id.longmessage_footer); initViewModel(getIntent().getLongExtra(KEY_MESSAGE_ID, -1), getIntent().getBooleanExtra(KEY_IS_MMS, false)); } @@ -105,7 +103,7 @@ public class LongMessageActivity extends PassphraseRequiredActionBarActivity { SpannableString styledBody = linkifyMessageBody(new SpannableString(trimmedBody)); textBody.setText(styledBody); - footerText.setMessageRecord(message.get().getMessageRecord(), Locale.getDefault()); + textBody.setMovementMethod(LinkMovementMethod.getInstance()); }); } diff --git a/app/src/main/res/layout/longmessage_activity.xml b/app/src/main/res/layout/longmessage_activity.xml index 382b0c6bcf..ed933b2355 100644 --- a/app/src/main/res/layout/longmessage_activity.xml +++ b/app/src/main/res/layout/longmessage_activity.xml @@ -11,15 +11,11 @@ android:padding="16dp"> - - \ No newline at end of file From a91cd45b89f03225d5d322c540d869fcc365d600 Mon Sep 17 00:00:00 2001 From: jubb Date: Mon, 28 Jun 2021 15:55:34 +1000 Subject: [PATCH 15/20] feat: new ThumbnailProgressBar.kt for animating the loading progress --- .../v2/utilities/KThumbnailView.kt | 2 +- .../v2/utilities/ThumbnailProgressBar.kt | 69 +++++++++++++++++++ .../main/res/layout/album_thumbnail_view.xml | 2 +- app/src/main/res/layout/thumbnail_view.xml | 7 +- 4 files changed, 73 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ThumbnailProgressBar.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/KThumbnailView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/KThumbnailView.kt index a8d20736ab..1710444c84 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/KThumbnailView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/KThumbnailView.kt @@ -42,7 +42,7 @@ open class KThumbnailView: FrameLayout { private val image by lazy { thumbnail_image } private val playOverlay by lazy { play_overlay } private val captionIcon by lazy { thumbnail_caption_icon } - val loadIndicator: ProgressBar by lazy { thumbnail_load_indicator } + val loadIndicator: View by lazy { thumbnail_load_indicator } private val transferControls by lazy { ViewUtil.inflateStub(this, R.id.transfer_controls_stub) } private val dimensDelegate = ThumbnailDimensDelegate() diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ThumbnailProgressBar.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ThumbnailProgressBar.kt new file mode 100644 index 0000000000..2a9b033732 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ThumbnailProgressBar.kt @@ -0,0 +1,69 @@ +package org.thoughtcrime.securesms.conversation.v2.utilities + +import android.animation.ValueAnimator +import android.content.Context +import android.graphics.Canvas +import android.graphics.Interpolator +import android.graphics.Paint +import android.graphics.Rect +import android.os.SystemClock +import android.util.AttributeSet +import android.util.Log +import android.view.View +import android.view.animation.AccelerateDecelerateInterpolator +import android.view.animation.Animation +import android.view.animation.AnimationSet +import android.view.animation.AnimationUtils +import androidx.core.content.res.ResourcesCompat +import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView +import network.loki.messenger.R +import kotlin.math.sin + +class ThumbnailProgressBar: View, ValueAnimator.AnimatorUpdateListener { + + constructor(context: Context) : super(context) + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) + + private val firstX: Double + get() = sin(SystemClock.elapsedRealtime() / 300.0) * 1.5 + + private val secondX: Double + get() = sin(SystemClock.elapsedRealtime() / 300.0 + (Math.PI/4)) * 1.5 + + private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply { + style = Paint.Style.FILL + color = ResourcesCompat.getColor(resources, R.color.accent, null) + } + + private val objectRect = Rect() + private val drawingRect = Rect() + + override fun onAnimationUpdate(animation: ValueAnimator?) { + if (animation == null || !isAttachedToWindow) return + invalidate() + } + + override fun dispatchDraw(canvas: Canvas?) { + if (canvas == null) return + + getDrawingRect(objectRect) + drawingRect.set(objectRect) + + val coercedFX = firstX + val coercedSX = secondX + + val firstMeasuredX = objectRect.left + (objectRect.width() * coercedFX) + val secondMeasuredX = objectRect.left + (objectRect.width() * coercedSX) + + drawingRect.set( + (if (firstMeasuredX < secondMeasuredX) firstMeasuredX else secondMeasuredX).toInt(), + objectRect.top, + (if (firstMeasuredX < secondMeasuredX) secondMeasuredX else firstMeasuredX).toInt(), + objectRect.bottom + ) + + canvas.drawRect(drawingRect, paint) + invalidate() + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/album_thumbnail_view.xml b/app/src/main/res/layout/album_thumbnail_view.xml index e9ca4e20e3..a7e80b31f6 100644 --- a/app/src/main/res/layout/album_thumbnail_view.xml +++ b/app/src/main/res/layout/album_thumbnail_view.xml @@ -35,7 +35,7 @@ /> - From 3611f4655cdd0547b4967cfcb802d3b2669aae5f Mon Sep 17 00:00:00 2001 From: jubb Date: Tue, 29 Jun 2021 10:06:57 +1000 Subject: [PATCH 16/20] fix: merge changes and fix conflicts --- .../securesms/conversation/v2/messages/VisibleMessageView.kt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt index 049796764e..88e4ee9ced 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt @@ -313,9 +313,5 @@ class VisibleMessageView : LinearLayout { onPress?.invoke(rawX, rawY) pressCallback = null } - - fun onContentClick() { - messageContentView.onContentClick?.invoke() - } // endregion } From 09f4cfb1ad7c8e0fe16e332d2b7def9280b6d347 Mon Sep 17 00:00:00 2001 From: jubb Date: Tue, 29 Jun 2021 11:22:59 +1000 Subject: [PATCH 17/20] feat: add back overflow text, increase AttachmentDownloadJob.kt max failure count, resize overflow text size, remove unused animator update listener implementation in ThumbnailProgressBar.kt --- .../securesms/conversation/v2/AlbumThumbnailView.kt | 12 ++++++++++++ .../v2/utilities/ThumbnailProgressBar.kt | 7 +------ app/src/main/res/layout/album_thumbnail_many.xml | 2 +- .../messaging/jobs/AttachmentDownloadJob.kt | 2 +- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt index aaee530245..b8ac292903 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt @@ -8,8 +8,10 @@ import android.util.AttributeSet import android.view.LayoutInflater import android.view.ViewGroup import android.widget.FrameLayout +import android.widget.TextView import androidx.core.view.children import androidx.core.view.isVisible +import kotlinx.android.synthetic.main.album_thumbnail_many.view.* import kotlinx.android.synthetic.main.album_thumbnail_view.view.* import network.loki.messenger.R import org.thoughtcrime.securesms.MediaPreviewActivity @@ -23,6 +25,10 @@ import org.thoughtcrime.securesms.mms.Slide class AlbumThumbnailView : FrameLayout { + companion object { + const val MAX_ALBUM_DISPLAY_SIZE = 5 + } + // region Lifecycle constructor(context: Context) : super(context) { initialize() @@ -94,6 +100,12 @@ class AlbumThumbnailView : FrameLayout { if (slides.size != this.slideSize) { albumCellContainer.removeAllViews() LayoutInflater.from(context).inflate(layoutRes(slides.size), albumCellContainer) + val overflowed = slides.size > MAX_ALBUM_DISPLAY_SIZE + albumCellContainer.findViewById(R.id.album_cell_overflow_text)?.let { overflowText -> + // overflowText will be null if !overflowed + overflowText.isVisible = overflowed // more than max album size + overflowText.text = context.getString(R.string.AlbumThumbnailView_plus, slides.size - MAX_ALBUM_DISPLAY_SIZE) + } this.slideSize = slides.size } // iterate binding diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ThumbnailProgressBar.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ThumbnailProgressBar.kt index 2a9b033732..60ef8116d2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ThumbnailProgressBar.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ThumbnailProgressBar.kt @@ -19,7 +19,7 @@ import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView import network.loki.messenger.R import kotlin.math.sin -class ThumbnailProgressBar: View, ValueAnimator.AnimatorUpdateListener { +class ThumbnailProgressBar: View { constructor(context: Context) : super(context) constructor(context: Context, attrs: AttributeSet) : super(context, attrs) @@ -39,11 +39,6 @@ class ThumbnailProgressBar: View, ValueAnimator.AnimatorUpdateListener { private val objectRect = Rect() private val drawingRect = Rect() - override fun onAnimationUpdate(animation: ValueAnimator?) { - if (animation == null || !isAttachedToWindow) return - invalidate() - } - override fun dispatchDraw(canvas: Canvas?) { if (canvas == null) return diff --git a/app/src/main/res/layout/album_thumbnail_many.xml b/app/src/main/res/layout/album_thumbnail_many.xml index 9672055b80..de60462995 100644 --- a/app/src/main/res/layout/album_thumbnail_many.xml +++ b/app/src/main/res/layout/album_thumbnail_many.xml @@ -51,7 +51,7 @@ android:layout_height="match_parent" android:layout_width="match_parent" android:gravity="center" - android:textSize="28dp" + android:textSize="@dimen/text_size" android:textColor="@color/core_white" android:background="@color/transparent_black_40" tools:text="+2" /> diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentDownloadJob.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentDownloadJob.kt index b0d923b0c3..a5aaeaa429 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentDownloadJob.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentDownloadJob.kt @@ -23,7 +23,7 @@ class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long) } // Settings - override val maxFailureCount: Int = 20 + override val maxFailureCount: Int = 100 companion object { val KEY: String = "AttachmentDownloadJob" From 476c0ea4cdcf8fddc10ee963d67f27adaf14534b Mon Sep 17 00:00:00 2001 From: jubb Date: Tue, 29 Jun 2021 12:03:04 +1000 Subject: [PATCH 18/20] fix: alignment issues fixed --- .../conversation/v2/AlbumThumbnailView.kt | 1 - .../main/res/layout/album_thumbnail_view.xml | 18 +++++++++++++----- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt index b8ac292903..c14c8fd662 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt @@ -54,7 +54,6 @@ class AlbumThumbnailView : FrameLayout { super.dispatchDraw(canvas) cornerMask.mask(canvas) } - // endregion // region Interaction diff --git a/app/src/main/res/layout/album_thumbnail_view.xml b/app/src/main/res/layout/album_thumbnail_view.xml index a7e80b31f6..d9b566976d 100644 --- a/app/src/main/res/layout/album_thumbnail_view.xml +++ b/app/src/main/res/layout/album_thumbnail_view.xml @@ -1,9 +1,9 @@ - + android:layout_width="match_parent" + android:layout_height="match_parent"> - \ No newline at end of file + \ No newline at end of file From c68781fffb394a7aaa7d4215415f5bb7f3a62215 Mon Sep 17 00:00:00 2001 From: jubb Date: Tue, 29 Jun 2021 13:32:30 +1000 Subject: [PATCH 19/20] fix: long message handles mentions properly and doesn't do linkified text --- .../longmessage/LongMessageActivity.java | 42 ++++--------------- 1 file changed, 7 insertions(+), 35 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessageActivity.java b/app/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessageActivity.java index e5430fa78c..adb8a921b5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessageActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessageActivity.java @@ -2,36 +2,20 @@ package org.thoughtcrime.securesms.longmessage; import android.content.Context; import android.content.Intent; -import android.graphics.PorterDuff; import android.os.Bundle; -import android.text.SpannableString; import android.text.method.LinkMovementMethod; -import android.text.style.URLSpan; -import android.text.util.Linkify; -import android.util.TypedValue; import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.lifecycle.ViewModelProvider; -import com.annimon.stream.Stream; - -import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity; -import org.thoughtcrime.securesms.components.ConversationItemFooter; -import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil; - import org.session.libsession.utilities.Address; -import org.session.libsession.utilities.recipients.Recipient; -import org.session.libsession.utilities.TextSecurePreferences; -import org.session.libsession.utilities.ThemeUtil; import org.session.libsession.utilities.Util; -import org.session.libsession.utilities.Stub; - -import java.util.Locale; +import org.session.libsession.utilities.recipients.Recipient; +import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity; +import org.thoughtcrime.securesms.loki.utilities.MentionUtilities; import network.loki.messenger.R; @@ -90,19 +74,18 @@ public class LongMessageActivity extends PassphraseRequiredActionBarActivity { return; } - if (message.get().getMessageRecord().isOutgoing()) { getSupportActionBar().setTitle(getString(R.string.LongMessageActivity_your_message)); } else { Recipient recipient = message.get().getMessageRecord().getRecipient(); - String name = Util.getFirstNonEmpty(recipient.getName(), recipient.getProfileName(), recipient.getAddress().serialize()) ; + String name = Util.getFirstNonEmpty(recipient.getName(), recipient.getProfileName(), recipient.getAddress().serialize()); getSupportActionBar().setTitle(getString(R.string.LongMessageActivity_message_from_s, name)); } - String trimmedBody = getTrimmedBody(message.get().getFullBody()); - SpannableString styledBody = linkifyMessageBody(new SpannableString(trimmedBody)); + String trimmedBody = getTrimmedBody(message.get().getFullBody()); + String mentionBody = MentionUtilities.highlightMentions(trimmedBody, message.get().getMessageRecord().getThreadId(), this); - textBody.setText(styledBody); + textBody.setText(mentionBody); textBody.setMovementMethod(LinkMovementMethod.getInstance()); }); } @@ -112,15 +95,4 @@ public class LongMessageActivity extends PassphraseRequiredActionBarActivity { : text.substring(0, MAX_DISPLAY_LENGTH); } - private SpannableString linkifyMessageBody(SpannableString messageBody) { - int linkPattern = Linkify.WEB_URLS | Linkify.EMAIL_ADDRESSES | Linkify.PHONE_NUMBERS; - boolean hasLinks = Linkify.addLinks(messageBody, linkPattern); - - if (hasLinks) { - Stream.of(messageBody.getSpans(0, messageBody.length(), URLSpan.class)) - .filterNot(url -> LinkPreviewUtil.isLegalUrl(url.getURL())) - .forEach(messageBody::removeSpan); - } - return messageBody; - } } From 60f9f0edfb138570a995aa86df5dd5b651ff38f6 Mon Sep 17 00:00:00 2001 From: jubb Date: Tue, 29 Jun 2021 13:35:46 +1000 Subject: [PATCH 20/20] fix: remove transfer controls from KThumbnailView.kt to prevent view stub being inflated --- .../components/AlbumThumbnailView.java | 2 +- .../conversation/v2/AlbumThumbnailView.kt | 4 +--- .../v2/utilities/KThumbnailView.kt | 22 ++++--------------- 3 files changed, 6 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/AlbumThumbnailView.java b/app/src/main/java/org/thoughtcrime/securesms/components/AlbumThumbnailView.java index df88367710..bda4159c9a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/AlbumThumbnailView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/AlbumThumbnailView.java @@ -150,7 +150,7 @@ public class AlbumThumbnailView extends FrameLayout { private void setSlide(@NonNull GlideRequests glideRequests, @NonNull Slide slide, @IdRes int id) { KThumbnailView cell = findViewById(id); - cell.setImageResource(glideRequests, slide, false, false); + cell.setImageResource(glideRequests, slide, false); cell.setThumbnailClickListener(defaultThumbnailClickListener); cell.setOnLongClickListener(defaultLongClickListener); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt index c14c8fd662..5384be14e2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/AlbumThumbnailView.kt @@ -1,7 +1,6 @@ package org.thoughtcrime.securesms.conversation.v2 import android.content.Context -import android.content.Intent import android.graphics.Canvas import android.graphics.Rect import android.util.AttributeSet @@ -11,7 +10,6 @@ import android.widget.FrameLayout import android.widget.TextView import androidx.core.view.children import androidx.core.view.isVisible -import kotlinx.android.synthetic.main.album_thumbnail_many.view.* import kotlinx.android.synthetic.main.album_thumbnail_view.view.* import network.loki.messenger.R import org.thoughtcrime.securesms.MediaPreviewActivity @@ -110,7 +108,7 @@ class AlbumThumbnailView : FrameLayout { // iterate binding slides.take(5).forEachIndexed { position, slide -> val thumbnailView = getThumbnailView(position) - thumbnailView.setImageResource(glideRequests, slide, showControls = false, isPreview = false) + thumbnailView.setImageResource(glideRequests, slide, isPreview = false) } albumCellBodyParent.isVisible = message.body.isNotEmpty() albumCellBodyText.text = message.body diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/KThumbnailView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/KThumbnailView.kt index 1710444c84..b070264944 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/KThumbnailView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/KThumbnailView.kt @@ -7,7 +7,6 @@ import android.net.Uri import android.util.AttributeSet import android.view.View import android.widget.FrameLayout -import android.widget.ProgressBar import androidx.core.view.isVisible import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.load.resource.bitmap.CenterCrop @@ -18,12 +17,10 @@ import kotlinx.android.synthetic.main.thumbnail_view.view.* import network.loki.messenger.R import org.session.libsession.messaging.sending_receiving.attachments.AttachmentTransferProgress import org.session.libsession.utilities.Util.equals -import org.session.libsession.utilities.ViewUtil import org.session.libsignal.utilities.ListenableFuture import org.session.libsignal.utilities.SettableFuture import org.thoughtcrime.securesms.components.GlideBitmapListeningTarget import org.thoughtcrime.securesms.components.GlideDrawableListeningTarget -import org.thoughtcrime.securesms.components.TransferControlView import org.thoughtcrime.securesms.mms.* import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri @@ -43,7 +40,6 @@ open class KThumbnailView: FrameLayout { private val playOverlay by lazy { play_overlay } private val captionIcon by lazy { thumbnail_caption_icon } val loadIndicator: View by lazy { thumbnail_load_indicator } - private val transferControls by lazy { ViewUtil.inflateStub(this, R.id.transfer_controls_stub) } private val dimensDelegate = ThumbnailDimensDelegate() @@ -88,23 +84,16 @@ open class KThumbnailView: FrameLayout { // endregion // region Interaction - fun setImageResource(glide: GlideRequests, slide: Slide, showControls: Boolean, isPreview: Boolean): ListenableFuture { - return setImageResource(glide, slide, showControls, isPreview, 0, 0) + fun setImageResource(glide: GlideRequests, slide: Slide, isPreview: Boolean): ListenableFuture { + return setImageResource(glide, slide, isPreview, 0, 0) } fun setImageResource(glide: GlideRequests, slide: Slide, - showControls: Boolean, isPreview: Boolean, - naturalWidth: Int, naturalHeight: Int): ListenableFuture { + isPreview: Boolean, naturalWidth: Int, + naturalHeight: Int): ListenableFuture { val currentSlide = this.slide - if (showControls) { - transferControls.setSlide(slide) -// transferControls.setDownloadClickListener() TODO: re-add this - } else { - transferControls.isVisible = false - } - playOverlay.isVisible = (slide.thumbnailUri != null && slide.hasPlayOverlay() && (slide.transferState == AttachmentTransferProgress.TRANSFER_PROGRESS_DONE || isPreview)) @@ -182,15 +171,12 @@ open class KThumbnailView: FrameLayout { open fun clear(glideRequests: GlideRequests) { glideRequests.clear(image) - transferControls.clear() slide = null } fun setImageResource(glideRequests: GlideRequests, uri: Uri): ListenableFuture { val future = SettableFuture() - transferControls.isVisible = false - var request: GlideRequest = glideRequests.load(DecryptableUri(uri)) .diskCacheStrategy(DiskCacheStrategy.NONE) .transition(DrawableTransitionOptions.withCrossFade())