Skip to content

Commit

Permalink
Reactions UX polish.
Browse files Browse the repository at this point in the history
  • Loading branch information
alex-signal authored and greyson-signal committed Jul 30, 2020
1 parent 0950235 commit 9d3764c
Show file tree
Hide file tree
Showing 32 changed files with 488 additions and 283 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ interface EventListener {
void onAddToContactsClicked(@NonNull Contact contact);
void onMessageSharedContactClicked(@NonNull List<Recipient> choices);
void onInviteSharedContactClicked(@NonNull List<Recipient> choices);
void onReactionClicked(long messageId, boolean isMms);
void onReactionClicked(@NonNull View reactionTarget, long messageId, boolean isMms);
void onGroupMemberAvatarClicked(@NonNull RecipientId recipientId, @NonNull GroupId groupId);
void onMessageWithErrorClicked(@NonNull MessageRecord messageRecord);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ protected void onDraw(@NonNull Canvas canvas) {
target.getDrawingRect(drawingRect);
activityContentView.offsetDescendantRectToMyCoords(target, drawingRect);

drawingRect.bottom = Math.min(drawingRect.bottom, getBottom() - getPaddingBottom());
drawingRect.top += targetParentTranslationY;
drawingRect.bottom += targetParentTranslationY;

Expand All @@ -88,6 +87,7 @@ protected void onDraw(@NonNull Canvas canvas) {

target.draw(maskCanvas);

canvas.clipRect(drawingRect.left, Math.max(drawingRect.top, getTop() + getPaddingTop()), drawingRect.right, Math.min(drawingRect.bottom, getBottom() - getPaddingBottom()));
canvas.drawBitmap(mask, 0, drawingRect.top, maskPaint);

mask.recycle();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.profiles.GroupShareProfileView;
import org.thoughtcrime.securesms.providers.BlobProvider;
import org.thoughtcrime.securesms.reactions.ReactionsBottomSheetDialogFragment;
import org.thoughtcrime.securesms.reactions.any.ReactWithAnyEmojiBottomSheetDialogFragment;
import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.Recipient;
Expand Down Expand Up @@ -281,7 +282,8 @@ public class ConversationActivity extends PassphraseRequiredActivity
AttachmentKeyboard.Callback,
ConversationReactionOverlay.OnReactionSelectedListener,
ReactWithAnyEmojiBottomSheetDialogFragment.Callback,
SafetyNumberChangeDialog.Callback
SafetyNumberChangeDialog.Callback,
ReactionsBottomSheetDialogFragment.Callback
{

private static final int SHORTCUT_ICON_SIZE = Build.VERSION.SDK_INT >= 26 ? ViewUtil.dpToPx(72) : ViewUtil.dpToPx(48 + 16 * 2);
Expand Down Expand Up @@ -2763,6 +2765,11 @@ private void silentlySetComposeText(String text) {
typingTextWatcher.setEnabled(true);
}

@Override
public void onReactionsDialogDismissed() {
reactionOverlay.hideMask();
}

// Listeners

private class QuickCameraToggleListener implements OnClickListener {
Expand Down Expand Up @@ -2950,6 +2957,11 @@ public void onMessageWithErrorClicked(@NonNull MessageRecord messageRecord) {
}
}

@Override
public void handleReactionDetails(@NonNull View maskTarget) {
reactionOverlay.showMask(maskTarget, titleView.getMeasuredHeight(), panelParent.getMeasuredHeight());
}

@Override
public void onCursorChanged() {
if (!reactionOverlay.isShowing()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1014,6 +1014,7 @@ void handleReaction(@NonNull View maskTarget,
void onCursorChanged();
void onListVerticalTranslationChanged(float translationY);
void onMessageWithErrorClicked(@NonNull MessageRecord messageRecord);
void handleReactionDetails(@NonNull View maskTarget);
}

private class ConversationScrollListener extends OnScrollListener {
Expand Down Expand Up @@ -1264,9 +1265,10 @@ public void onInviteSharedContactClicked(@NonNull List<Recipient> choices) {
}

@Override
public void onReactionClicked(long messageId, boolean isMms) {
public void onReactionClicked(@NonNull View reactionTarget, long messageId, boolean isMms) {
if (getContext() == null) return;

listener.handleReactionDetails(reactionTarget);
ReactionsBottomSheetDialogFragment.create(messageId, isMms).show(requireFragmentManager(), null);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -970,7 +970,7 @@ private void setReactionsWithWidth(@NonNull MessageRecord current, int width) {
reactionsView.setOnClickListener(v -> {
if (eventListener == null) return;

eventListener.onReactionClicked(current.getId(), current.isMms());
eventListener.onReactionClicked(this, current.getId(), current.isMms());
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
import android.app.Activity;
import android.content.Context;
import android.graphics.PointF;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.AttributeSet;
import android.view.HapticFeedbackConstants;
Expand All @@ -19,7 +21,6 @@
import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.appcompat.widget.Toolbar;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.ConstraintSet;
Expand All @@ -32,10 +33,11 @@
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;
import org.thoughtcrime.securesms.database.model.ReactionRecord;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.ThemeUtil;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
Expand Down Expand Up @@ -91,6 +93,7 @@ public final class ConversationReactionOverlay extends RelativeLayout {
private OnHideListener onHideListener;

private AnimatorSet revealAnimatorSet = new AnimatorSet();
private AnimatorSet revealMaskAnimatorSet = new AnimatorSet();
private AnimatorSet hideAnimatorSet = new AnimatorSet();
private AnimatorSet hideAllButMaskAnimatorSet = new AnimatorSet();
private AnimatorSet hideMaskAnimatorSet = new AnimatorSet();
Expand Down Expand Up @@ -185,16 +188,31 @@ public void show(@NonNull Activity activity, @NonNull View maskTarget, @NonNull
maskView.setTarget(maskTarget);

hideAnimatorSet.end();
toolbar.setVisibility(VISIBLE);
setVisibility(View.VISIBLE);
revealAnimatorSet.start();

if (Build.VERSION.SDK_INT >= 21) {
this.activity = activity;
originalStatusBarColor = activity.getWindow().getStatusBarColor();
activity.getWindow().setStatusBarColor(ContextCompat.getColor(activity, R.color.action_mode_status_bar));
activity.getWindow().setStatusBarColor(ThemeUtil.getThemedColor(getContext(), R.attr.reactions_overlay_toolbar_background_color));

if (!ThemeUtil.isDarkTheme(getContext()) && Build.VERSION.SDK_INT >= 23) {
activity.getWindow().getDecorView().setSystemUiVisibility(activity.getWindow().getDecorView().getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
}
}

public void showMask(@NonNull View 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);
Expand All @@ -220,6 +238,11 @@ private void hideInternal(@NonNull AnimatorSet hideAnimatorSet, @Nullable OnHide

if (Build.VERSION.SDK_INT >= 21 && activity != null) {
activity.getWindow().setStatusBarColor(originalStatusBarColor);

if (!ThemeUtil.isDarkTheme(getContext()) && Build.VERSION.SDK_INT >= 23) {
activity.getWindow().getDecorView().setSystemUiVisibility(activity.getWindow().getDecorView().getSystemUiVisibility() & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}

activity = null;
}

Expand Down Expand Up @@ -358,7 +381,7 @@ private void setupSelectedEmoji() {
view.setTranslationY(0);

boolean isAtCustomIndex = i == customEmojiIndex;
boolean isNotAtCustomIndexAndOldEmojiMatches = !isAtCustomIndex && ReactionEmoji.values()[i].emoji.equals(oldEmoji);
boolean isNotAtCustomIndexAndOldEmojiMatches = !isAtCustomIndex && oldEmoji != null && ReactionEmoji.values()[i].emoji.equals(EmojiUtil.getCanonicalRepresentation(oldEmoji));
boolean isAtCustomIndexAndOldEmojiExists = isAtCustomIndex && oldEmoji != null;

if (!foundSelected &&
Expand All @@ -379,13 +402,13 @@ private void setupSelectedEmoji() {
view.setImageEmoji(oldEmoji);
view.setTag(oldEmoji);
} else {
view.setImageEmoji(ReactionEmoji.values()[i].emoji);
view.setImageEmoji(SignalStore.emojiValues().getPreferredVariation(ReactionEmoji.values()[i].emoji));
}
} else if (isAtCustomIndex) {
view.setImageDrawable(AppCompatResources.getDrawable(getContext(), R.drawable.ic_any_emoji_32));
view.setImageDrawable(ThemeUtil.getThemedDrawable(getContext(), R.attr.reactions_overlay_custom_emoji_icon));
view.setTag(null);
} else {
view.setImageEmoji(ReactionEmoji.values()[i].emoji);
view.setImageEmoji(SignalStore.emojiValues().getPreferredVariation(ReactionEmoji.values()[i].emoji));
}
}
}
Expand Down Expand Up @@ -447,7 +470,7 @@ private void handleUpEvent() {
if (selected == customEmojiIndex) {
onReactionSelectedListener.onCustomReactionSelected(messageRecord, emojiViews[selected].getTag() != null);
} else {
onReactionSelectedListener.onReactionSelected(messageRecord, ReactionEmoji.values()[selected].emoji);
onReactionSelectedListener.onReactionSelected(messageRecord, SignalStore.emojiValues().getPreferredVariation(ReactionEmoji.values()[selected].emoji));
}
} else {
hide();
Expand Down Expand Up @@ -534,6 +557,9 @@ private void initAnimators() {
revealAnimatorSet.setInterpolator(INTERPOLATOR);
revealAnimatorSet.playTogether(reveals);

revealMaskAnimatorSet.setInterpolator(INTERPOLATOR);
revealMaskAnimatorSet.playTogether(overlayRevealAnim);

List<Animator> hides = Stream.of(emojiViews)
.mapIndexed((idx, v) -> {
Animator anim = AnimatorInflaterCompat.loadAnimator(getContext(), R.animator.reactions_scrubber_hide);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.thoughtcrime.securesms.keyvalue;

import androidx.annotation.NonNull;

import org.thoughtcrime.securesms.components.emoji.EmojiUtil;

public class EmojiValues extends SignalStoreValues {

private static final String PREFIX = "emojiPref__";

EmojiValues(@NonNull KeyValueStore store) {
super(store);
}

@Override
void onFirstEverAppLaunch() {

}

public void setPreferredVariation(@NonNull String emoji) {
String canonical = EmojiUtil.getCanonicalRepresentation(emoji);

if (canonical.equals(emoji)) {
getStore().beginWrite().remove(PREFIX + canonical).apply();
} else {
putString(PREFIX + canonical, emoji);
}
}

public @NonNull String getPreferredVariation(@NonNull String emoji) {
String canonical = EmojiUtil.getCanonicalRepresentation(emoji);

return getString(PREFIX + canonical, emoji);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public final class SignalStore {
private final TooltipValues tooltipValues;
private final MiscellaneousValues misc;
private final InternalValues internalValues;
private final EmojiValues emojiValues;

private SignalStore() {
this.store = ApplicationDependencies.getKeyValueStore();
Expand All @@ -36,6 +37,7 @@ private SignalStore() {
this.tooltipValues = new TooltipValues(store);
this.misc = new MiscellaneousValues(store);
this.internalValues = new InternalValues(store);
this.emojiValues = new EmojiValues(store);
}

public static void onFirstEverAppLaunch() {
Expand Down Expand Up @@ -86,6 +88,10 @@ public static void onFirstEverAppLaunch() {
return INSTANCE.internalValues;
}

public static @NonNull EmojiValues emojiValues() {
return INSTANCE.emojiValues;
}

public static @NonNull GroupsV2AuthorizationSignalStoreCache groupsV2AuthorizationCache() {
return new GroupsV2AuthorizationSignalStoreCache(getStore());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,25 @@

import androidx.annotation.NonNull;

import java.util.List;

final class EmojiCount {
private final String baseEmoji;
private final String displayEmoji;
private final int count;

EmojiCount(@NonNull String baseEmoji, @NonNull String emoji, int count) {
static EmojiCount all(@NonNull List<ReactionDetails> reactions) {
return new EmojiCount("", "", reactions);
}

private final String baseEmoji;
private final String displayEmoji;
private final List<ReactionDetails> reactions;

EmojiCount(@NonNull String baseEmoji,
@NonNull String emoji,
@NonNull List<ReactionDetails> reactions)
{
this.baseEmoji = baseEmoji;
this.displayEmoji = emoji;
this.count = count;
this.reactions = reactions;
}

public @NonNull String getBaseEmoji() {
Expand All @@ -22,6 +32,10 @@ final class EmojiCount {
}

public int getCount() {
return count;
return reactions.size();
}

public @NonNull List<ReactionDetails> getReactions() {
return reactions;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.thoughtcrime.securesms.reactions;

import androidx.annotation.NonNull;

import org.thoughtcrime.securesms.recipients.Recipient;

class ReactionDetails {
private final Recipient sender;
private final String baseEmoji;
private final String displayEmoji;
private final long timestamp;

ReactionDetails(@NonNull Recipient sender, @NonNull String baseEmoji, @NonNull String displayEmoji, long timestamp) {
this.sender = sender;
this.baseEmoji = baseEmoji;
this.displayEmoji = displayEmoji;
this.timestamp = timestamp;
}

public @NonNull Recipient getSender() {
return sender;
}

public @NonNull String getBaseEmoji() {
return baseEmoji;
}

public @NonNull String getDisplayEmoji() {
return displayEmoji;
}

public long getTimestamp() {
return timestamp;
}
}

0 comments on commit 9d3764c

Please sign in to comment.