mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-24 19:00:26 +01:00
Implement badge gifting behind feature flag.
This commit is contained in:
committed by
Greyson Parrelli
parent
5d16d1cd23
commit
a4a4665aaa
@@ -77,6 +77,9 @@ import org.signal.core.util.concurrent.SimpleTask;
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.LoggingFragment;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.badges.gifts.OpenableGiftItemDecoration;
|
||||
import org.thoughtcrime.securesms.badges.gifts.viewgift.received.ViewReceivedGiftBottomSheet;
|
||||
import org.thoughtcrime.securesms.badges.gifts.viewgift.sent.ViewSentGiftBottomSheet;
|
||||
import org.thoughtcrime.securesms.components.ConversationScrollToView;
|
||||
import org.thoughtcrime.securesms.components.ConversationTypingView;
|
||||
import org.thoughtcrime.securesms.components.TypingStatusRepository;
|
||||
@@ -308,6 +311,10 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
|
||||
|
||||
RecyclerViewColorizer recyclerViewColorizer = new RecyclerViewColorizer(list);
|
||||
|
||||
OpenableGiftItemDecoration openableGiftItemDecoration = new OpenableGiftItemDecoration(requireContext());
|
||||
getViewLifecycleOwner().getLifecycle().addObserver(openableGiftItemDecoration);
|
||||
|
||||
list.addItemDecoration(openableGiftItemDecoration);
|
||||
list.addItemDecoration(multiselectItemDecoration);
|
||||
list.setItemAnimator(conversationItemAnimator);
|
||||
|
||||
@@ -413,6 +420,17 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
getChildFragmentManager().setFragmentResultListener(ViewReceivedGiftBottomSheet.REQUEST_KEY, getViewLifecycleOwner(), (key, bundle) -> {
|
||||
if (bundle.getBoolean(ViewReceivedGiftBottomSheet.RESULT_NOT_NOW, false)) {
|
||||
Snackbar.make(view.getRootView(), R.string.ConversationFragment__you_can_redeem_your_badge_later, Snackbar.LENGTH_SHORT)
|
||||
.setTextColor(Color.WHITE)
|
||||
.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private @NonNull GiphyMp4ProjectionRecycler initializeGiphyMp4() {
|
||||
int maxPlayback = GiphyMp4PlaybackPolicy.maxSimultaneousPlaybackInConversation();
|
||||
List<GiphyMp4ProjectionPlayerHolder> holders = GiphyMp4ProjectionPlayerHolder.injectVideoViews(requireContext(),
|
||||
@@ -1950,6 +1968,19 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
|
||||
|
||||
RecipientBottomSheetDialogFragment.create(target, recipient.get().getGroupId().orElse(null)).show(getParentFragmentManager(), "BOTTOM");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewGiftBadgeClicked(@NonNull MessageRecord messageRecord) {
|
||||
if (!MessageRecordUtil.hasGiftBadge(messageRecord)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (messageRecord.isOutgoing()) {
|
||||
ViewSentGiftBottomSheet.show(getChildFragmentManager(), (MmsMessageRecord) messageRecord);
|
||||
} else {
|
||||
ViewReceivedGiftBottomSheet.show(getChildFragmentManager(), (MmsMessageRecord) messageRecord);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void refreshList() {
|
||||
|
||||
@@ -7,6 +7,7 @@ import android.net.Uri;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.thoughtcrime.securesms.badges.models.Badge;
|
||||
import org.thoughtcrime.securesms.conversation.colors.ChatColors;
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||
import org.thoughtcrime.securesms.mediasend.Media;
|
||||
@@ -33,6 +34,7 @@ public class ConversationIntents {
|
||||
private static final String EXTRA_STARTING_POSITION = "starting_position";
|
||||
private static final String EXTRA_FIRST_TIME_IN_SELF_CREATED_GROUP = "first_time_in_group";
|
||||
private static final String EXTRA_WITH_SEARCH_OPEN = "with_search_open";
|
||||
private static final String EXTRA_GIFT_BADGE = "gift_badge";
|
||||
|
||||
private ConversationIntents() {
|
||||
}
|
||||
@@ -72,6 +74,7 @@ public class ConversationIntents {
|
||||
private final int startingPosition;
|
||||
private final boolean firstTimeInSelfCreatedGroup;
|
||||
private final boolean withSearchOpen;
|
||||
private final Badge giftBadge;
|
||||
|
||||
static Args from(@NonNull Intent intent) {
|
||||
if (isBubbleIntent(intent)) {
|
||||
@@ -84,7 +87,8 @@ public class ConversationIntents {
|
||||
ThreadDatabase.DistributionTypes.DEFAULT,
|
||||
-1,
|
||||
false,
|
||||
false);
|
||||
false,
|
||||
null);
|
||||
}
|
||||
|
||||
return new Args(RecipientId.from(Objects.requireNonNull(intent.getStringExtra(EXTRA_RECIPIENT))),
|
||||
@@ -96,7 +100,8 @@ public class ConversationIntents {
|
||||
intent.getIntExtra(EXTRA_DISTRIBUTION_TYPE, ThreadDatabase.DistributionTypes.DEFAULT),
|
||||
intent.getIntExtra(EXTRA_STARTING_POSITION, -1),
|
||||
intent.getBooleanExtra(EXTRA_FIRST_TIME_IN_SELF_CREATED_GROUP, false),
|
||||
intent.getBooleanExtra(EXTRA_WITH_SEARCH_OPEN, false));
|
||||
intent.getBooleanExtra(EXTRA_WITH_SEARCH_OPEN, false),
|
||||
intent.getParcelableExtra(EXTRA_GIFT_BADGE));
|
||||
}
|
||||
|
||||
private Args(@NonNull RecipientId recipientId,
|
||||
@@ -108,7 +113,8 @@ public class ConversationIntents {
|
||||
int distributionType,
|
||||
int startingPosition,
|
||||
boolean firstTimeInSelfCreatedGroup,
|
||||
boolean withSearchOpen)
|
||||
boolean withSearchOpen,
|
||||
@Nullable Badge giftBadge)
|
||||
{
|
||||
this.recipientId = recipientId;
|
||||
this.threadId = threadId;
|
||||
@@ -120,6 +126,7 @@ public class ConversationIntents {
|
||||
this.startingPosition = startingPosition;
|
||||
this.firstTimeInSelfCreatedGroup = firstTimeInSelfCreatedGroup;
|
||||
this.withSearchOpen = withSearchOpen;
|
||||
this.giftBadge = giftBadge;
|
||||
}
|
||||
|
||||
public @NonNull RecipientId getRecipientId() {
|
||||
@@ -169,6 +176,10 @@ public class ConversationIntents {
|
||||
public boolean isWithSearchOpen() {
|
||||
return withSearchOpen;
|
||||
}
|
||||
|
||||
public @Nullable Badge getGiftBadge() {
|
||||
return giftBadge;
|
||||
}
|
||||
}
|
||||
|
||||
public final static class Builder {
|
||||
@@ -187,6 +198,7 @@ public class ConversationIntents {
|
||||
private String dataType;
|
||||
private boolean firstTimeInSelfCreatedGroup;
|
||||
private boolean withSearchOpen;
|
||||
private Badge giftBadge;
|
||||
|
||||
private Builder(@NonNull Context context,
|
||||
@NonNull RecipientId recipientId,
|
||||
@@ -255,7 +267,12 @@ public class ConversationIntents {
|
||||
this.firstTimeInSelfCreatedGroup = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public Builder withGiftBadge(@NonNull Badge badge) {
|
||||
this.giftBadge = badge;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NonNull Intent build() {
|
||||
if (stickerLocator != null && media != null) {
|
||||
throw new IllegalStateException("Cannot have both sticker and media array");
|
||||
@@ -281,6 +298,7 @@ public class ConversationIntents {
|
||||
intent.putExtra(EXTRA_BORDERLESS, isBorderless);
|
||||
intent.putExtra(EXTRA_FIRST_TIME_IN_SELF_CREATED_GROUP, firstTimeInSelfCreatedGroup);
|
||||
intent.putExtra(EXTRA_WITH_SEARCH_OPEN, withSearchOpen);
|
||||
intent.putExtra(EXTRA_GIFT_BADGE, giftBadge);
|
||||
|
||||
if (draftText != null) {
|
||||
intent.putExtra(EXTRA_TEXT, draftText);
|
||||
|
||||
@@ -71,6 +71,8 @@ import org.thoughtcrime.securesms.MediaPreviewActivity;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
|
||||
import org.thoughtcrime.securesms.badges.BadgeImageView;
|
||||
import org.thoughtcrime.securesms.badges.gifts.GiftMessageView;
|
||||
import org.thoughtcrime.securesms.badges.gifts.OpenableGift;
|
||||
import org.thoughtcrime.securesms.components.AlertView;
|
||||
import org.thoughtcrime.securesms.components.AudioView;
|
||||
import org.thoughtcrime.securesms.components.AvatarImageView;
|
||||
@@ -147,6 +149,9 @@ import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import kotlin.Unit;
|
||||
import kotlin.jvm.functions.Function1;
|
||||
|
||||
/**
|
||||
* A view that displays an individual conversation item within a conversation
|
||||
* thread. Used by ComposeMessageActivity's ListActivity via a ConversationAdapter.
|
||||
@@ -156,7 +161,8 @@ import java.util.concurrent.TimeUnit;
|
||||
*/
|
||||
|
||||
public final class ConversationItem extends RelativeLayout implements BindableConversationItem,
|
||||
RecipientForeverObserver
|
||||
RecipientForeverObserver,
|
||||
OpenableGift
|
||||
{
|
||||
private static final String TAG = Log.tag(ConversationItem.class);
|
||||
|
||||
@@ -207,6 +213,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
|
||||
private Stub<BorderlessImageView> stickerStub;
|
||||
private Stub<ViewOnceMessageView> revealableStub;
|
||||
private Stub<Button> callToActionStub;
|
||||
private Stub<GiftMessageView> giftViewStub;
|
||||
private @Nullable EventListener eventListener;
|
||||
|
||||
private int defaultBubbleColor;
|
||||
@@ -224,6 +231,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
|
||||
private final UrlClickListener urlClickListener = new UrlClickListener();
|
||||
private final Rect thumbnailMaskingRect = new Rect();
|
||||
private final TouchDelegateChangedListener touchDelegateChangedListener = new TouchDelegateChangedListener();
|
||||
private final GiftMessageViewCallback giftMessageViewCallback = new GiftMessageViewCallback();
|
||||
|
||||
private final Context context;
|
||||
|
||||
@@ -298,6 +306,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
|
||||
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));
|
||||
|
||||
setOnClickListener(new ClickListener(null));
|
||||
|
||||
@@ -914,6 +923,10 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
|
||||
return MessageRecordUtil.isViewOnceMessage(messageRecord);
|
||||
}
|
||||
|
||||
private boolean isGiftMessage(MessageRecord messageRecord) {
|
||||
return MessageRecordUtil.hasGiftBadge(messageRecord);
|
||||
}
|
||||
|
||||
private void setBodyText(@NonNull MessageRecord messageRecord,
|
||||
@Nullable String searchQuery,
|
||||
boolean messageRequestAccepted)
|
||||
@@ -935,7 +948,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
|
||||
bodyText.setText(italics);
|
||||
bodyText.setVisibility(View.VISIBLE);
|
||||
bodyText.setOverflowText(null);
|
||||
} else if (isCaptionlessMms(messageRecord) || isStoryReaction(messageRecord)) {
|
||||
} else if (isCaptionlessMms(messageRecord) || isStoryReaction(messageRecord) || isGiftMessage(messageRecord)) {
|
||||
bodyText.setVisibility(View.GONE);
|
||||
} else {
|
||||
Spannable styledText = conversationMessage.getDisplayBody(getContext());
|
||||
@@ -1004,6 +1017,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
|
||||
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);
|
||||
|
||||
revealableStub.get().setMessage((MmsMessageRecord) messageRecord, hasWallpaper);
|
||||
revealableStub.get().setOnClickListener(revealableClickListener);
|
||||
@@ -1020,6 +1034,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
|
||||
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);
|
||||
|
||||
sharedContactStub.get().setContact(((MediaMmsMessageRecord) messageRecord).getSharedContacts().get(0), glideRequests, locale);
|
||||
sharedContactStub.get().setEventListener(sharedContactEventListener);
|
||||
@@ -1039,6 +1054,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
|
||||
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);
|
||||
|
||||
//noinspection ConstantConditions
|
||||
LinkPreview linkPreview = ((MmsMessageRecord) messageRecord).getLinkPreviews().get(0);
|
||||
@@ -1083,6 +1099,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
|
||||
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);
|
||||
|
||||
audioViewStub.get().setAudio(Objects.requireNonNull(((MediaMmsMessageRecord) messageRecord).getSlideDeck().getAudioSlide()), new AudioViewCallbacks(), showControls, true);
|
||||
audioViewStub.get().setDownloadClickListener(singleDownloadClickListener);
|
||||
@@ -1108,6 +1125,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
|
||||
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);
|
||||
|
||||
//noinspection ConstantConditions
|
||||
documentViewStub.get().setDocument(((MediaMmsMessageRecord) messageRecord).getSlideDeck().getDocumentSlide(), showControls);
|
||||
@@ -1130,6 +1148,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
|
||||
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 (hasSticker(messageRecord)) {
|
||||
//noinspection ConstantConditions
|
||||
@@ -1159,6 +1178,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
|
||||
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);
|
||||
|
||||
List<Slide> thumbnailSlides = ((MmsMessageRecord) messageRecord).getSlideDeck().getThumbnailSlides();
|
||||
mediaThumbnailStub.require().setMinimumThumbnailWidth(readDimen(isCaptionlessMms(messageRecord) ? R.dimen.media_bubble_min_width_solo
|
||||
@@ -1202,6 +1222,20 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
|
||||
canPlayContent = (GiphyMp4PlaybackPolicy.autoplay() || allowedToPlayInline) && mediaItem != null;
|
||||
}
|
||||
|
||||
} 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);
|
||||
|
||||
MmsMessageRecord mmsMessageRecord = (MmsMessageRecord) messageRecord;
|
||||
giftViewStub.get().setGiftBadge(glideRequests, Objects.requireNonNull(mmsMessageRecord.getGiftBadge()), messageRecord.isOutgoing(), giftMessageViewCallback);
|
||||
giftViewStub.get().setVisibility(VISIBLE);
|
||||
|
||||
footer.setVisibility(VISIBLE);
|
||||
} else {
|
||||
if (mediaThumbnailStub.resolved()) mediaThumbnailStub.require().setVisibility(View.GONE);
|
||||
if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE);
|
||||
@@ -1210,6 +1244,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
|
||||
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);
|
||||
|
||||
ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
ViewUtil.updateLayoutParamsIfNonNull(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
@@ -1699,7 +1734,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
|
||||
background = R.drawable.message_bubble_background_received_alone;
|
||||
outliner.setRadius(bigRadius);
|
||||
pulseOutliner.setRadius(bigRadius);
|
||||
bodyBubbleCorners = null;
|
||||
bodyBubbleCorners = new Projection.Corners(bigRadius);
|
||||
}
|
||||
} else if (isStartOfMessageCluster(current, previous, isGroupThread)) {
|
||||
if (current.isOutgoing()) {
|
||||
@@ -1711,7 +1746,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
|
||||
background = R.drawable.message_bubble_background_received_start;
|
||||
setOutlinerRadii(outliner, bigRadius, bigRadius, bigRadius, smallRadius);
|
||||
setOutlinerRadii(pulseOutliner, bigRadius, bigRadius, bigRadius, smallRadius);
|
||||
bodyBubbleCorners = null;
|
||||
bodyBubbleCorners = getBodyBubbleCorners(bigRadius, bigRadius, bigRadius, smallRadius);
|
||||
}
|
||||
} else if (isEndOfMessageCluster(current, next, isGroupThread)) {
|
||||
if (current.isOutgoing()) {
|
||||
@@ -1723,7 +1758,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
|
||||
background = R.drawable.message_bubble_background_received_end;
|
||||
setOutlinerRadii(outliner, smallRadius, bigRadius, bigRadius, bigRadius);
|
||||
setOutlinerRadii(pulseOutliner, smallRadius, bigRadius, bigRadius, bigRadius);
|
||||
bodyBubbleCorners = null;
|
||||
bodyBubbleCorners = getBodyBubbleCorners(smallRadius, bigRadius, bigRadius, bigRadius);
|
||||
}
|
||||
} else {
|
||||
if (current.isOutgoing()) {
|
||||
@@ -1735,7 +1770,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
|
||||
background = R.drawable.message_bubble_background_received_middle;
|
||||
setOutlinerRadii(outliner, smallRadius, bigRadius, bigRadius, smallRadius);
|
||||
setOutlinerRadii(pulseOutliner, smallRadius, bigRadius, bigRadius, smallRadius);
|
||||
bodyBubbleCorners = null;
|
||||
bodyBubbleCorners = getBodyBubbleCorners(smallRadius, bigRadius, bigRadius, smallRadius);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2004,6 +2039,41 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Projection getOpenableGiftProjection() {
|
||||
boolean isViewedAndIncoming = !messageRecord.isOutgoing() && messageRecord.getViewedReceiptCount() > 0;
|
||||
if (!isGiftMessage(messageRecord) || messageRecord.isRemoteDelete() || isViewedAndIncoming) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Projection.relativeToViewRoot(bodyBubble, bodyBubbleCorners)
|
||||
.translateX(bodyBubble.getTranslationX())
|
||||
.translateX(getTranslationX())
|
||||
.scale(bodyBubble.getScaleX());
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getGiftId() {
|
||||
return messageRecord.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOpenGiftCallback(@NonNull Function1<? super OpenableGift, Unit> openGift) {
|
||||
if (giftViewStub.resolved()) {
|
||||
bodyBubble.setOnClickListener(unused -> openGift.invoke(this));
|
||||
giftViewStub.get().onGiftNotOpened();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearOpenGiftCallback() {
|
||||
if (giftViewStub.resolved()) {
|
||||
bodyBubble.setOnClickListener(null);
|
||||
bodyBubble.setClickable(false);
|
||||
giftViewStub.get().onGiftOpened();
|
||||
}
|
||||
}
|
||||
|
||||
private class SharedContactEventListener implements SharedContactView.EventListener {
|
||||
@Override
|
||||
public void onAddToContactsClicked(@NonNull Contact contact) {
|
||||
@@ -2179,6 +2249,14 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
|
||||
}
|
||||
}
|
||||
|
||||
private class GiftMessageViewCallback implements GiftMessageView.Callback {
|
||||
|
||||
@Override
|
||||
public void onViewGiftBadgeClicked() {
|
||||
eventListener.onViewGiftBadgeClicked(messageRecord);
|
||||
}
|
||||
}
|
||||
|
||||
private class ClickListener implements View.OnClickListener {
|
||||
private OnClickListener parent;
|
||||
|
||||
|
||||
@@ -114,6 +114,7 @@ import org.thoughtcrime.securesms.TransportOption;
|
||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||
import org.thoughtcrime.securesms.attachments.TombstoneAttachment;
|
||||
import org.thoughtcrime.securesms.audio.AudioRecorder;
|
||||
import org.thoughtcrime.securesms.badges.gifts.thanks.GiftThanksSheet;
|
||||
import org.thoughtcrime.securesms.components.AnimatingToggle;
|
||||
import org.thoughtcrime.securesms.components.ComposeText;
|
||||
import org.thoughtcrime.securesms.components.ConversationSearchBottomBar;
|
||||
@@ -485,6 +486,9 @@ public class ConversationParentFragment extends Fragment
|
||||
|
||||
// TODO [alex] LargeScreenSupport -- This will need to be built from requireArguments()
|
||||
ConversationIntents.Args args = ConversationIntents.Args.from(requireActivity().getIntent());
|
||||
if (savedInstanceState == null && args.getGiftBadge() != null) {
|
||||
GiftThanksSheet.show(getChildFragmentManager(), args.getRecipientId(), args.getGiftBadge());
|
||||
}
|
||||
|
||||
isSearchRequested = args.isWithSearchOpen();
|
||||
|
||||
@@ -2979,7 +2983,7 @@ public class ConversationParentFragment extends Fragment
|
||||
long expiresIn = TimeUnit.SECONDS.toMillis(recipient.get().getExpiresInSeconds());
|
||||
QuoteModel quote = result.isViewOnce() ? null : inputPanel.getQuote().orElse(null);
|
||||
List<Mention> mentions = new ArrayList<>(result.getMentions());
|
||||
OutgoingMediaMessage message = new OutgoingMediaMessage(recipient.get(), new SlideDeck(), result.getBody(), System.currentTimeMillis(), -1, expiresIn, result.isViewOnce(), distributionType, result.getStoryType(), null, false, quote, Collections.emptyList(), Collections.emptyList(), mentions);
|
||||
OutgoingMediaMessage message = new OutgoingMediaMessage(recipient.get(), new SlideDeck(), result.getBody(), System.currentTimeMillis(), -1, expiresIn, result.isViewOnce(), distributionType, result.getStoryType(), null, false, quote, Collections.emptyList(), Collections.emptyList(), mentions, null);
|
||||
OutgoingMediaMessage secureMessage = new OutgoingSecureMediaMessage(message);
|
||||
|
||||
final Context context = requireContext().getApplicationContext();
|
||||
@@ -3055,7 +3059,7 @@ public class ConversationParentFragment extends Fragment
|
||||
}
|
||||
}
|
||||
|
||||
OutgoingMediaMessage outgoingMessageCandidate = new OutgoingMediaMessage(Recipient.resolved(recipientId), slideDeck, body, System.currentTimeMillis(), subscriptionId, expiresIn, viewOnce, distributionType, StoryType.NONE, null, false, quote, contacts, previews, mentions);
|
||||
OutgoingMediaMessage outgoingMessageCandidate = new OutgoingMediaMessage(Recipient.resolved(recipientId), slideDeck, body, System.currentTimeMillis(), subscriptionId, expiresIn, viewOnce, distributionType, StoryType.NONE, null, false, quote, contacts, previews, mentions, null);
|
||||
|
||||
final SettableFuture<Void> future = new SettableFuture<>();
|
||||
final Context context = requireContext().getApplicationContext();
|
||||
|
||||
@@ -7,6 +7,7 @@ import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.MessageRecordUtil;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -82,6 +83,7 @@ final class MenuState {
|
||||
boolean hasInMemory = false;
|
||||
boolean hasPendingMedia = false;
|
||||
boolean mediaIsSelected = false;
|
||||
boolean hasGift = false;
|
||||
|
||||
for (MultiselectPart part : selectedParts) {
|
||||
MessageRecord messageRecord = part.getMessageRecord();
|
||||
@@ -115,6 +117,10 @@ final class MenuState {
|
||||
if (messageRecord.isRemoteDelete()) {
|
||||
remoteDelete = true;
|
||||
}
|
||||
|
||||
if (MessageRecordUtil.hasGiftBadge(messageRecord)) {
|
||||
hasGift = true;
|
||||
}
|
||||
}
|
||||
|
||||
boolean shouldShowForwardAction = !actionMessage &&
|
||||
@@ -122,6 +128,7 @@ final class MenuState {
|
||||
!viewOnce &&
|
||||
!remoteDelete &&
|
||||
!hasPendingMedia &&
|
||||
!hasGift &&
|
||||
selectedParts.size() <= MAX_FORWARDABLE_COUNT;
|
||||
|
||||
int uniqueRecords = selectedParts.stream()
|
||||
@@ -144,6 +151,7 @@ final class MenuState {
|
||||
!viewOnce &&
|
||||
messageRecord.isMms() &&
|
||||
!hasPendingMedia &&
|
||||
!hasGift &&
|
||||
!messageRecord.isMmsNotification() &&
|
||||
((MediaMmsMessageRecord)messageRecord).containsMediaSlide() &&
|
||||
((MediaMmsMessageRecord)messageRecord).getSlideDeck().getStickerSlide() == null)
|
||||
@@ -152,7 +160,7 @@ final class MenuState {
|
||||
.shouldShowReplyAction(canReplyToMessage(conversationRecipient, actionMessage, messageRecord, shouldShowMessageRequest, isNonAdminInAnnouncementGroup));
|
||||
}
|
||||
|
||||
return builder.shouldShowCopyAction(!actionMessage && !remoteDelete && hasText)
|
||||
return builder.shouldShowCopyAction(!actionMessage && !remoteDelete && hasText && !hasGift)
|
||||
.shouldShowDeleteAction(!hasInMemory && onlyContainsCompleteMessages(selectedParts))
|
||||
.shouldShowReactions(!conversationRecipient.isReleaseNotes())
|
||||
.build();
|
||||
@@ -180,6 +188,7 @@ final class MenuState {
|
||||
messageRecord.isSecure() &&
|
||||
(!conversationRecipient.isGroup() || conversationRecipient.isActiveGroup()) &&
|
||||
!messageRecord.getRecipient().isBlocked() &&
|
||||
!MessageRecordUtil.hasGiftBadge(messageRecord) &&
|
||||
!conversationRecipient.isReleaseNotes();
|
||||
}
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ class MultiselectForwardFragment :
|
||||
view.minimumHeight = resources.displayMetrics.heightPixels
|
||||
|
||||
val contactSearchRecycler: RecyclerView = view.findViewById(R.id.contact_selection_list)
|
||||
contactSearchMediator = ContactSearchMediator(this, contactSearchRecycler, FeatureFlags.shareSelectionLimit(), this::getConfiguration)
|
||||
contactSearchMediator = ContactSearchMediator(this, contactSearchRecycler, FeatureFlags.shareSelectionLimit(), !isSingleRecipientSelection(), this::getConfiguration)
|
||||
|
||||
callback = findListener()!!
|
||||
disposables.bindTo(viewLifecycleOwner.lifecycle)
|
||||
@@ -155,24 +155,11 @@ class MultiselectForwardFragment :
|
||||
}
|
||||
|
||||
sendButton.setOnClickListener {
|
||||
sendButton.isEnabled = false
|
||||
|
||||
StoryDialogs.guardWithAddToYourStoryDialog(
|
||||
requireContext(),
|
||||
contactSearchMediator.getSelectedContacts(),
|
||||
onAddToStory = {
|
||||
performSend()
|
||||
},
|
||||
onEditViewers = {
|
||||
sendButton.isEnabled = true
|
||||
HideStoryFromDialogFragment().show(childFragmentManager, null)
|
||||
},
|
||||
onCancel = {
|
||||
sendButton.isEnabled = true
|
||||
}
|
||||
)
|
||||
onSend(it)
|
||||
}
|
||||
|
||||
sendButton.visible = !isSingleRecipientSelection()
|
||||
|
||||
shareSelectionRecycler.adapter = shareSelectionAdapter
|
||||
|
||||
bottomBar.visible = false
|
||||
@@ -180,6 +167,11 @@ class MultiselectForwardFragment :
|
||||
container.addView(bottomBarAndSpacer)
|
||||
|
||||
contactSearchMediator.getSelectionState().observe(viewLifecycleOwner) { contactSelection ->
|
||||
if (contactSelection.isNotEmpty() && isSingleRecipientSelection()) {
|
||||
onSend(sendButton)
|
||||
return@observe
|
||||
}
|
||||
|
||||
shareSelectionAdapter.submitList(contactSelection.mapIndexed { index, key -> ShareSelectionMappingModel(key.requireShareContact(), index == 0) })
|
||||
|
||||
addMessage.visible = !forceDisableAddMessage && contactSelection.any { key -> key !is ContactSearchKey.RecipientSearchKey.Story } && getMultiShareArgs().isNotEmpty()
|
||||
@@ -276,6 +268,25 @@ class MultiselectForwardFragment :
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun onSend(sendButton: View) {
|
||||
sendButton.isEnabled = false
|
||||
|
||||
StoryDialogs.guardWithAddToYourStoryDialog(
|
||||
requireContext(),
|
||||
contactSearchMediator.getSelectedContacts(),
|
||||
onAddToStory = {
|
||||
performSend()
|
||||
},
|
||||
onEditViewers = {
|
||||
sendButton.isEnabled = true
|
||||
HideStoryFromDialogFragment().show(childFragmentManager, null)
|
||||
},
|
||||
onCancel = {
|
||||
sendButton.isEnabled = true
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun performSend() {
|
||||
viewModel.send(addMessage.text.toString(), contactSearchMediator.getSelectedContacts())
|
||||
}
|
||||
@@ -390,6 +401,10 @@ class MultiselectForwardFragment :
|
||||
return Util.isDefaultSmsProvider(requireContext()) && requireArguments().getBoolean(ARG_CAN_SEND_TO_NON_PUSH)
|
||||
}
|
||||
|
||||
private fun isSingleRecipientSelection(): Boolean {
|
||||
return requireArguments().getBoolean(ARG_SELECT_SINGLE_RECIPIENT, false)
|
||||
}
|
||||
|
||||
private fun isSelectedMediaValidForStories(): Boolean {
|
||||
return requireListener<Callback>().canSendMediaToStories() && getMultiShareArgs().all { it.isValidForStories }
|
||||
}
|
||||
@@ -422,6 +437,7 @@ class MultiselectForwardFragment :
|
||||
const val ARG_TITLE = "multiselect.forward.fragment.title"
|
||||
const val ARG_FORCE_DISABLE_ADD_MESSAGE = "multiselect.forward.fragment.force.disable.add.message"
|
||||
const val ARG_FORCE_SELECTION_ONLY = "multiselect.forward.fragment.force.disable.add.message"
|
||||
const val ARG_SELECT_SINGLE_RECIPIENT = "multiselect.forward.framgent.select.single.recipient"
|
||||
const val RESULT_KEY = "result_key"
|
||||
const val RESULT_SELECTION = "result_selection_recipients"
|
||||
const val RESULT_SENT = "result_sent"
|
||||
@@ -460,6 +476,7 @@ class MultiselectForwardFragment :
|
||||
putInt(ARG_TITLE, multiselectForwardFragmentArgs.title)
|
||||
putBoolean(ARG_FORCE_DISABLE_ADD_MESSAGE, multiselectForwardFragmentArgs.forceDisableAddMessage)
|
||||
putBoolean(ARG_FORCE_SELECTION_ONLY, multiselectForwardFragmentArgs.forceSelectionOnly)
|
||||
putBoolean(ARG_SELECT_SINGLE_RECIPIENT, multiselectForwardFragmentArgs.selectSingleRecipient)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,13 +28,15 @@ import java.util.function.Consumer
|
||||
* @param title The title to display at the top of the sheet
|
||||
* @param forceDisableAddMessage Hide the add message field even if it would normally be available.
|
||||
* @param forceSelectionOnly Force the fragment to only select recipients, never actually performing the send.
|
||||
* @param selectSingleRecipient Only allow the selection of a single recipient.
|
||||
*/
|
||||
class MultiselectForwardFragmentArgs @JvmOverloads constructor(
|
||||
val canSendToNonPush: Boolean,
|
||||
val multiShareArgs: List<MultiShareArgs> = listOf(),
|
||||
@StringRes val title: Int = R.string.MultiselectForwardFragment__forward_to,
|
||||
val forceDisableAddMessage: Boolean = false,
|
||||
val forceSelectionOnly: Boolean = false
|
||||
val forceSelectionOnly: Boolean = false,
|
||||
val selectSingleRecipient: Boolean = false
|
||||
) {
|
||||
|
||||
companion object {
|
||||
|
||||
Reference in New Issue
Block a user