Fix multiple chatcolors issues from beta feedback.

- Fix issue where custom color would come out as black
- Completely remove mask view in favour of using the item decoration.
- Fix issue where video gifs wouldn't "cut through" bubble.
- Fix issue where multiselect shade would only appear if bottom or top item was not visible
fork-5.53.8
Alex Hart 2021-09-29 13:38:34 -03:00 zatwierdzone przez Cody Henthorne
rodzic 705839068a
commit 7e91132e7e
15 zmienionych plików z 153 dodań i 337 usunięć

Wyświetl plik

@ -66,7 +66,7 @@ public interface BindableConversationItem extends Unbindable, GiphyMp4Playable,
void onAddToContactsClicked(@NonNull Contact contact);
void onMessageSharedContactClicked(@NonNull List<Recipient> choices);
void onInviteSharedContactClicked(@NonNull List<Recipient> choices);
void onReactionClicked(@NonNull View reactionTarget, long messageId, boolean isMms);
void onReactionClicked(@NonNull MultiselectPart multiselectPart, long messageId, boolean isMms);
void onGroupMemberClicked(@NonNull RecipientId recipientId, @NonNull GroupId groupId);
void onMessageWithErrorClicked(@NonNull MessageRecord messageRecord);
void onMessageWithRecaptchaNeededClicked(@NonNull MessageRecord messageRecord);

Wyświetl plik

@ -1,152 +0,0 @@
package org.thoughtcrime.securesms.components;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class MaskView extends View {
private MaskTarget maskTarget;
private ViewGroup activityContentView;
private Paint maskPaint;
private Rect drawingRect = new Rect();
private float targetParentTranslationY;
private final ViewTreeObserver.OnDrawListener onDrawListener = this::invalidate;
public MaskView(@NonNull Context context) {
super(context);
}
public MaskView(@NonNull Context context, @Nullable AttributeSet attributeSet) {
super(context, attributeSet);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
maskPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
setLayerType(LAYER_TYPE_HARDWARE, maskPaint);
maskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
activityContentView = getRootView().findViewById(android.R.id.content);
}
public void setTarget(@Nullable MaskTarget maskTarget) {
if (this.maskTarget != null) {
removeOnDrawListener(this.maskTarget, onDrawListener);
}
this.maskTarget = maskTarget;
if (this.maskTarget != null) {
addOnDrawListener(maskTarget, onDrawListener);
}
invalidate();
}
public void setTargetParentTranslationY(float targetParentTranslationY) {
this.targetParentTranslationY = targetParentTranslationY;
}
@Override
protected void onDraw(@NonNull Canvas canvas) {
super.onDraw(canvas);
if (nothingToMask(maskTarget)) {
return;
}
maskTarget.getPrimaryTarget().getDrawingRect(drawingRect);
activityContentView.offsetDescendantRectToMyCoords(maskTarget.getPrimaryTarget(), drawingRect);
drawingRect.top += targetParentTranslationY;
drawingRect.bottom += targetParentTranslationY;
Bitmap mask = Bitmap.createBitmap(maskTarget.getPrimaryTarget().getWidth(), drawingRect.height(), Bitmap.Config.ARGB_8888);
Canvas maskCanvas = new Canvas(mask);
maskTarget.draw(maskCanvas);
canvas.clipRect(drawingRect.left, Math.max(drawingRect.top, getTop() + getPaddingTop()), drawingRect.right, Math.min(drawingRect.bottom, getBottom() - getPaddingBottom()));
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) maskTarget.getPrimaryTarget().getLayoutParams();
canvas.drawBitmap(mask, params.leftMargin, drawingRect.top, maskPaint);
mask.recycle();
}
private static void removeOnDrawListener(@NonNull MaskTarget maskTarget, @NonNull ViewTreeObserver.OnDrawListener onDrawListener) {
for (View view : maskTarget.getAllTargets()) {
if (view != null) {
view.getViewTreeObserver().removeOnDrawListener(onDrawListener);
}
}
}
private static void addOnDrawListener(@NonNull MaskTarget maskTarget, @NonNull ViewTreeObserver.OnDrawListener onDrawListener) {
for (View view : maskTarget.getAllTargets()) {
if (view != null) {
view.getViewTreeObserver().addOnDrawListener(onDrawListener);
}
}
}
private static boolean nothingToMask(@Nullable MaskTarget maskTarget) {
if (maskTarget == null) {
return true;
}
for (View view : maskTarget.getAllTargets()) {
if (view == null || !view.isAttachedToWindow()) {
return true;
}
}
return false;
}
public static class MaskTarget {
private final View primaryTarget;
public MaskTarget(@NonNull View primaryTarget) {
this.primaryTarget = primaryTarget;
}
final @NonNull View getPrimaryTarget() {
return primaryTarget;
}
protected @NonNull List<View> getAllTargets() {
return Collections.singletonList(primaryTarget);
}
protected void draw(@NonNull Canvas canvas) {
primaryTarget.draw(canvas);
}
}
}

Wyświetl plik

@ -115,7 +115,6 @@ import org.thoughtcrime.securesms.components.HidingLinearLayout;
import org.thoughtcrime.securesms.components.InputAwareLayout;
import org.thoughtcrime.securesms.components.InputPanel;
import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout.OnKeyboardShownListener;
import org.thoughtcrime.securesms.components.MaskView;
import org.thoughtcrime.securesms.components.SendButton;
import org.thoughtcrime.securesms.components.TooltipPopup;
import org.thoughtcrime.securesms.components.TypingStatusSender;
@ -158,8 +157,6 @@ import org.thoughtcrime.securesms.database.DraftDatabase;
import org.thoughtcrime.securesms.database.DraftDatabase.Draft;
import org.thoughtcrime.securesms.database.DraftDatabase.Drafts;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.IdentityDatabase;
import org.thoughtcrime.securesms.database.model.IdentityRecord;
import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus;
import org.thoughtcrime.securesms.database.MentionUtil;
import org.thoughtcrime.securesms.database.MentionUtil.UpdatedBodyAndMentions;
@ -168,6 +165,7 @@ import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.database.identity.IdentityRecordList;
import org.thoughtcrime.securesms.database.model.IdentityRecord;
import org.thoughtcrime.securesms.database.model.Mention;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
@ -2430,11 +2428,12 @@ public class ConversationActivity extends PassphraseRequiredActivity
@Override
public void onReactWithAnyEmojiDialogDismissed() {
reactionDelegate.hideMask();
reactionDelegate.hide();
}
@Override
public void onReactWithAnyEmojiSelected(@NonNull String emoji) {
reactionDelegate.hide();
}
@Override
@ -3334,7 +3333,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
@Override
public void onReactionsDialogDismissed() {
reactionDelegate.hideMask();
fragment.clearFocusedItem();
}
@Override
@ -3669,19 +3668,13 @@ public class ConversationActivity extends PassphraseRequiredActivity
}
@Override
public void handleReaction(@NonNull MaskView.MaskTarget maskTarget,
@NonNull ConversationMessage conversationMessage,
public void handleReaction(@NonNull ConversationMessage conversationMessage,
@NonNull Toolbar.OnMenuItemClickListener toolbarListener,
@NonNull ConversationReactionOverlay.OnHideListener onHideListener)
{
reactionDelegate.setOnToolbarItemClickedListener(toolbarListener);
reactionDelegate.setOnHideListener(onHideListener);
reactionDelegate.show(this, maskTarget, recipient.get(), conversationMessage, inputAreaHeight(), groupViewModel.isNonAdminInAnnouncementGroup());
}
@Override
public void onListVerticalTranslationChanged(float translationY) {
reactionDelegate.setListVerticalTranslation(translationY);
reactionDelegate.show(this, recipient.get(), conversationMessage, groupViewModel.isNonAdminInAnnouncementGroup());
}
@Override
@ -3699,11 +3692,6 @@ public class ConversationActivity extends PassphraseRequiredActivity
}
}
@Override
public void handleReactionDetails(@NonNull MaskView.MaskTarget maskTarget) {
reactionDelegate.showMask(maskTarget, titleView.getMeasuredHeight(), inputAreaHeight());
}
@Override
public void onVoiceNotePause(@NonNull Uri uri) {
voiceNoteMediaController.pausePlayback(uri);

Wyświetl plik

@ -75,7 +75,6 @@ import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.VerifyIdentityActivity;
import org.thoughtcrime.securesms.components.ConversationScrollToView;
import org.thoughtcrime.securesms.components.ConversationTypingView;
import org.thoughtcrime.securesms.components.MaskView;
import org.thoughtcrime.securesms.components.TooltipPopup;
import org.thoughtcrime.securesms.components.TypingStatusRepository;
import org.thoughtcrime.securesms.components.recyclerview.SmoothScrollingLinearLayoutManager;
@ -224,6 +223,7 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
private GiphyMp4ProjectionRecycler giphyMp4ProjectionRecycler;
private Colorizer colorizer;
private ConversationUpdateTick conversationUpdateTick;
private MultiselectItemDecoration multiselectItemDecoration;
public static void prepare(@NonNull Context context) {
FrameLayout parent = new FrameLayout(context);
@ -275,22 +275,21 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
return adapter.getSelectedItems().contains(multiselectPart);
}
});
MultiselectItemDecoration multiselectItemDecoration = new MultiselectItemDecoration(requireContext(),
() -> conversationViewModel.getWallpaper().getValue(),
multiselectItemAnimator::getSelectedProgressForPart,
multiselectItemAnimator::isInitialAnimation);
multiselectItemDecoration = new MultiselectItemDecoration(requireContext(),
() -> conversationViewModel.getWallpaper().getValue(),
multiselectItemAnimator::getSelectedProgressForPart,
multiselectItemAnimator::isInitialAnimation);
list.setHasFixedSize(false);
list.setLayoutManager(layoutManager);
RecyclerViewColorizer recyclerViewColorizer = new RecyclerViewColorizer(list);
list.addItemDecoration(multiselectItemDecoration);
list.setItemAnimator(multiselectItemAnimator);
getViewLifecycleOwner().getLifecycle().addObserver(multiselectItemDecoration);
if (Build.VERSION.SDK_INT >= 31) {
list.setOverScrollMode(View.OVER_SCROLL_NEVER);
}
snapToTopDataObserver = new ConversationSnapToTopDataObserver(list, new ConversationScrollRequestValidator());
conversationBanner = (ConversationBannerView) inflater.inflate(R.layout.conversation_item_banner, container, false);
topLoadMoreView = (ViewSwitcher) inflater.inflate(R.layout.load_more_header, container, false);
@ -319,6 +318,7 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
this.messageCountsViewModel = ViewModelProviders.of(requireActivity()).get(MessageCountsViewModel.class);
this.conversationViewModel = ViewModelProviders.of(requireActivity(), new ConversationViewModel.Factory()).get(ConversationViewModel.class);
conversationViewModel.getChatColors().observe(getViewLifecycleOwner(), recyclerViewColorizer::setChatColors);
conversationViewModel.getMessages().observe(getViewLifecycleOwner(), messages -> {
ConversationAdapter adapter = getListAdapter();
if (adapter != null) {
@ -350,9 +350,6 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
updateToolbarDependentMargins();
colorizer = new Colorizer();
RecyclerViewColorizer recyclerViewColorizer = new RecyclerViewColorizer(list);
conversationViewModel.getChatColors().observe(getViewLifecycleOwner(), recyclerViewColorizer::setChatColors);
conversationViewModel.getNameColorsMap().observe(getViewLifecycleOwner(), nameColorsMap -> {
colorizer.onNameColorsChanged(nameColorsMap);
@ -387,11 +384,9 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
return callback;
}
private @NonNull MaskView.MaskTarget getMaskTarget(@NonNull View itemView) {
int adapterPosition = list.getChildAdapterPosition(itemView);
View videoPlayer = giphyMp4ProjectionRecycler.getVideoPlayerAtAdapterPosition(adapterPosition);
return new ConversationItemMaskTarget((ConversationItem) itemView, videoPlayer);
public void clearFocusedItem() {
multiselectItemDecoration.setFocusedItem(null);
list.invalidateItemDecorations();
}
private void setupListLayoutListeners() {
@ -419,9 +414,6 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
list.setTranslationY(Math.min(0, -chTop));
list.setOverScrollMode(RecyclerView.OVER_SCROLL_NEVER);
}
int offset = WindowUtil.isStatusBarPresent(requireActivity().getWindow()) ? ViewUtil.getStatusBarHeight(list) : 0;
listener.onListVerticalTranslationChanged(list.getTranslationY() - offset);
}
private void updateConversationItemTimestamps() {
@ -1285,14 +1277,11 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
void onMessageActionToolbarOpened();
void onForwardClicked();
void onMessageRequest(@NonNull MessageRequestViewModel viewModel);
void handleReaction(@NonNull MaskView.MaskTarget maskTarget,
@NonNull ConversationMessage conversationMessage,
void handleReaction(@NonNull ConversationMessage conversationMessage,
@NonNull Toolbar.OnMenuItemClickListener toolbarListener,
@NonNull ConversationReactionOverlay.OnHideListener onHideListener);
void onCursorChanged();
void onListVerticalTranslationChanged(float translationY);
void onMessageWithErrorClicked(@NonNull MessageRecord messageRecord);
void handleReactionDetails(@NonNull MaskView.MaskTarget maskTarget);
void onVoiceNotePause(@NonNull Uri uri);
void onVoiceNotePlay(@NonNull Uri uri, long messageId, double progress);
void onVoiceNoteSeekTo(@NonNull Uri uri, double progress);
@ -1402,14 +1391,19 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
(!recipient.get().isGroup() || recipient.get().isActiveGroup()) &&
((ConversationAdapter) list.getAdapter()).getSelectedItems().isEmpty())
{
multiselectItemDecoration.setFocusedItem(new MultiselectPart.Message(item.getConversationMessage()));
list.invalidateItemDecorations();
isReacting = true;
list.setLayoutFrozen(true);
listener.handleReaction(getMaskTarget(itemView), item.getConversationMessage(), new ReactionsToolbarListener(item.getConversationMessage()), () -> {
listener.handleReaction(item.getConversationMessage(), new ReactionsToolbarListener(item.getConversationMessage()), () -> {
isReacting = false;
list.setLayoutFrozen(false);
WindowUtil.setLightStatusBarFromTheme(requireActivity());
clearFocusedItem();
});
} else {
clearFocusedItem();
((ConversationAdapter) list.getAdapter()).toggleSelection(item);
list.getAdapter().notifyDataSetChanged();
@ -1550,10 +1544,10 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
}
@Override
public void onReactionClicked(@NonNull View reactionTarget, long messageId, boolean isMms) {
public void onReactionClicked(@NonNull MultiselectPart multiselectPart, long messageId, boolean isMms) {
if (getContext() == null) return;
listener.handleReactionDetails(getMaskTarget(reactionTarget));
multiselectItemDecoration.setFocusedItem(multiselectPart);
ReactionsBottomSheetDialogFragment.create(messageId, isMms).show(requireFragmentManager(), null);
}

Wyświetl plik

@ -142,6 +142,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
/**
* A view that displays an individual conversation item within a conversation
@ -1368,7 +1369,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
reactionsView.setOnClickListener(v -> {
if (eventListener == null) return;
eventListener.onReactionClicked(this, current.getId(), current.isMms());
eventListener.onReactionClicked(new MultiselectPart.Message(conversationMessage), current.getId(), current.isMms());
});
}
@ -1720,10 +1721,16 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
if (messageRecord.isOutgoing() &&
!hasNoBubble(messageRecord) &&
!messageRecord.isRemoteDelete() &&
bodyBubbleCorners != null &&
bodyBubble.getProjections().isEmpty())
bodyBubbleCorners != null)
{
projections.add(Projection.relativeToViewRoot(bodyBubble, bodyBubbleCorners).translateX(bodyBubble.getTranslationX()));
Projection bodyBubbleToRoot = Projection.relativeToViewRoot(bodyBubble, bodyBubbleCorners).translateX(bodyBubble.getTranslationX());
Projection videoToBubble = bodyBubble.getVideoPlayerProjection();
if (videoToBubble != null) {
Projection videoToRoot = Projection.translateFromDescendantToParentCoords(videoToBubble, bodyBubble, (ViewGroup) getRootView());
projections.addAll(Projection.getCapAndTail(bodyBubbleToRoot, videoToRoot));
} else {
projections.add(bodyBubbleToRoot);
}
}
if (messageRecord.isOutgoing() &&

Wyświetl plik

@ -69,6 +69,10 @@ public class ConversationItemBodyBubble extends LinearLayout {
clipProjectionDrawable.setProjections(getProjections());
}
public @Nullable Projection getVideoPlayerProjection() {
return videoPlayerProjection;
}
public @NonNull Set<Projection> getProjections() {
return Stream.of(quoteViewProjection, videoPlayerProjection)
.filterNot(Objects::isNull)

Wyświetl plik

@ -1,66 +0,0 @@
package org.thoughtcrime.securesms.conversation;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.components.MaskView;
import org.thoughtcrime.securesms.util.Projection;
import java.util.Arrays;
import java.util.List;
/**
* Masking area to ensure proper rendering of Reactions overlay.
*/
public final class ConversationItemMaskTarget extends MaskView.MaskTarget {
private final ConversationItem conversationItem;
private final View videoContainer;
private final Paint paint;
public ConversationItemMaskTarget(@NonNull ConversationItem conversationItem,
@Nullable View videoContainer)
{
super(conversationItem);
this.conversationItem = conversationItem;
this.videoContainer = videoContainer;
this.paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.FILL);
}
@Override
protected @NonNull List<View> getAllTargets() {
if (videoContainer == null) {
return super.getAllTargets();
} else {
return Arrays.asList(conversationItem, videoContainer);
}
}
@Override
protected void draw(@NonNull Canvas canvas) {
super.draw(canvas);
List<Projection> projections = Stream.of(conversationItem.getColorizerProjections()).map(p ->
Projection.translateFromRootToDescendantCoords(p, conversationItem)
).toList();
if (videoContainer != null) {
projections.add(conversationItem.getGiphyMp4PlayableProjection((RecyclerView) conversationItem.getParent()));
}
for (Projection projection : projections) {
canvas.drawPath(projection.getPath(), paint);
}
}
}

Wyświetl plik

@ -7,7 +7,6 @@ import android.view.MotionEvent;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.Toolbar;
import org.thoughtcrime.securesms.components.MaskView;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.views.Stub;
@ -27,7 +26,6 @@ final class ConversationReactionDelegate {
private ConversationReactionOverlay.OnReactionSelectedListener onReactionSelectedListener;
private Toolbar.OnMenuItemClickListener onToolbarItemClickedListener;
private ConversationReactionOverlay.OnHideListener onHideListener;
private float translationY;
ConversationReactionDelegate(@NonNull Stub<ConversationReactionOverlay> overlayStub) {
this.overlayStub = overlayStub;
@ -38,17 +36,11 @@ final class ConversationReactionDelegate {
}
void show(@NonNull Activity activity,
@NonNull MaskView.MaskTarget maskTarget,
@NonNull Recipient conversationRecipient,
@NonNull ConversationMessage conversationMessage,
int maskPaddingBottom,
boolean isNonAdminInAnnouncementGroup)
{
resolveOverlay().show(activity, maskTarget, conversationRecipient, conversationMessage, maskPaddingBottom, lastSeenDownPoint, isNonAdminInAnnouncementGroup);
}
void showMask(@NonNull MaskView.MaskTarget maskTarget, int maskPaddingTop, int maskPaddingBottom) {
resolveOverlay().showMask(maskTarget, maskPaddingTop, maskPaddingBottom);
resolveOverlay().show(activity, conversationRecipient, conversationMessage, lastSeenDownPoint, isNonAdminInAnnouncementGroup);
}
void hide() {
@ -59,10 +51,6 @@ final class ConversationReactionDelegate {
overlayStub.get().hideForReactWithAny();
}
void hideMask() {
overlayStub.get().hideMask();
}
void setOnReactionSelectedListener(@NonNull ConversationReactionOverlay.OnReactionSelectedListener onReactionSelectedListener) {
this.onReactionSelectedListener = onReactionSelectedListener;
@ -87,14 +75,6 @@ final class ConversationReactionDelegate {
}
}
void setListVerticalTranslation(float translationY) {
this.translationY = translationY;
if (overlayStub.resolved()) {
overlayStub.get().setListVerticalTranslation(translationY);
}
}
@NonNull MessageRecord getMessageRecord() {
if (!overlayStub.resolved()) {
throw new IllegalStateException("Cannot call getMessageRecord right now.");
@ -118,7 +98,6 @@ final class ConversationReactionDelegate {
ConversationReactionOverlay overlay = overlayStub.get();
overlay.requestFitSystemWindows();
overlay.setListVerticalTranslation(translationY);
overlay.setOnHideListener(onHideListener);
overlay.setOnToolbarItemClickedListener(onToolbarItemClickedListener);
overlay.setOnReactionSelectedListener(onReactionSelectedListener);

Wyświetl plik

@ -28,7 +28,6 @@ import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.animation.AnimationCompleteListener;
import org.thoughtcrime.securesms.components.MaskView;
import org.thoughtcrime.securesms.components.emoji.EmojiImageView;
import org.thoughtcrime.securesms.components.emoji.EmojiUtil;
import org.thoughtcrime.securesms.database.model.MessageRecord;
@ -40,7 +39,6 @@ import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.WindowUtil;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
@ -72,7 +70,6 @@ public final class ConversationReactionOverlay extends RelativeLayout {
private ConstraintLayout foregroundView;
private View selectedView;
private EmojiImageView[] emojiViews;
private MaskView maskView;
private Toolbar toolbar;
private float touchDownDeadZoneSize;
@ -112,7 +109,6 @@ public final class ConversationReactionOverlay extends RelativeLayout {
backgroundView = findViewById(R.id.conversation_reaction_scrubber_background);
foregroundView = findViewById(R.id.conversation_reaction_scrubber_foreground);
selectedView = findViewById(R.id.conversation_reaction_current_selection_indicator);
maskView = findViewById(R.id.conversation_reaction_mask);
toolbar = findViewById(R.id.conversation_reaction_toolbar);
toolbar.setOnMenuItemClickListener(this::handleToolbarItemClicked);
@ -144,15 +140,9 @@ public final class ConversationReactionOverlay extends RelativeLayout {
initAnimators();
}
public void setListVerticalTranslation(float translationY) {
maskView.setTargetParentTranslationY(translationY);
}
public void show(@NonNull Activity activity,
@NonNull MaskView.MaskTarget maskTarget,
@NonNull Recipient conversationRecipient,
@NonNull ConversationMessage conversationMessage,
int maskPaddingBottom,
@NonNull PointF lastSeenDownPoint,
boolean isNonAdminInAnnouncementGroup)
{
@ -195,9 +185,6 @@ public final class ConversationReactionOverlay extends RelativeLayout {
verticalScrubBoundary.update(lastSeenDownPoint.y - distanceFromTouchDownPointToTopOfScrubberDeadZone,
lastSeenDownPoint.y + distanceFromTouchDownPointToBottomOfScrubberDeadZone);
maskView.setPadding(0, 0, 0, maskPaddingBottom);
maskView.setTarget(maskTarget);
hideAnimatorSet.end();
toolbar.setVisibility(VISIBLE);
setVisibility(View.VISIBLE);
@ -214,18 +201,7 @@ public final class ConversationReactionOverlay extends RelativeLayout {
}
}
public void showMask(@NonNull MaskView.MaskTarget maskTarget, int maskPaddingTop, int maskPaddingBottom) {
maskView.setPadding(0, maskPaddingTop, 0, maskPaddingBottom);
maskView.setTarget(maskTarget);
hideAnimatorSet.end();
toolbar.setVisibility(GONE);
setVisibility(VISIBLE);
revealMaskAnimatorSet.start();
}
public void hide() {
maskView.setTarget(null);
hideInternal(hideAnimatorSet, onHideListener);
}
@ -233,14 +209,6 @@ public final class ConversationReactionOverlay extends RelativeLayout {
hideInternal(hideAnimatorSet, null);
}
public void hideMask() {
hideMaskAnimatorSet.start();
if (onHideListener != null) {
onHideListener.onHide();
}
}
private void hideInternal(@NonNull AnimatorSet hideAnimatorSet, @Nullable OnHideListener onHideListener) {
overlayState = OverlayState.HIDDEN;
@ -540,7 +508,6 @@ public final class ConversationReactionOverlay extends RelativeLayout {
.toList();
Animator overlayRevealAnim = AnimatorInflaterCompat.loadAnimator(getContext(), android.R.animator.fade_in);
overlayRevealAnim.setTarget(maskView);
overlayRevealAnim.setDuration(duration);
reveals.add(overlayRevealAnim);
@ -575,7 +542,6 @@ public final class ConversationReactionOverlay extends RelativeLayout {
.toList();
Animator overlayHideAnim = AnimatorInflaterCompat.loadAnimator(getContext(), android.R.animator.fade_out);
overlayHideAnim.setTarget(maskView);
overlayHideAnim.setDuration(duration);
Animator backgroundHideAnim = AnimatorInflaterCompat.loadAnimator(getContext(), android.R.animator.fade_out);

Wyświetl plik

@ -118,6 +118,7 @@ class RecyclerViewColorizer(private val recyclerView: RecyclerView) {
mask.setBounds(0, 0, parent.width, parent.height)
mask.draw(canvas)
} else {
colorPaint.color = chatColors.asSingleColor()
canvas.drawRect(
0f,
0f,

Wyświetl plik

@ -9,9 +9,11 @@ import android.graphics.Path
import android.graphics.Rect
import android.graphics.Region
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.content.ContextCompat
import androidx.core.view.children
import androidx.core.view.forEach
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.recyclerview.widget.RecyclerView
@ -54,6 +56,12 @@ class MultiselectItemDecoration(
private var checkedBitmap: Bitmap? = null
private var focusedItem: MultiselectPart? = null
fun setFocusedItem(multiselectPart: MultiselectPart?) {
this.focusedItem = multiselectPart
}
override fun onCreate(owner: LifecycleOwner) {
val bitmap = Bitmap.createBitmap(circleRadius * 2, circleRadius * 2, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
@ -67,6 +75,8 @@ class MultiselectItemDecoration(
checkedBitmap = null
}
private val shadeColor = ContextCompat.getColor(context, R.color.reactions_screen_shade_color)
private val unselectedPaint = Paint().apply {
isAntiAlias = true
strokeWidth = 1.5f
@ -102,6 +112,7 @@ class MultiselectItemDecoration(
val adapter = parent.adapter as ConversationAdapter
if (adapter.selectedItems.isEmpty()) {
drawFocusShadeUnderIfNecessary(canvas, parent)
return
}
@ -116,7 +127,7 @@ class MultiselectItemDecoration(
val parts: MultiselectCollection = child.conversationMessage.multiselectCollection
val projections: List<Projection> = child.colorizerProjections
val projections: List<Projection> = child.colorizerProjections + if (child.canPlayContent()) listOf(child.getGiphyMp4PlayableProjection(child.rootView as ViewGroup)) else emptyList()
path.reset()
projections.forEach { it.applyToPath(path) }
@ -148,6 +159,7 @@ class MultiselectItemDecoration(
override fun onDrawOver(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) {
val adapter = parent.adapter as ConversationAdapter
if (adapter.selectedItems.isEmpty()) {
drawFocusShadeOverIfNecessary(canvas, parent)
return
}
@ -285,4 +297,48 @@ class MultiselectItemDecoration(
child.translationX = 0f
}
}
private fun drawFocusShadeUnderIfNecessary(canvas: Canvas, parent: RecyclerView) {
val inFocus = focusedItem
if (inFocus != null) {
path.reset()
canvas.save()
parent.forEach { child ->
if (child is Multiselectable && child.conversationMessage == inFocus.conversationMessage) {
path.addRect(child.left.toFloat(), child.top.toFloat(), child.right.toFloat(), child.bottom.toFloat(), Path.Direction.CW)
child.colorizerProjections.forEach {
path.op(it.path, Path.Op.DIFFERENCE)
}
if (child.canPlayContent()) {
val mp4GifProjection = child.getGiphyMp4PlayableProjection(child.rootView as ViewGroup)
path.op(mp4GifProjection.path, Path.Op.DIFFERENCE)
}
}
}
canvas.clipPath(path)
canvas.drawColor(shadeColor)
canvas.restore()
}
}
private fun drawFocusShadeOverIfNecessary(canvas: Canvas, parent: RecyclerView) {
val inFocus = focusedItem
if (inFocus != null) {
path.reset()
canvas.save()
parent.forEach { child ->
if (child is Multiselectable && child.conversationMessage == inFocus.conversationMessage) {
path.addRect(child.left.toFloat(), child.top.toFloat(), child.right.toFloat(), child.bottom.toFloat(), Path.Direction.CW)
}
}
canvas.clipPath(path, Region.Op.DIFFERENCE)
canvas.drawColor(shadeColor)
canvas.restore()
}
}
}

Wyświetl plik

@ -3,8 +3,9 @@ package org.thoughtcrime.securesms.conversation.mutiselect
import android.view.View
import org.thoughtcrime.securesms.conversation.ConversationMessage
import org.thoughtcrime.securesms.conversation.colors.Colorizable
import org.thoughtcrime.securesms.giph.mp4.GiphyMp4Playable
interface Multiselectable : Colorizable {
interface Multiselectable : Colorizable, GiphyMp4Playable {
val conversationMessage: ConversationMessage
fun getTopBoundaryOfMultiselectPart(multiselectPart: MultiselectPart): Int

Wyświetl plik

@ -39,7 +39,7 @@ public final class GiphyMp4ProjectionRecycler implements GiphyMp4PlaybackControl
for (final GiphyMp4Playable holder : holders) {
if (playbackSet.contains(holder.getAdapterPosition())) {
startPlayback(acquireHolderForPosition(holder.getAdapterPosition()), holder);
startPlayback(recyclerView, acquireHolderForPosition(holder.getAdapterPosition()), holder);
} else {
holder.showProjectionArea();
}
@ -107,16 +107,22 @@ public final class GiphyMp4ProjectionRecycler implements GiphyMp4PlaybackControl
holder.setCorners(projection.getCorners());
}
private void startPlayback(@NonNull GiphyMp4ProjectionPlayerHolder holder, @NonNull GiphyMp4Playable giphyMp4Playable) {
private void startPlayback(@NonNull RecyclerView parent, @NonNull GiphyMp4ProjectionPlayerHolder holder, @NonNull GiphyMp4Playable giphyMp4Playable) {
if (!Objects.equals(holder.getMediaItem(), giphyMp4Playable.getMediaItem())) {
holder.setOnPlaybackReady(null);
giphyMp4Playable.showProjectionArea();
holder.show();
holder.setOnPlaybackReady(giphyMp4Playable::hideProjectionArea);
holder.setOnPlaybackReady(() -> {
giphyMp4Playable.hideProjectionArea();
parent.invalidateItemDecorations();
});
holder.playContent(giphyMp4Playable.getMediaItem(), giphyMp4Playable.getPlaybackPolicyEnforcer());
} else {
holder.setOnPlaybackReady(giphyMp4Playable::hideProjectionArea);
holder.setOnPlaybackReady(() -> {
giphyMp4Playable.hideProjectionArea();
parent.invalidateItemDecorations();
});
}
}

Wyświetl plik

@ -14,6 +14,9 @@ import androidx.recyclerview.widget.RecyclerView;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.components.CornerMask;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
/**
@ -159,6 +162,42 @@ public final class Projection {
return new Projection(viewBounds.left, viewBounds.top, descendantProjection.width, descendantProjection.height, descendantProjection.corners);
}
public static @NonNull List<Projection> getCapAndTail(@NonNull Projection parentProjection, @NonNull Projection childProjection) {
if (parentProjection.equals(childProjection)) {
return Collections.emptyList();
}
float topX = parentProjection.x;
float topY = parentProjection.y;
int topWidth = parentProjection.getWidth();
int topHeight = (int) (childProjection.y - parentProjection.y);
final Corners topCorners;
Corners parentCorners = parentProjection.getCorners();
if (parentCorners != null) {
topCorners = new Corners(parentCorners.topLeft, parentCorners.topRight, 0f, 0f);
} else {
topCorners = null;
}
float bottomX = parentProjection.x;
float bottomY = parentProjection.y + topHeight + childProjection.getHeight();
int bottomWidth = parentProjection.getWidth();
int bottomHeight = (int) ((parentProjection.y + parentProjection.getHeight()) - bottomY);
final Corners bottomCorners;
if (parentCorners != null) {
bottomCorners = new Corners(0f, 0f, parentCorners.bottomRight, parentCorners.bottomLeft);
} else {
bottomCorners = null;
}
return Arrays.asList(
new Projection(topX, topY, topWidth, topHeight, topCorners),
new Projection(bottomX, bottomY, bottomWidth, bottomHeight, bottomCorners)
);
}
public static final class Corners {
private final float topLeft;
private final float topRight;

Wyświetl plik

@ -13,13 +13,6 @@
android:visibility="gone"
tools:visibility="visible">
<org.thoughtcrime.securesms.components.MaskView
android:id="@+id/conversation_reaction_mask"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0"
android:background="@color/reactions_screen_shade_color" />
<include
android:id="@+id/conversation_reaction_toolbar"
layout="@layout/conversation_reaction_long_press_toolbar" />