diff --git a/app/src/main/java/org/thoughtcrime/securesms/BindableConversationItem.java b/app/src/main/java/org/thoughtcrime/securesms/BindableConversationItem.java index ba376430a..a74f4fd0d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/BindableConversationItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/BindableConversationItem.java @@ -10,10 +10,11 @@ import androidx.lifecycle.Observer; import org.thoughtcrime.securesms.components.voice.VoiceNotePlaybackState; import org.thoughtcrime.securesms.contactshare.Contact; +import org.thoughtcrime.securesms.conversation.ConversationItem; +import org.thoughtcrime.securesms.conversation.ConversationItemDisplayMode; import org.thoughtcrime.securesms.conversation.ConversationMessage; import org.thoughtcrime.securesms.conversation.colors.Colorizable; import org.thoughtcrime.securesms.conversation.colors.Colorizer; -import org.thoughtcrime.securesms.conversation.ConversationItemDisplayMode; import org.thoughtcrime.securesms.conversation.mutiselect.MultiselectPart; import org.thoughtcrime.securesms.conversation.mutiselect.Multiselectable; import org.thoughtcrime.securesms.database.model.InMemoryMessageRecord; @@ -23,6 +24,7 @@ import org.thoughtcrime.securesms.giph.mp4.GiphyMp4Playable; import org.thoughtcrime.securesms.groups.GroupId; import org.thoughtcrime.securesms.groups.GroupMigrationMembershipChange; import org.thoughtcrime.securesms.linkpreview.LinkPreview; +import org.thoughtcrime.securesms.mediapreview.MediaIntentFactory; import org.thoughtcrime.securesms.mms.GlideRequests; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientId; @@ -113,5 +115,7 @@ public interface BindableConversationItem extends Unbindable, GiphyMp4Playable, void onViewGiftBadgeClicked(@NonNull MessageRecord messageRecord); void onGiftBadgeRevealed(@NonNull MessageRecord messageRecord); + + void goToMediaPreview(ConversationItem parent, View sharedElement, MediaIntentFactory.MediaPreviewArgs args); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/ThreadPhotoRailView.java b/app/src/main/java/org/thoughtcrime/securesms/components/ThreadPhotoRailView.java index 7c16f9f8c..9cf0d7a48 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/ThreadPhotoRailView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/ThreadPhotoRailView.java @@ -97,7 +97,7 @@ public class ThreadPhotoRailView extends FrameLayout { } imageView.setOnClickListener(v -> { - if (clickedListener != null) clickedListener.onItemClicked(mediaRecord); + if (clickedListener != null) clickedListener.onItemClicked(imageView, mediaRecord); }); } @@ -118,6 +118,6 @@ public class ThreadPhotoRailView extends FrameLayout { } public interface OnItemClickedListener { - void onItemClicked(MediaTable.MediaRecord mediaRecord); + void onItemClicked(View itemView, MediaTable.MediaRecord mediaRecord); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsActivity.kt index a6f2b8c05..efa1196d5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsActivity.kt @@ -8,6 +8,7 @@ import android.view.View import androidx.core.app.ActivityCompat import androidx.core.app.ActivityOptionsCompat import androidx.core.util.Pair +import com.google.android.material.transition.platform.MaterialContainerTransformSharedElementCallback import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.components.settings.DSLSettingsActivity import org.thoughtcrime.securesms.groups.GroupId @@ -22,6 +23,7 @@ class ConversationSettingsActivity : DSLSettingsActivity(), ConversationSettings override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) { ActivityCompat.postponeEnterTransition(this) + setExitSharedElementCallback(MaterialContainerTransformSharedElementCallback()) super.onCreate(savedInstanceState, ready) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsFragment.kt index 2e5e5535f..5eec0e595 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsFragment.kt @@ -1,5 +1,6 @@ package org.thoughtcrime.securesms.components.settings.conversation +import android.app.ActivityOptions import android.content.ActivityNotFoundException import android.content.Context import android.content.Intent @@ -528,10 +529,13 @@ class ConversationSettingsFragment : DSLSettingsFragment( SharedMediaPreference.Model( mediaCursor = state.sharedMedia, mediaIds = state.sharedMediaIds, - onMediaRecordClick = { mediaRecord, isLtr -> + onMediaRecordClick = { view, mediaRecord, isLtr -> + view.transitionName = "thumb" + val options = ActivityOptions.makeSceneTransitionAnimation(requireActivity(), view, "thumb") startActivityForResult( MediaIntentFactory.intentFromMediaRecord(requireContext(), mediaRecord, isLtr, allMediaInRail = true), - REQUEST_CODE_RETURN_FROM_MEDIA + REQUEST_CODE_RETURN_FROM_MEDIA, + options.toBundle() ) } ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/preferences/SharedMediaPreference.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/preferences/SharedMediaPreference.kt index 27870f438..484e2be18 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/preferences/SharedMediaPreference.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/preferences/SharedMediaPreference.kt @@ -24,7 +24,7 @@ object SharedMediaPreference { class Model( val mediaCursor: Cursor, val mediaIds: List, - val onMediaRecordClick: (MediaTable.MediaRecord, Boolean) -> Unit + val onMediaRecordClick: (View, MediaTable.MediaRecord, Boolean) -> Unit ) : PreferenceModel() { override fun areItemsTheSame(newItem: Model): Boolean { return true @@ -42,8 +42,8 @@ object SharedMediaPreference { override fun bind(model: Model) { rail.setCursor(GlideApp.with(rail), model.mediaCursor) - rail.setListener { - model.onMediaRecordClick(it, ViewUtil.isLtr(rail)) + rail.setListener { v, m -> + model.onMediaRecordClick(v, m, ViewUtil.isLtr(rail)) } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.kt index 9ca4c97a0..b88fdd5d8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.kt @@ -4,6 +4,7 @@ import android.content.Intent import android.os.Bundle import android.view.MotionEvent import android.view.View +import android.view.Window import androidx.appcompat.content.res.AppCompatResources import androidx.appcompat.widget.Toolbar import io.reactivex.rxjava3.subjects.PublishSubject @@ -34,6 +35,8 @@ open class ConversationActivity : PassphraseRequiredActivity(), ConversationPare } override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) { + window.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS) + if (savedInstanceState != null) { shareDataTimestamp = savedInstanceState.getLong(STATE_WATERMARK, -1L) } else if (intent.flags and Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY != 0) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java index 1e6bbaf0b..2d89f4bd0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java @@ -21,6 +21,7 @@ import android.animation.Animator; import android.animation.LayoutTransition; import android.animation.ValueAnimator; import android.annotation.SuppressLint; +import android.app.ActivityOptions; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; @@ -38,6 +39,7 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; +import android.view.Window; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.widget.FrameLayout; @@ -68,6 +70,7 @@ import com.annimon.stream.Collectors; import com.annimon.stream.Stream; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.snackbar.Snackbar; +import com.google.android.material.transition.platform.MaterialContainerTransformSharedElementCallback; import org.jetbrains.annotations.NotNull; import org.signal.core.util.DimensionUnit; @@ -138,6 +141,8 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.linkpreview.LinkPreview; import org.thoughtcrime.securesms.longmessage.LongMessageFragment; import org.thoughtcrime.securesms.main.Material3OnScrollHelperBinder; +import org.thoughtcrime.securesms.mediapreview.MediaIntentFactory; +import org.thoughtcrime.securesms.mediapreview.MediaPreviewV2Activity; import org.thoughtcrime.securesms.messagedetails.MessageDetailsFragment; import org.thoughtcrime.securesms.messagerequests.MessageRequestState; import org.thoughtcrime.securesms.messagerequests.MessageRequestViewModel; @@ -2100,6 +2105,24 @@ public class ConversationFragment extends LoggingFragment implements Multiselect } } + @Override + public void goToMediaPreview(ConversationItem parent, View sharedElement, MediaIntentFactory.MediaPreviewArgs args) { + if (args.isVideoGif()) { + int adapterPosition = list.getChildAdapterPosition(parent); + GiphyMp4ProjectionPlayerHolder holder = giphyMp4ProjectionRecycler.getCurrentHolder(adapterPosition); + + if (holder != null) { + parent.showProjectionArea(); + holder.hide(); + } + } + + sharedElement.setTransitionName(MediaPreviewV2Activity.SHARED_ELEMENT_TRANSITION_NAME); + requireActivity().setExitSharedElementCallback(new MaterialContainerTransformSharedElementCallback()); + ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(requireActivity(), sharedElement, MediaPreviewV2Activity.SHARED_ELEMENT_TRANSITION_NAME); + requireActivity().startActivity(MediaIntentFactory.create(requireActivity(), args), options.toBundle()); + } + @Override public void onActivatePaymentsClicked() { Intent intent = new Intent(requireContext(), PaymentsActivity.class); diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java index 4eb0613e9..aa84e8f65 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java @@ -163,7 +163,6 @@ import kotlin.jvm.functions.Function1; * thread. Used by ComposeMessageActivity's ListActivity via a ConversationAdapter. * * @author Moxie Marlinspike - * */ public final class ConversationItem extends RelativeLayout implements BindableConversationItem, @@ -172,11 +171,11 @@ public final class ConversationItem extends RelativeLayout implements BindableCo { private static final String TAG = Log.tag(ConversationItem.class); - private static final int MAX_MEASURE_CALLS = 3; + private static final int MAX_MEASURE_CALLS = 3; private static final Rect SWIPE_RECT = new Rect(); - public static final float LONG_PRESS_SCALE_FACTOR = 0.95f; + public static final float LONG_PRESS_SCALE_FACTOR = 0.95f; private static final int SHRINK_BUBBLE_DELAY_MILLIS = 100; private static final long MAX_CLUSTERING_TIME_DIFF = TimeUnit.MINUTES.toMillis(3); private static final int CONDENSED_MODE_MAX_LINES = 3; @@ -193,24 +192,24 @@ public final class ConversationItem extends RelativeLayout implements BindableCo private Optional previousMessage; private ConversationItemDisplayMode displayMode; - protected ConversationItemBodyBubble bodyBubble; - protected View reply; - protected View replyIcon; + protected ConversationItemBodyBubble bodyBubble; + protected View reply; + protected View replyIcon; @Nullable protected ViewGroup contactPhotoHolder; @Nullable private QuoteView quoteView; - private EmojiTextView bodyText; - private ConversationItemFooter footer; + private EmojiTextView bodyText; + private ConversationItemFooter footer; @Nullable private ConversationItemFooter stickerFooter; @Nullable private TextView groupSender; @Nullable private View groupSenderHolder; - private AvatarImageView contactPhoto; - private AlertView alertView; - protected ReactionsConversationView reactionsView; - protected BadgeImageView badgeImageView; - private View storyReactionLabelWrapper; - private TextView storyReactionLabel; - protected View quotedIndicator; - protected View scheduledIndicator; + private AvatarImageView contactPhoto; + private AlertView alertView; + protected ReactionsConversationView reactionsView; + protected BadgeImageView badgeImageView; + private View storyReactionLabelWrapper; + private TextView storyReactionLabel; + protected View quotedIndicator; + protected View scheduledIndicator; private @NonNull Set batchSelected = new HashSet<>(); private @NonNull Outliner outliner = new Outliner(); @@ -308,34 +307,34 @@ public final class ConversationItem extends RelativeLayout implements BindableCo initializeAttributes(); - this.bodyText = findViewById(R.id.conversation_item_body); - this.footer = findViewById(R.id.conversation_item_footer); - this.stickerFooter = findViewById(R.id.conversation_item_sticker_footer); - this.groupSender = findViewById(R.id.group_message_sender); - this.alertView = findViewById(R.id.indicators_parent); - this.contactPhoto = findViewById(R.id.contact_photo); - this.contactPhotoHolder = findViewById(R.id.contact_photo_container); - this.bodyBubble = findViewById(R.id.body_bubble); + this.bodyText = findViewById(R.id.conversation_item_body); + this.footer = findViewById(R.id.conversation_item_footer); + this.stickerFooter = findViewById(R.id.conversation_item_sticker_footer); + this.groupSender = findViewById(R.id.group_message_sender); + this.alertView = findViewById(R.id.indicators_parent); + this.contactPhoto = findViewById(R.id.contact_photo); + this.contactPhotoHolder = findViewById(R.id.contact_photo_container); + this.bodyBubble = findViewById(R.id.body_bubble); this.mediaThumbnailStub = new NullableStub<>(findViewById(R.id.image_view_stub)); - this.audioViewStub = new Stub<>(findViewById(R.id.audio_view_stub)); - this.documentViewStub = new Stub<>(findViewById(R.id.document_view_stub)); - this.sharedContactStub = new Stub<>(findViewById(R.id.shared_contact_view_stub)); - this.linkPreviewStub = new Stub<>(findViewById(R.id.link_preview_stub)); - this.stickerStub = new Stub<>(findViewById(R.id.sticker_view_stub)); - this.revealableStub = new Stub<>(findViewById(R.id.revealable_view_stub)); - this.callToActionStub = ViewUtil.findStubById(this, R.id.conversation_item_call_to_action_stub); - this.groupSenderHolder = findViewById(R.id.group_sender_holder); - this.quoteView = findViewById(R.id.quote_view); - this.reply = findViewById(R.id.reply_icon_wrapper); - this.replyIcon = findViewById(R.id.reply_icon); - this.reactionsView = findViewById(R.id.reactions_view); - this.badgeImageView = findViewById(R.id.badge); - this.storyReactionLabelWrapper = findViewById(R.id.story_reacted_label_holder); - this.storyReactionLabel = findViewById(R.id.story_reacted_label); - this.giftViewStub = new Stub<>(findViewById(R.id.gift_view_stub)); - this.quotedIndicator = findViewById(R.id.quoted_indicator); - this.paymentViewStub = new Stub<>(findViewById(R.id.payment_view_stub)); - this.scheduledIndicator = findViewById(R.id.scheduled_indicator); + this.audioViewStub = new Stub<>(findViewById(R.id.audio_view_stub)); + this.documentViewStub = new Stub<>(findViewById(R.id.document_view_stub)); + this.sharedContactStub = new Stub<>(findViewById(R.id.shared_contact_view_stub)); + this.linkPreviewStub = new Stub<>(findViewById(R.id.link_preview_stub)); + this.stickerStub = new Stub<>(findViewById(R.id.sticker_view_stub)); + this.revealableStub = new Stub<>(findViewById(R.id.revealable_view_stub)); + this.callToActionStub = ViewUtil.findStubById(this, R.id.conversation_item_call_to_action_stub); + this.groupSenderHolder = findViewById(R.id.group_sender_holder); + this.quoteView = findViewById(R.id.quote_view); + this.reply = findViewById(R.id.reply_icon_wrapper); + this.replyIcon = findViewById(R.id.reply_icon); + this.reactionsView = findViewById(R.id.reactions_view); + this.badgeImageView = findViewById(R.id.badge); + this.storyReactionLabelWrapper = findViewById(R.id.story_reacted_label_holder); + this.storyReactionLabel = findViewById(R.id.story_reacted_label); + this.giftViewStub = new Stub<>(findViewById(R.id.gift_view_stub)); + this.quotedIndicator = findViewById(R.id.quoted_indicator); + this.paymentViewStub = new Stub<>(findViewById(R.id.payment_view_stub)); + this.scheduledIndicator = findViewById(R.id.scheduled_indicator); setOnClickListener(new ClickListener(null)); @@ -368,20 +367,20 @@ public final class ConversationItem extends RelativeLayout implements BindableCo conversationRecipient = conversationRecipient.resolve(); - this.conversationMessage = conversationMessage; - this.messageRecord = conversationMessage.getMessageRecord(); - this.nextMessageRecord = nextMessageRecord; - this.locale = locale; - this.glideRequests = glideRequests; - this.batchSelected = batchSelected; - this.conversationRecipient = conversationRecipient.live(); - this.groupThread = conversationRecipient.isGroup(); - this.recipient = messageRecord.getIndividualRecipient().live(); - this.canPlayContent = false; - this.mediaItem = null; - this.colorizer = colorizer; - this.displayMode = displayMode; - this.previousMessage = previousMessageRecord; + this.conversationMessage = conversationMessage; + this.messageRecord = conversationMessage.getMessageRecord(); + this.nextMessageRecord = nextMessageRecord; + this.locale = locale; + this.glideRequests = glideRequests; + this.batchSelected = batchSelected; + this.conversationRecipient = conversationRecipient.live(); + this.groupThread = conversationRecipient.isGroup(); + this.recipient = messageRecord.getIndividualRecipient().live(); + this.canPlayContent = false; + this.mediaItem = null; + this.colorizer = colorizer; + this.displayMode = displayMode; + this.previousMessage = previousMessageRecord; this.recipient.observeForever(this); this.conversationRecipient.observeForever(this); @@ -505,7 +504,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo if (quoteWidth != availableWidth) { quoteView.getLayoutParams().width = availableWidth; - needsMeasure = true; + needsMeasure = true; } } @@ -513,15 +512,15 @@ public final class ConversationItem extends RelativeLayout implements BindableCo int defaultBottomMargin = readDimen(R.dimen.message_bubble_bottom_padding); int collapsedBottomMargin = readDimen(R.dimen.message_bubble_collapsed_bottom_padding); - if (!updatingFooter && - getActiveFooter(messageRecord) == footer && - !hasAudio(messageRecord) && - !isStoryReaction(messageRecord) && + if (!updatingFooter && + getActiveFooter(messageRecord) == footer && + !hasAudio(messageRecord) && + !isStoryReaction(messageRecord) && isFooterVisible(messageRecord, nextMessageRecord, groupThread) && - !bodyText.isJumbomoji() && - conversationMessage.getBottomButton() == null && - !StringUtil.hasMixedTextDirection(bodyText.getText()) && - !messageRecord.isRemoteDelete() && + !bodyText.isJumbomoji() && + conversationMessage.getBottomButton() == null && + !StringUtil.hasMixedTextDirection(bodyText.getText()) && + !messageRecord.isRemoteDelete() && bodyText.getLastLineWidth() > 0) { TextView dateView = footer.getDateView(); @@ -570,7 +569,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo if (contactWidth != availableWidth) { sharedContactStub.get().getLayoutParams().width = availableWidth; - needsMeasure = true; + needsMeasure = true; } } @@ -580,13 +579,13 @@ public final class ConversationItem extends RelativeLayout implements BindableCo if (activeFooter.getVisibility() != GONE && activeFooter.getMeasuredWidth() != availableWidth) { activeFooter.getLayoutParams().width = availableWidth; - needsMeasure = true; + needsMeasure = true; } int desiredWidth = audioViewStub.get().getMeasuredWidth() + ViewUtil.getLeftMargin(audioViewStub.get()) + ViewUtil.getRightMargin(audioViewStub.get()); if (bodyBubble.getMeasuredWidth() != desiredWidth) { bodyBubble.getLayoutParams().width = desiredWidth; - needsMeasure = true; + needsMeasure = true; } } @@ -598,7 +597,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo Log.w(TAG, "Hit measure() cap of " + MAX_MEASURE_CALLS); } } else { - measureCalls = 0; + measureCalls = 0; updatingFooter = false; } } @@ -702,7 +701,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo private boolean isTouchBelowBoundary(@NonNull View child) { Projection childProjection = Projection.relativeToParent(this, child, null); - float childBoundary = childProjection.getY() + childProjection.getHeight(); + float childBoundary = childProjection.getY() + childProjection.getHeight(); return lastYDownRelativeToThis > childBoundary; } @@ -734,14 +733,14 @@ public final class ConversationItem extends RelativeLayout implements BindableCo private static int getProjectionTop(@NonNull View child) { Projection projection = Projection.relativeToViewRoot(child, null); - int y = (int) projection.getY(); + int y = (int) projection.getY(); projection.release(); return y; } private static int getProjectionBottom(@NonNull View child) { Projection projection = Projection.relativeToViewRoot(child, null); - int bottom = (int) projection.getY() + projection.getHeight(); + int bottom = (int) projection.getY() + projection.getHeight(); projection.release(); return bottom; } @@ -953,7 +952,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo return MessageRecordUtil.hasSharedContact(messageRecord); } - private boolean hasLinkPreview(MessageRecord messageRecord) { + private boolean hasLinkPreview(MessageRecord messageRecord) { return MessageRecordUtil.hasLinkPreview(messageRecord); } @@ -979,13 +978,13 @@ public final class ConversationItem extends RelativeLayout implements BindableCo bodyText.setMovementMethod(LongClickMovementMethod.getInstance(getContext())); if (messageRecord.isRemoteDelete()) { - String deletedMessage = context.getString(messageRecord.isOutgoing() ? R.string.ConversationItem_you_deleted_this_message : R.string.ConversationItem_this_message_was_deleted); - SpannableString italics = new SpannableString(deletedMessage); + String deletedMessage = context.getString(messageRecord.isOutgoing() ? R.string.ConversationItem_you_deleted_this_message : R.string.ConversationItem_this_message_was_deleted); + SpannableString italics = new SpannableString(deletedMessage); italics.setSpan(new StyleSpan(android.graphics.Typeface.ITALIC), 0, deletedMessage.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); italics.setSpan(new ForegroundColorSpan(ContextCompat.getColor(context, R.color.signal_text_primary)), - 0, - deletedMessage.length(), - Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + 0, + deletedMessage.length(), + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); bodyText.setText(italics); bodyText.setVisibility(View.VISIBLE); @@ -1036,13 +1035,13 @@ public final class ConversationItem extends RelativeLayout implements BindableCo } } - private void setMediaAttributes(@NonNull MessageRecord messageRecord, - @NonNull Optional previousRecord, - @NonNull Optional nextRecord, - boolean isGroupThread, - boolean hasWallpaper, - boolean messageRequestAccepted, - boolean allowedToPlayInline) + private void setMediaAttributes(@NonNull MessageRecord messageRecord, + @NonNull Optional previousRecord, + @NonNull Optional nextRecord, + boolean isGroupThread, + boolean hasWallpaper, + boolean messageRequestAccepted, + boolean allowedToPlayInline) { boolean showControls = !messageRecord.isFailed() && !MessageRecordUtil.isScheduled(messageRecord); @@ -1061,12 +1060,12 @@ public final class ConversationItem extends RelativeLayout implements BindableCo if (isViewOnceMessage(messageRecord) && !messageRecord.isRemoteDelete()) { revealableStub.get().setVisibility(VISIBLE); if (mediaThumbnailStub.resolved()) mediaThumbnailStub.require().setVisibility(View.GONE); - if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE); - if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE); - if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE); - if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE); - if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE); - if (giftViewStub.resolved()) giftViewStub.get().setVisibility(View.GONE); + if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE); + if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE); + if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE); + if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE); + if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE); + if (giftViewStub.resolved()) giftViewStub.get().setVisibility(View.GONE); paymentViewStub.setVisibility(View.GONE); revealableStub.get().setMessage((MmsMessageRecord) messageRecord, hasWallpaper); @@ -1078,13 +1077,13 @@ public final class ConversationItem extends RelativeLayout implements BindableCo footer.setVisibility(VISIBLE); } else if (hasSharedContact(messageRecord)) { sharedContactStub.get().setVisibility(VISIBLE); - if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE); + if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE); if (mediaThumbnailStub.resolved()) mediaThumbnailStub.require().setVisibility(View.GONE); - if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE); - if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE); - if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE); - if (revealableStub.resolved()) revealableStub.get().setVisibility(View.GONE); - if (giftViewStub.resolved()) giftViewStub.get().setVisibility(View.GONE); + if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE); + if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE); + if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE); + if (revealableStub.resolved()) revealableStub.get().setVisibility(View.GONE); + if (giftViewStub.resolved()) giftViewStub.get().setVisibility(View.GONE); paymentViewStub.setVisibility(View.GONE); sharedContactStub.get().setContact(((MediaMmsMessageRecord) messageRecord).getSharedContacts().get(0), glideRequests, locale); @@ -1099,13 +1098,13 @@ public final class ConversationItem extends RelativeLayout implements BindableCo footer.setVisibility(GONE); } else if (hasLinkPreview(messageRecord) && messageRequestAccepted) { linkPreviewStub.get().setVisibility(View.VISIBLE); - if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE); + if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE); if (mediaThumbnailStub.resolved()) mediaThumbnailStub.require().setVisibility(View.GONE); - if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE); - if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE); - if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE); - if (revealableStub.resolved()) revealableStub.get().setVisibility(View.GONE); - if (giftViewStub.resolved()) giftViewStub.get().setVisibility(View.GONE); + if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE); + if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE); + if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE); + if (revealableStub.resolved()) revealableStub.get().setVisibility(View.GONE); + if (giftViewStub.resolved()) giftViewStub.get().setVisibility(View.GONE); paymentViewStub.setVisibility(View.GONE); //noinspection ConstantConditions @@ -1148,12 +1147,12 @@ public final class ConversationItem extends RelativeLayout implements BindableCo } else if (hasAudio(messageRecord)) { audioViewStub.get().setVisibility(View.VISIBLE); if (mediaThumbnailStub.resolved()) mediaThumbnailStub.require().setVisibility(View.GONE); - if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE); - if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE); - if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE); - if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE); - if (revealableStub.resolved()) revealableStub.get().setVisibility(View.GONE); - if (giftViewStub.resolved()) giftViewStub.get().setVisibility(View.GONE); + if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE); + if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE); + if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE); + if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE); + if (revealableStub.resolved()) revealableStub.get().setVisibility(View.GONE); + if (giftViewStub.resolved()) giftViewStub.get().setVisibility(View.GONE); paymentViewStub.setVisibility(View.GONE); audioViewStub.get().setAudio(Objects.requireNonNull(((MediaMmsMessageRecord) messageRecord).getSlideDeck().getAudioSlide()), new AudioViewCallbacks(), showControls, true); @@ -1175,12 +1174,12 @@ public final class ConversationItem extends RelativeLayout implements BindableCo } else if (hasDocument(messageRecord)) { documentViewStub.get().setVisibility(View.VISIBLE); if (mediaThumbnailStub.resolved()) mediaThumbnailStub.require().setVisibility(View.GONE); - if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE); - if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE); - if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE); - if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE); - if (revealableStub.resolved()) revealableStub.get().setVisibility(View.GONE); - if (giftViewStub.resolved()) giftViewStub.get().setVisibility(View.GONE); + if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE); + if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE); + if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE); + if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE); + if (revealableStub.resolved()) revealableStub.get().setVisibility(View.GONE); + if (giftViewStub.resolved()) giftViewStub.get().setVisibility(View.GONE); paymentViewStub.setVisibility(View.GONE); //noinspection ConstantConditions @@ -1203,12 +1202,12 @@ public final class ConversationItem extends RelativeLayout implements BindableCo stickerStub.get().setVisibility(View.VISIBLE); if (mediaThumbnailStub.resolved()) mediaThumbnailStub.require().setVisibility(View.GONE); - if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE); - if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE); - if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE); - if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE); - if (revealableStub.resolved()) revealableStub.get().setVisibility(View.GONE); - if (giftViewStub.resolved()) giftViewStub.get().setVisibility(View.GONE); + if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE); + if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE); + if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE); + if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE); + if (revealableStub.resolved()) revealableStub.get().setVisibility(View.GONE); + if (giftViewStub.resolved()) giftViewStub.get().setVisibility(View.GONE); paymentViewStub.setVisibility(View.GONE); if (hasSticker(messageRecord)) { @@ -1233,13 +1232,13 @@ public final class ConversationItem extends RelativeLayout implements BindableCo bodyBubble.setBackgroundColor(Color.TRANSPARENT); } else if (hasThumbnail(messageRecord)) { mediaThumbnailStub.require().setVisibility(View.VISIBLE); - if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE); - if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE); + if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE); + if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE); if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE); - if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE); - if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE); - if (revealableStub.resolved()) revealableStub.get().setVisibility(View.GONE); - if (giftViewStub.resolved()) giftViewStub.get().setVisibility(View.GONE); + if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE); + if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE); + if (revealableStub.resolved()) revealableStub.get().setVisibility(View.GONE); + if (giftViewStub.resolved()) giftViewStub.get().setVisibility(View.GONE); paymentViewStub.setVisibility(View.GONE); List thumbnailSlides = ((MmsMessageRecord) messageRecord).getSlideDeck().getThumbnailSlides(); @@ -1272,8 +1271,8 @@ public final class ConversationItem extends RelativeLayout implements BindableCo footer.setVisibility(VISIBLE); - if (thumbnailSlides.size() == 1 && - thumbnailSlides.get(0).isVideoGif() && + if (thumbnailSlides.size() == 1 && + thumbnailSlides.get(0).isVideoGif() && thumbnailSlides.get(0) instanceof VideoSlide) { Uri uri = thumbnailSlides.get(0).getUri(); @@ -1288,12 +1287,12 @@ public final class ConversationItem extends RelativeLayout implements BindableCo } else if (isGiftMessage(messageRecord)) { if (mediaThumbnailStub.resolved()) mediaThumbnailStub.require().setVisibility(GONE); - if (audioViewStub.resolved()) audioViewStub.get().setVisibility(GONE); - if (documentViewStub.resolved()) documentViewStub.get().setVisibility(GONE); - if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE); - if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE); - if (stickerStub.resolved()) stickerStub.get().setVisibility(GONE); - if (revealableStub.resolved()) revealableStub.get().setVisibility(GONE); + if (audioViewStub.resolved()) audioViewStub.get().setVisibility(GONE); + if (documentViewStub.resolved()) documentViewStub.get().setVisibility(GONE); + if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE); + if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE); + if (stickerStub.resolved()) stickerStub.get().setVisibility(GONE); + if (revealableStub.resolved()) revealableStub.get().setVisibility(GONE); paymentViewStub.setVisibility(View.GONE); MmsMessageRecord mmsMessageRecord = (MmsMessageRecord) messageRecord; @@ -1303,13 +1302,13 @@ public final class ConversationItem extends RelativeLayout implements BindableCo footer.setVisibility(VISIBLE); } else if (messageRecord.isPaymentNotification()) { if (mediaThumbnailStub.resolved()) mediaThumbnailStub.require().setVisibility(GONE); - if (audioViewStub.resolved()) audioViewStub.get().setVisibility(GONE); - if (documentViewStub.resolved()) documentViewStub.get().setVisibility(GONE); - if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE); - if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE); - if (stickerStub.resolved()) stickerStub.get().setVisibility(GONE); - if (revealableStub.resolved()) revealableStub.get().setVisibility(GONE); - if (giftViewStub.resolved()) giftViewStub.get().setVisibility(View.GONE); + if (audioViewStub.resolved()) audioViewStub.get().setVisibility(GONE); + if (documentViewStub.resolved()) documentViewStub.get().setVisibility(GONE); + if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE); + if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE); + if (stickerStub.resolved()) stickerStub.get().setVisibility(GONE); + if (revealableStub.resolved()) revealableStub.get().setVisibility(GONE); + if (giftViewStub.resolved()) giftViewStub.get().setVisibility(View.GONE); MediaMmsMessageRecord mediaMmsMessageRecord = (MediaMmsMessageRecord) messageRecord; @@ -1319,13 +1318,13 @@ public final class ConversationItem extends RelativeLayout implements BindableCo footer.setVisibility(VISIBLE); } else { if (mediaThumbnailStub.resolved()) mediaThumbnailStub.require().setVisibility(View.GONE); - if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE); - if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE); - if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE); - if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE); - if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE); - if (revealableStub.resolved()) revealableStub.get().setVisibility(View.GONE); - if (giftViewStub.resolved()) giftViewStub.get().setVisibility(View.GONE); + if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE); + if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE); + if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE); + if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE); + if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE); + if (revealableStub.resolved()) revealableStub.get().setVisibility(View.GONE); + if (giftViewStub.resolved()) giftViewStub.get().setVisibility(View.GONE); paymentViewStub.setVisibility(View.GONE); ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); @@ -1342,7 +1341,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo } private void updateRevealableMargins(MessageRecord messageRecord, Optional previous, Optional next, boolean isGroupThread) { - int bigMargin = readDimen(R.dimen.message_bubble_revealable_padding); + int bigMargin = readDimen(R.dimen.message_bubble_revealable_padding); int smallMargin = readDimen(R.dimen.message_bubble_top_padding); //noinspection ConstantConditions @@ -1359,10 +1358,10 @@ public final class ConversationItem extends RelativeLayout implements BindableCo } } - private void setThumbnailCorners(@NonNull MessageRecord current, + private void setThumbnailCorners(@NonNull MessageRecord current, @NonNull Optional previous, @NonNull Optional next, - boolean isGroupThread) + boolean isGroupThread) { int defaultRadius = readDimen(R.dimen.message_corner_radius); int collapseRadius = readDimen(R.dimen.message_corner_collapse_radius); @@ -1427,13 +1426,13 @@ public final class ConversationItem extends RelativeLayout implements BindableCo } private void setSharedContactCorners(@NonNull MessageRecord current, @NonNull Optional previous, @NonNull Optional next, boolean isGroupThread) { - if (messageRecord.isDisplayBodyEmpty(getContext())){ + if (messageRecord.isDisplayBodyEmpty(getContext())) { if (isSingularMessage(current, previous, next, isGroupThread) || isEndOfMessageCluster(current, next, isGroupThread)) { - sharedContactStub.get().setSingularStyle(); + sharedContactStub.get().setSingularStyle(); } else if (current.isOutgoing()) { - sharedContactStub.get().setClusteredOutgoingStyle(); + sharedContactStub.get().setClusteredOutgoingStyle(); } else { - sharedContactStub.get().setClusteredIncomingStyle(); + sharedContactStub.get().setClusteredIncomingStyle(); } } } @@ -1542,7 +1541,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo if (quoteView == null) { throw new AssertionError(); } - Quote quote = ((MediaMmsMessageRecord)current).getQuote(); + Quote quote = ((MediaMmsMessageRecord) current).getQuote(); if (((MediaMmsMessageRecord) current).getParentStoryId() != null) { quoteView.setMessageType(current.isOutgoing() ? QuoteView.MessageType.STORY_REPLY_OUTGOING : QuoteView.MessageType.STORY_REPLY_INCOMING); @@ -1656,11 +1655,10 @@ public final class ConversationItem extends RelativeLayout implements BindableCo footer.setVisibility(GONE); ViewUtil.setVisibilityIfNonNull(stickerFooter, GONE); - if (sharedContactStub.resolved()) sharedContactStub.get().getFooter().setVisibility(GONE); + if (sharedContactStub.resolved()) sharedContactStub.get().getFooter().setVisibility(GONE); if (mediaThumbnailStub.resolved()) mediaThumbnailStub.require().getFooter().setVisibility(GONE); - if (isFooterVisible(current, next, isGroupThread)) - { + if (isFooterVisible(current, next, isGroupThread)) { ConversationItemFooter activeFooter = getActiveFooter(current); activeFooter.setVisibility(VISIBLE); activeFooter.setMessageRecord(current, locale); @@ -1672,9 +1670,9 @@ public final class ConversationItem extends RelativeLayout implements BindableCo activeFooter.setIconColor(ContextCompat.getColor(context, R.color.conversation_item_sent_text_secondary_color)); activeFooter.setRevealDotColor(ContextCompat.getColor(context, R.color.conversation_item_sent_text_secondary_color)); } else { - activeFooter.enableBubbleBackground(R.drawable.wallpaper_bubble_background_tintable_11, getDefaultBubbleColor(hasWallpaper)); + activeFooter.enableBubbleBackground(R.drawable.wallpaper_bubble_background_tintable_11, getDefaultBubbleColor(hasWallpaper)); } - } else if (hasNoBubble(messageRecord)){ + } else if (hasNoBubble(messageRecord)) { activeFooter.disableBubbleBackground(); activeFooter.setTextColor(ContextCompat.getColor(context, R.color.signal_text_secondary)); activeFooter.setIconColor(ContextCompat.getColor(context, R.color.signal_icon_tint_secondary)); @@ -1754,11 +1752,11 @@ public final class ConversationItem extends RelativeLayout implements BindableCo } private boolean shouldInterceptClicks(MessageRecord messageRecord) { - return batchSelected.isEmpty() && - ((messageRecord.isFailed() && !messageRecord.isMmsNotification()) || - (messageRecord.isRateLimited() && SignalStore.rateLimit().needsRecaptcha()) || - messageRecord.isPendingInsecureSmsFallback() || - messageRecord.isBundleKeyExchange()); + return batchSelected.isEmpty() && + ((messageRecord.isFailed() && !messageRecord.isMmsNotification()) || + (messageRecord.isRateLimited() && SignalStore.rateLimit().needsRecaptcha()) || + messageRecord.isPendingInsecureSmsFallback() || + messageRecord.isBundleKeyExchange()); } @SuppressLint("SetTextI18n") @@ -1770,7 +1768,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo private void setGroupAuthorColor(@NonNull MessageRecord messageRecord, boolean hasWallpaper, @NonNull Colorizer colorizer) { if (groupSender != null) { - groupSender.setTextColor(colorizer.getIncomingGroupSenderColor(getContext(), messageRecord.getIndividualRecipient())); + groupSender.setTextColor(colorizer.getIncomingGroupSenderColor(getContext(), messageRecord.getIndividualRecipient())); } } @@ -1931,7 +1929,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo } private void setMessageSpacing(@NonNull Context context, @NonNull MessageRecord current, @NonNull Optional previous, @NonNull Optional next, boolean isGroupThread) { - int spacingTop = readDimen(context, R.dimen.conversation_vertical_message_spacing_collapse); + int spacingTop = readDimen(context, R.dimen.conversation_vertical_message_spacing_collapse); int spacingBottom = spacingTop; if (isStartOfMessageCluster(current, previous, isGroupThread)) { @@ -2077,9 +2075,9 @@ public final class ConversationItem extends RelativeLayout implements BindableCo colorizerProjections.clear(); if ((messageRecord.isOutgoing() || !outgoingOnly) && - !hasNoBubble(messageRecord) && + !hasNoBubble(messageRecord) && !messageRecord.isRemoteDelete() && - bodyBubbleCorners != null && + bodyBubbleCorners != null && bodyBubble.getVisibility() == VISIBLE) { Projection bodyBubbleToRoot = Projection.relativeToParent(coordinateRoot, bodyBubble, bodyBubbleCorners).translateX(bodyBubble.getTranslationX()); @@ -2138,13 +2136,13 @@ public final class ConversationItem extends RelativeLayout implements BindableCo } } - if ((messageRecord.isOutgoing() || !outgoingOnly) && + if ((messageRecord.isOutgoing() || !outgoingOnly) && hasNoBubble(messageRecord) && - hasWallpaper && + hasWallpaper && bodyBubble.getVisibility() == VISIBLE) { - ConversationItemFooter footer = getActiveFooter(messageRecord); - Projection footerProjection = footer.getProjection(coordinateRoot); + ConversationItemFooter footer = getActiveFooter(messageRecord); + Projection footerProjection = footer.getProjection(coordinateRoot); if (footerProjection != null) { colorizerProjections.add( footerProjection.translateX(bodyBubble.getTranslationX()) @@ -2327,7 +2325,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo for (Slide slide : slides) { ApplicationDependencies.getJobManager().add(new AttachmentDownloadJob(messageRecord.getId(), - ((DatabaseAttachment)slide.asAttachment()).getAttachmentId(), + ((DatabaseAttachment) slide.asAttachment()).getAttachmentId(), true)); } } @@ -2379,8 +2377,16 @@ public final class ConversationItem extends RelativeLayout implements BindableCo false, false, MediaTable.Sorting.Newest, - slide.isVideoGif()); - context.startActivity(MediaIntentFactory.create(context, args)); + slide.isVideoGif(), + new MediaIntentFactory.SharedElementArgs( + slide.asAttachment().getWidth(), + slide.asAttachment().getHeight(), + mediaThumbnailStub.require().getCorners().getTopLeft(), + mediaThumbnailStub.require().getCorners().getTopRight(), + mediaThumbnailStub.require().getCorners().getBottomRight(), + mediaThumbnailStub.require().getCorners().getBottomLeft() + )); + eventListener.goToMediaPreview(ConversationItem.this, mediaThumbnailStub.require(), args); } else if (slide.getUri() != null) { Log.i(TAG, "Clicked: " + slide.getUri() + " , " + slide.getContentType()); Uri publicUri = PartAuthority.getAttachmentPublicUri(slide.getUri()); @@ -2502,7 +2508,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo } @Override - public void updateDrawState(@NonNull TextPaint ds) { } + public void updateDrawState(@NonNull TextPaint ds) {} } private final class AudioPlaybackSpeedToggleListener implements PlaybackSpeedToggleTextView.PlaybackSpeedListener { @@ -2565,7 +2571,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo final int message; if (messageRecord.isMms()) title = R.string.ConversationItem_click_to_approve_unencrypted_mms_dialog_title; - else title = R.string.ConversationItem_click_to_approve_unencrypted_sms_dialog_title; + else title = R.string.ConversationItem_click_to_approve_unencrypted_sms_dialog_title; message = R.string.ConversationItem_click_to_approve_unencrypted_dialog_message; diff --git a/app/src/main/java/org/thoughtcrime/securesms/giph/mp4/GiphyMp4ProjectionRecycler.java b/app/src/main/java/org/thoughtcrime/securesms/giph/mp4/GiphyMp4ProjectionRecycler.java index d1cf6736b..09b1fe03f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/giph/mp4/GiphyMp4ProjectionRecycler.java +++ b/app/src/main/java/org/thoughtcrime/securesms/giph/mp4/GiphyMp4ProjectionRecycler.java @@ -114,7 +114,10 @@ public final class GiphyMp4ProjectionRecycler implements GiphyMp4PlaybackControl }); holder.playContent(giphyMp4Playable.getMediaItem(), giphyMp4Playable.getPlaybackPolicyEnforcer()); } else { + giphyMp4Playable.showProjectionArea(); + holder.setOnPlaybackReady(() -> { + holder.show(); giphyMp4Playable.hideProjectionArea(); parent.invalidate(); }); diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaGalleryAllAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaGalleryAllAdapter.java index 37a758e4a..6ed5db3fb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaGalleryAllAdapter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaGalleryAllAdapter.java @@ -345,7 +345,7 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter { } thumbnailView.setImageResource(glideRequests, slide, false, false); - thumbnailView.setOnClickListener(view -> itemClickListener.onMediaClicked(mediaRecord)); + thumbnailView.setOnClickListener(view -> itemClickListener.onMediaClicked(thumbnailView, mediaRecord)); thumbnailView.setOnLongClickListener(view -> onLongClick()); } @@ -411,7 +411,7 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter { line1.setText(fileName.orElse(fileTypeDescription)); line2.setText(getLine2(context, mediaRecord, slide)); - itemView.setOnClickListener(view -> itemClickListener.onMediaClicked(mediaRecord)); + itemView.setOnClickListener(view -> itemClickListener.onMediaClicked(getTransitionAnchor(), mediaRecord)); itemView.setOnLongClickListener(view -> onLongClick()); selectForMarque = () -> line1.setSelected(true); handler = new Handler(Looper.getMainLooper()); @@ -459,6 +459,10 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter { return fileName.orElse(null); } + protected @NonNull View getTransitionAnchor() { + return itemView; + } + private @NonNull String describe(@NonNull Recipient from, @NonNull Recipient thread) { if (from == Recipient.UNKNOWN && thread == Recipient.UNKNOWN) { return fileName.orElse(fileTypeDescription); @@ -541,8 +545,8 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter { audioView.setAudio((AudioSlide) slide, new AudioViewCallbacksAdapter(audioItemListener, mmsId), true, true); audioItemListener.registerPlaybackStateObserver(audioView.getPlaybackStateObserver()); - audioView.setOnClickListener(view -> itemClickListener.onMediaClicked(mediaRecord)); - itemView.setOnClickListener(view -> itemClickListener.onMediaClicked(mediaRecord)); + audioView.setOnClickListener(view -> itemClickListener.onMediaClicked(audioView, mediaRecord)); + itemView.setOnClickListener(view -> itemClickListener.onMediaClicked(audioView, mediaRecord)); } @Override @@ -584,10 +588,15 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter { super.bind(context, mediaRecord, slide); this.slide = slide; thumbnailView.setImageResource(glideRequests, slide, false, false); - thumbnailView.setOnClickListener(view -> itemClickListener.onMediaClicked(mediaRecord)); + thumbnailView.setOnClickListener(view -> itemClickListener.onMediaClicked(thumbnailView, mediaRecord)); thumbnailView.setOnLongClickListener(view -> onLongClick()); } + @Override + protected @NonNull View getTransitionAnchor() { + return thumbnailView; + } + @Override protected String getFileTypeDescription(@NonNull Context context, @NonNull Slide slide) { if (slide.hasVideo()) return context.getString(R.string.MediaOverviewActivity_video); @@ -648,7 +657,7 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter { } interface ItemClickListener { - void onMediaClicked(@NonNull MediaTable.MediaRecord mediaRecord); + void onMediaClicked(@NonNull View view, @NonNull MediaTable.MediaRecord mediaRecord); void onMediaLongClicked(MediaTable.MediaRecord mediaRecord); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaOverviewActivity.java b/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaOverviewActivity.java index 897dd8c15..b61bc7997 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaOverviewActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaOverviewActivity.java @@ -34,6 +34,7 @@ import androidx.viewpager.widget.ViewPager; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.tabs.TabLayout; +import com.google.android.material.transition.platform.MaterialContainerTransformSharedElementCallback; import org.signal.libsignal.protocol.util.Pair; import org.thoughtcrime.securesms.PassphraseRequiredActivity; @@ -91,6 +92,7 @@ public final class MediaOverviewActivity extends PassphraseRequiredActivity { @Override protected void onCreate(Bundle bundle, boolean ready) { + setExitSharedElementCallback(new MaterialContainerTransformSharedElementCallback()); setContentView(R.layout.media_overview_activity); initializeResources(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaOverviewPageFragment.java b/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaOverviewPageFragment.java index 438b9ede3..73ca6a3ad 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaOverviewPageFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaOverviewPageFragment.java @@ -1,11 +1,13 @@ package org.thoughtcrime.securesms.mediaoverview; +import android.app.ActivityOptions; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.content.res.Configuration; import android.net.Uri; import android.os.Bundle; +import android.util.Size; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; @@ -27,6 +29,7 @@ import androidx.recyclerview.widget.RecyclerView; import com.codewaves.stickyheadergrid.StickyHeaderGridLayoutManager; +import org.signal.core.util.DimensionUnit; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.attachments.DatabaseAttachment; @@ -38,6 +41,7 @@ import org.thoughtcrime.securesms.database.MediaTable; import org.thoughtcrime.securesms.database.loaders.GroupedThreadMediaLoader; import org.thoughtcrime.securesms.database.loaders.MediaLoader; import org.thoughtcrime.securesms.mediapreview.MediaIntentFactory; +import org.thoughtcrime.securesms.mediapreview.MediaPreviewV2Activity; import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.mms.PartAuthority; import org.thoughtcrime.securesms.util.BottomOffsetDecoration; @@ -196,11 +200,11 @@ public final class MediaOverviewPageFragment extends Fragment } @Override - public void onMediaClicked(@NonNull MediaTable.MediaRecord mediaRecord) { + public void onMediaClicked(@NonNull View view, @NonNull MediaTable.MediaRecord mediaRecord) { if (actionMode != null) { handleMediaMultiSelectClick(mediaRecord); } else { - handleMediaPreviewClick(mediaRecord); + handleMediaPreviewClick(view, mediaRecord); } } @@ -224,7 +228,7 @@ public final class MediaOverviewPageFragment extends Fragment } } - private void handleMediaPreviewClick(@NonNull MediaTable.MediaRecord mediaRecord) { + private void handleMediaPreviewClick(@NonNull View view, @NonNull MediaTable.MediaRecord mediaRecord) { if (mediaRecord.getAttachment().getUri() == null) { return; } @@ -249,8 +253,18 @@ public final class MediaOverviewPageFragment extends Fragment threadId == MediaTable.ALL_THREADS, true, sorting, - attachment.isVideoGif()); - context.startActivity(MediaIntentFactory.create(context, args)); + attachment.isVideoGif(), + new MediaIntentFactory.SharedElementArgs( + attachment.getWidth(), + attachment.getHeight(), + DimensionUnit.DP.toDp(12), + DimensionUnit.DP.toDp(12), + DimensionUnit.DP.toDp(12), + DimensionUnit.DP.toDp(12) + )); + view.setTransitionName(MediaPreviewV2Activity.SHARED_ELEMENT_TRANSITION_NAME); + ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(requireActivity(), view, MediaPreviewV2Activity.SHARED_ELEMENT_TRANSITION_NAME); + context.startActivity(MediaIntentFactory.create(context, args), options.toBundle()); } else { if (!MediaUtil.isAudio(attachment)) { showFileExternally(context, mediaRecord); diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/ImageMediaPreviewFragment.java b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/ImageMediaPreviewFragment.java index 17781edee..7da03274d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/ImageMediaPreviewFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/ImageMediaPreviewFragment.java @@ -7,16 +7,22 @@ import android.view.View; import android.view.ViewGroup; import androidx.annotation.Nullable; +import androidx.lifecycle.ViewModelProvider; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.components.ZoomingImageView; import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.mms.GlideRequests; +import org.thoughtcrime.securesms.util.LifecycleDisposable; import org.thoughtcrime.securesms.util.MediaUtil; public final class ImageMediaPreviewFragment extends MediaPreviewFragment { private MediaPreviewPlayerControlView bottomBarControlView; + private MediaPreviewV2ViewModel viewModel; + private LifecycleDisposable lifecycleDisposable; + + @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -24,12 +30,19 @@ public final class ImageMediaPreviewFragment extends MediaPreviewFragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - ZoomingImageView zoomingImageView = (ZoomingImageView) inflater.inflate(R.layout.media_preview_image_fragment, container, false); + Bundle savedInstanceState) + { + View view = inflater.inflate(R.layout.media_preview_image_fragment, container, false); GlideRequests glideRequests = GlideApp.with(requireActivity()); Bundle arguments = requireArguments(); Uri uri = arguments.getParcelable(DATA_URI); String contentType = arguments.getString(DATA_CONTENT_TYPE); + ZoomingImageView zoomingImageView = view.findViewById(R.id.zooming_image_view); + + viewModel = new ViewModelProvider(requireActivity()).get(MediaPreviewV2ViewModel.class); + lifecycleDisposable = new LifecycleDisposable(); + + lifecycleDisposable.bindTo(getViewLifecycleOwner()); if (!MediaUtil.isImageType(contentType)) { throw new AssertionError("This fragment can only display images"); @@ -40,7 +53,11 @@ public final class ImageMediaPreviewFragment extends MediaPreviewFragment { zoomingImageView.setOnClickListener(v -> events.singleTapOnMedia()); - return zoomingImageView; + lifecycleDisposable.add(viewModel.getState().distinctUntilChanged().subscribe(state -> { + zoomingImageView.setVisibility(state.isInSharedAnimation() ? View.INVISIBLE : View.VISIBLE); + })); + + return view; } @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaIntentFactory.kt b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaIntentFactory.kt index ad920519d..021579f60 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaIntentFactory.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaIntentFactory.kt @@ -6,6 +6,7 @@ import android.net.Uri import android.os.Bundle import android.os.Parcelable import kotlinx.parcelize.Parcelize +import org.signal.core.util.dp import org.thoughtcrime.securesms.attachments.DatabaseAttachment import org.thoughtcrime.securesms.database.MediaTable import org.thoughtcrime.securesms.database.MediaTable.MediaRecord @@ -15,15 +16,16 @@ object MediaIntentFactory { const val NOT_IN_A_THREAD = -2 const val UNKNOWN_TIMESTAMP = -2 - const val THREAD_ID_EXTRA = "thread_id" - const val DATE_EXTRA = "date" - const val SIZE_EXTRA = "size" - const val CAPTION_EXTRA = "caption" - const val LEFT_IS_RECENT_EXTRA = "left_is_recent" - const val HIDE_ALL_MEDIA_EXTRA = "came_from_all_media" - const val SHOW_THREAD_EXTRA = "show_thread" - const val SORTING_EXTRA = "sorting" - const val IS_VIDEO_GIF = "is_video_gif" + + @Parcelize + data class SharedElementArgs( + val width: Int = 1, + val height: Int = 1, + val topLeft: Float = 0f, + val topRight: Float = 0f, + val bottomRight: Float = 0f, + val bottomLeft: Float = 0f + ) : Parcelable @Parcelize data class MediaPreviewArgs( @@ -38,7 +40,8 @@ object MediaIntentFactory { val showThread: Boolean = false, val allMediaInRail: Boolean = false, val sorting: MediaTable.Sorting, - val isVideoGif: Boolean + val isVideoGif: Boolean, + val sharedElementArgs: SharedElementArgs = SharedElementArgs() ) : Parcelable @JvmStatic @@ -68,7 +71,15 @@ object MediaIntentFactory { leftIsRecent, allMediaInRail = allMediaInRail, sorting = MediaTable.Sorting.Newest, - isVideoGif = attachment.isVideoGif + isVideoGif = attachment.isVideoGif, + sharedElementArgs = SharedElementArgs( + attachment.width, + attachment.height, + 12.dp.toFloat(), + 12.dp.toFloat(), + 12.dp.toFloat(), + 12.dp.toFloat() + ) ) ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2Activity.kt b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2Activity.kt index 03aef514c..b7a0a54db 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2Activity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2Activity.kt @@ -1,28 +1,104 @@ package org.thoughtcrime.securesms.mediapreview import android.content.Context +import android.net.Uri import android.os.Bundle +import android.view.View +import android.widget.ImageView +import androidx.activity.viewModels import androidx.appcompat.app.AppCompatDelegate import androidx.core.content.ContextCompat +import androidx.core.transition.addListener +import androidx.core.view.animation.PathInterpolatorCompat import androidx.fragment.app.commit +import com.bumptech.glide.load.engine.DiskCacheStrategy +import com.bumptech.glide.load.resource.bitmap.DownsampleStrategy +import com.google.android.material.shape.ShapeAppearanceModel +import com.google.android.material.transition.platform.MaterialContainerTransform +import com.google.android.material.transition.platform.MaterialContainerTransformSharedElementCallback import org.thoughtcrime.securesms.PassphraseRequiredActivity import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaController import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaControllerOwner +import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader +import org.thoughtcrime.securesms.mms.GlideApp +import org.thoughtcrime.securesms.util.ActionRequestListener +import org.thoughtcrime.securesms.util.LifecycleDisposable class MediaPreviewV2Activity : PassphraseRequiredActivity(), VoiceNoteMediaControllerOwner { override lateinit var voiceNoteMediaController: VoiceNoteMediaController + private val viewModel: MediaPreviewV2ViewModel by viewModels() + private val lifecycleDisposable = LifecycleDisposable() + + private lateinit var transitionImageView: ImageView + override fun attachBaseContext(newBase: Context) { delegate.localNightMode = AppCompatDelegate.MODE_NIGHT_YES super.attachBaseContext(newBase) } override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) { + val args = MediaIntentFactory.requireArguments(intent.extras!!) + val originalCorners = ShapeAppearanceModel.Builder() + .setTopLeftCornerSize(args.sharedElementArgs.topLeft) + .setTopRightCornerSize(args.sharedElementArgs.topRight) + .setBottomRightCornerSize(args.sharedElementArgs.bottomRight) + .setBottomLeftCornerSize(args.sharedElementArgs.bottomLeft) + .build() + + postponeEnterTransition() + setEnterSharedElementCallback(MaterialContainerTransformSharedElementCallback()) + window.sharedElementEnterTransition = MaterialContainerTransform().apply { + addTarget(SHARED_ELEMENT_TRANSITION_NAME) + startShapeAppearanceModel = originalCorners + endShapeAppearanceModel = ShapeAppearanceModel.builder().setAllCornerSizes(0f).build() + duration = 250L + interpolator = PathInterpolatorCompat.create(0.17f, 0.17f, 0f, 1f) + addListener( + onStart = { + transitionImageView.visibility = View.VISIBLE + viewModel.setIsInSharedAnimation(true) + }, + onEnd = { + transitionImageView.clearAnimation() + transitionImageView.visibility = View.INVISIBLE + viewModel.setIsInSharedAnimation(false) + } + ) + } + + window.sharedElementExitTransition = MaterialContainerTransform().apply { + addTarget(SHARED_ELEMENT_TRANSITION_NAME) + startShapeAppearanceModel = ShapeAppearanceModel.builder().setAllCornerSizes(0f).build() + endShapeAppearanceModel = originalCorners + duration = 250L + interpolator = PathInterpolatorCompat.create(0.17f, 0.17f, 0f, 1f) + addListener( + onStart = { + transitionImageView.visibility = View.VISIBLE + viewModel.setIsInSharedAnimation(true) + }, + onEnd = { + transitionImageView.clearAnimation() + transitionImageView.visibility = View.INVISIBLE + viewModel.setIsInSharedAnimation(false) + } + ) + } + super.onCreate(savedInstanceState, ready) setTheme(R.style.TextSecure_MediaPreview) setContentView(R.layout.activity_mediapreview_v2) + + transitionImageView = findViewById(R.id.transition_image_view) + lifecycleDisposable += viewModel.state.subscribe { state -> + if (state.position in state.mediaRecords.indices) { + setTransitionImage(state.mediaRecords[state.position].attachment?.uri) + } + } + voiceNoteMediaController = VoiceNoteMediaController(this) val systemBarColor = ContextCompat.getColor(this, R.color.signal_dark_colorSurface) @@ -31,7 +107,6 @@ class MediaPreviewV2Activity : PassphraseRequiredActivity(), VoiceNoteMediaContr if (savedInstanceState == null) { val bundle = Bundle() - val args = MediaIntentFactory.requireArguments(intent.extras!!) bundle.putParcelable(MediaPreviewV2Fragment.ARGS_KEY, args) supportFragmentManager.commit { setReorderingAllowed(true) @@ -40,7 +115,23 @@ class MediaPreviewV2Activity : PassphraseRequiredActivity(), VoiceNoteMediaContr } } + private fun setTransitionImage(mediaUri: Uri?) { + if (mediaUri == null) { + GlideApp.with(this).clear(transitionImageView) + return + } + + GlideApp.with(this) + .load(DecryptableStreamUriLoader.DecryptableUri(mediaUri)) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .dontTransform() + .downsample(DownsampleStrategy.FIT_CENTER) + .addListener(ActionRequestListener.onEither { startPostponedEnterTransition() }) + .into(transitionImageView) + } + companion object { private const val FRAGMENT_TAG = "media_preview_fragment_v2" + const val SHARED_ELEMENT_TRANSITION_NAME = "thumb" } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2Fragment.kt b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2Fragment.kt index a6be6e1cd..a47eade61 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2Fragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2Fragment.kt @@ -79,7 +79,9 @@ class MediaPreviewV2Fragment : LoggingFragment(R.layout.fragment_media_preview_v private val lifecycleDisposable = LifecycleDisposable() private val binding by ViewBinderDelegate(FragmentMediaPreviewV2Binding::bind) - private val viewModel: MediaPreviewV2ViewModel by viewModels() + private val viewModel: MediaPreviewV2ViewModel by viewModels(ownerProducer = { + requireActivity() + }) private val debouncer = Debouncer(2, TimeUnit.SECONDS) private lateinit var pagerAdapter: MediaPreviewV2Adapter diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2State.kt b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2State.kt index 1e5392331..bfde97ec2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2State.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2State.kt @@ -12,7 +12,8 @@ data class MediaPreviewV2State( val allMediaInAlbumRail: Boolean = false, val leftIsRecent: Boolean = false, val albums: Map> = mapOf(), - val messageBodies: Map = mapOf() + val messageBodies: Map = mapOf(), + val isInSharedAnimation: Boolean = true ) { enum class LoadState { INIT, DATA_LOADED, MEDIA_READY } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2ViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2ViewModel.kt index 9afe58a69..7558cabc9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2ViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2ViewModel.kt @@ -27,6 +27,10 @@ class MediaPreviewV2ViewModel : ViewModel() { val currentPosition: Int get() = store.state.position + fun setIsInSharedAnimation(isInSharedAnimation: Boolean) { + store.update { it.copy(isInSharedAnimation = isInSharedAnimation) } + } + fun fetchAttachments(context: Context, startingAttachmentId: AttachmentId, threadId: Long, sorting: MediaTable.Sorting, forceRefresh: Boolean = false) { if (store.state.loadState == MediaPreviewV2State.LoadState.INIT || forceRefresh) { disposables += store.update(repository.getAttachments(context, startingAttachmentId, threadId, sorting)) { result: MediaPreviewRepository.Result, oldState: MediaPreviewV2State -> diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/VideoMediaPreviewFragment.java b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/VideoMediaPreviewFragment.java index f413ffc78..7a30967ff 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/VideoMediaPreviewFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/VideoMediaPreviewFragment.java @@ -9,6 +9,7 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.lifecycle.ViewModelProvider; import com.google.android.exoplayer2.ui.PlayerControlView; @@ -16,6 +17,7 @@ import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaControllerOwner; import org.thoughtcrime.securesms.mms.VideoSlide; +import org.thoughtcrime.securesms.util.LifecycleDisposable; import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.video.VideoPlayer; @@ -27,8 +29,11 @@ public final class VideoMediaPreviewFragment extends MediaPreviewFragment { private static final Long MINIMUM_DURATION_FOR_SKIP_MS = TimeUnit.MILLISECONDS.convert(30, TimeUnit.SECONDS); - private VideoPlayer videoView; - private boolean isVideoGif; + private VideoPlayer videoView; + private boolean isVideoGif; + private MediaPreviewV2ViewModel viewModel; + private LifecycleDisposable lifecycleDisposable; + @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { @@ -45,7 +50,14 @@ public final class VideoMediaPreviewFragment extends MediaPreviewFragment { throw new AssertionError("This fragment can only display video"); } - videoView = itemView.findViewById(R.id.video_player); + videoView = itemView.findViewById(R.id.video_player); + viewModel = new ViewModelProvider(requireActivity()).get(MediaPreviewV2ViewModel.class); + lifecycleDisposable = new LifecycleDisposable(); + + lifecycleDisposable.add(viewModel.getState().distinctUntilChanged().subscribe(state -> { + Log.d(TAG, "ANIM" + state.isInSharedAnimation()); + itemView.setVisibility(state.isInSharedAnimation() ? View.INVISIBLE : View.VISIBLE); + })); videoView.setWindow(requireActivity().getWindow()); videoView.setVideoSource(new VideoSlide(getContext(), uri, size, false), autoPlay, TAG); 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 351ddbbc0..fdb0edcf1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/AttachmentManager.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/AttachmentManager.java @@ -527,7 +527,8 @@ public class AttachmentManager { false, false, MediaTable.Sorting.Newest, - slide.isVideoGif()); + slide.isVideoGif(), + new MediaIntentFactory.SharedElementArgs()); context.startActivity(MediaIntentFactory.create(context, args)); } } diff --git a/app/src/main/res/layout/activity_mediapreview_v2.xml b/app/src/main/res/layout/activity_mediapreview_v2.xml index fa93ff97d..3105efa4b 100644 --- a/app/src/main/res/layout/activity_mediapreview_v2.xml +++ b/app/src/main/res/layout/activity_mediapreview_v2.xml @@ -1,7 +1,21 @@ - \ No newline at end of file + android:layout_height="match_parent"> + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/conversation_item_received_thumbnail.xml b/app/src/main/res/layout/conversation_item_received_thumbnail.xml index cced3718d..2b0701b0e 100644 --- a/app/src/main/res/layout/conversation_item_received_thumbnail.xml +++ b/app/src/main/res/layout/conversation_item_received_thumbnail.xml @@ -1,21 +1,20 @@ - + tools:visibility="gone" /> diff --git a/app/src/main/res/layout/media_preview_image_fragment.xml b/app/src/main/res/layout/media_preview_image_fragment.xml index 99aa3d21d..641267e4b 100644 --- a/app/src/main/res/layout/media_preview_image_fragment.xml +++ b/app/src/main/res/layout/media_preview_image_fragment.xml @@ -1,8 +1,16 @@ - \ No newline at end of file + tools:viewBindingIgnore="true"> + + + + diff --git a/app/src/main/res/layout/media_preview_video_fragment.xml b/app/src/main/res/layout/media_preview_video_fragment.xml index 6e8796adb..74b33233e 100644 --- a/app/src/main/res/layout/media_preview_video_fragment.xml +++ b/app/src/main/res/layout/media_preview_video_fragment.xml @@ -1,14 +1,15 @@ + android:orientation="vertical" + tools:viewBindingIgnore="true"> + android:layout_height="match_parent" + android:layout_gravity="center" /> \ No newline at end of file diff --git a/app/src/main/res/values-v21/themes.xml b/app/src/main/res/values-v21/themes.xml index 8bc4f750e..5ee5980cd 100644 --- a/app/src/main/res/values-v21/themes.xml +++ b/app/src/main/res/values-v21/themes.xml @@ -43,6 +43,7 @@