kopia lustrzana https://github.com/ryukoposting/Signal-Android
Add thumbnail shared element animation.
rodzic
2c48d40375
commit
d0de43a6b2
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
)
|
||||
}
|
||||
)
|
||||
|
|
|
@ -24,7 +24,7 @@ object SharedMediaPreference {
|
|||
class Model(
|
||||
val mediaCursor: Cursor,
|
||||
val mediaIds: List<Long>,
|
||||
val onMediaRecordClick: (MediaTable.MediaRecord, Boolean) -> Unit
|
||||
val onMediaRecordClick: (View, MediaTable.MediaRecord, Boolean) -> Unit
|
||||
) : PreferenceModel<Model>() {
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<MessageRecord> 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<MultiselectPart> 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<MessageRecord> previousRecord,
|
||||
@NonNull Optional<MessageRecord> nextRecord,
|
||||
boolean isGroupThread,
|
||||
boolean hasWallpaper,
|
||||
boolean messageRequestAccepted,
|
||||
boolean allowedToPlayInline)
|
||||
private void setMediaAttributes(@NonNull MessageRecord messageRecord,
|
||||
@NonNull Optional<MessageRecord> previousRecord,
|
||||
@NonNull Optional<MessageRecord> 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<Slide> 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<MessageRecord> previous, Optional<MessageRecord> 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<MessageRecord> previous,
|
||||
@NonNull Optional<MessageRecord> 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<MessageRecord> previous, @NonNull Optional<MessageRecord> 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<MessageRecord> previous, @NonNull Optional<MessageRecord> 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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -12,7 +12,8 @@ data class MediaPreviewV2State(
|
|||
val allMediaInAlbumRail: Boolean = false,
|
||||
val leftIsRecent: Boolean = false,
|
||||
val albums: Map<Long, List<Media>> = mapOf(),
|
||||
val messageBodies: Map<Long, SpannableString> = mapOf()
|
||||
val messageBodies: Map<Long, SpannableString> = mapOf(),
|
||||
val isInSharedAnimation: Boolean = true
|
||||
) {
|
||||
enum class LoadState { INIT, DATA_LOADED, MEDIA_READY }
|
||||
}
|
||||
|
|
|
@ -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 ->
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,21 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/fragment_container_view"
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/signal_dark_colorNeutral"/>
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/transition_image_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:adjustViewBounds="true"
|
||||
android:importantForAccessibility="no"
|
||||
android:transitionName="thumb" />
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/fragment_container_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/signal_dark_colorNeutral" />
|
||||
|
||||
</FrameLayout>
|
|
@ -1,21 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<org.thoughtcrime.securesms.components.ConversationItemThumbnail
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<org.thoughtcrime.securesms.components.ConversationItemThumbnail xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/image_view"
|
||||
android:layout_width="@dimen/media_bubble_default_dimens"
|
||||
android:layout_height="@dimen/media_bubble_default_dimens"
|
||||
android:adjustViewBounds="true"
|
||||
android:contentDescription="@string/conversation_item__mms_image_description"
|
||||
android:elevation="8dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:visibility="gone"
|
||||
app:conversationThumbnail_gifWidth="@dimen/media_bubble_gif_width"
|
||||
app:conversationThumbnail_maxHeight="@dimen/media_bubble_max_height"
|
||||
app:conversationThumbnail_maxWidth="@dimen/media_bubble_max_width"
|
||||
app:conversationThumbnail_minHeight="@dimen/media_bubble_min_height"
|
||||
app:conversationThumbnail_minWidth="@dimen/media_bubble_min_width_solo"
|
||||
tools:src="@drawable/ic_video_light"
|
||||
tools:viewBindingIgnore="true"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/image_view"
|
||||
android:layout_width="@dimen/media_bubble_default_dimens"
|
||||
android:layout_height="@dimen/media_bubble_default_dimens"
|
||||
android:scaleType="centerCrop"
|
||||
android:adjustViewBounds="true"
|
||||
android:contentDescription="@string/conversation_item__mms_image_description"
|
||||
android:visibility="gone"
|
||||
android:elevation="8dp"
|
||||
app:conversationThumbnail_minWidth="@dimen/media_bubble_min_width_solo"
|
||||
app:conversationThumbnail_maxWidth="@dimen/media_bubble_max_width"
|
||||
app:conversationThumbnail_minHeight="@dimen/media_bubble_min_height"
|
||||
app:conversationThumbnail_maxHeight="@dimen/media_bubble_max_height"
|
||||
app:conversationThumbnail_gifWidth="@dimen/media_bubble_gif_width"
|
||||
tools:src="@drawable/ic_video_light"
|
||||
tools:visibility="gone" />
|
||||
tools:visibility="gone" />
|
||||
|
|
|
@ -1,8 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<org.thoughtcrime.securesms.components.ZoomingImageView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:viewBindingIgnore="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clickable="false"
|
||||
android:contentDescription="@string/media_preview_activity__media_content_description" />
|
||||
tools:viewBindingIgnore="true">
|
||||
|
||||
<org.thoughtcrime.securesms.components.ZoomingImageView
|
||||
android:id="@+id/zooming_image_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:clickable="false"
|
||||
android:contentDescription="@string/media_preview_activity__media_content_description" />
|
||||
|
||||
</FrameLayout>
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:viewBindingIgnore="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
android:orientation="vertical"
|
||||
tools:viewBindingIgnore="true">
|
||||
|
||||
<org.thoughtcrime.securesms.video.VideoPlayer
|
||||
android:id="@+id/video_player"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
</FrameLayout>
|
|
@ -43,6 +43,7 @@
|
|||
|
||||
<style name="TextSecure.MediaPreview" parent="@style/TextSecure.BaseMediaPreview">
|
||||
<item name="android:navigationBarColor">@color/media_preview_bar_background</item>
|
||||
<item name="android:windowContentTransitions">true</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.Signal.Light.BottomSheetDialog" parent="Theme.Material3.Light.BottomSheetDialog">
|
||||
|
|
Ładowanie…
Reference in New Issue