Update context menu with tweaks from design.
This commit is contained in:
parent
91c7e0a0ee
commit
45a91e0896
15 changed files with 191 additions and 98 deletions
|
@ -4,6 +4,7 @@ import android.content.Context
|
|||
import android.os.Build
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.PopupWindow
|
||||
|
@ -30,6 +31,7 @@ class ConversationContextMenu(private val anchor: View, items: List<ActionItem>)
|
|||
|
||||
init {
|
||||
setBackgroundDrawable(ContextCompat.getDrawable(context, R.drawable.signal_context_menu_background))
|
||||
animationStyle = R.style.ConversationContextMenuAnimation
|
||||
|
||||
isFocusable = false
|
||||
isOutsideTouchable = true
|
||||
|
@ -38,6 +40,10 @@ class ConversationContextMenu(private val anchor: View, items: List<ActionItem>)
|
|||
elevation = 20f
|
||||
}
|
||||
|
||||
setTouchInterceptor { _, event ->
|
||||
event.action == MotionEvent.ACTION_OUTSIDE
|
||||
}
|
||||
|
||||
contextMenuList.setItems(items)
|
||||
|
||||
contentView.measure(
|
||||
|
|
|
@ -1466,6 +1466,7 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
|
|||
itemView.getX(),
|
||||
itemView.getY() + list.getTranslationY(),
|
||||
bodyBubble.getX(),
|
||||
bodyBubble.getY(),
|
||||
bodyBubble.getWidth(),
|
||||
audioUri,
|
||||
messageRecord.isOutgoing());
|
||||
|
|
|
@ -363,13 +363,17 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
|
|||
|
||||
@Override
|
||||
public boolean dispatchTouchEvent(MotionEvent ev) {
|
||||
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
getHandler().postDelayed(shrinkBubble, SHRINK_BUBBLE_DELAY_MILLIS);
|
||||
} else {
|
||||
getHandler().removeCallbacks(shrinkBubble);
|
||||
bodyBubble.animate()
|
||||
.scaleX(1.0f)
|
||||
.scaleY(1.0f);
|
||||
switch (ev.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
getHandler().postDelayed(shrinkBubble, SHRINK_BUBBLE_DELAY_MILLIS);
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
getHandler().removeCallbacks(shrinkBubble);
|
||||
bodyBubble.animate()
|
||||
.scaleX(1.0f)
|
||||
.scaleY(1.0f);
|
||||
break;
|
||||
}
|
||||
|
||||
return super.dispatchTouchEvent(ev);
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package org.thoughtcrime.securesms.conversation
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Path
|
||||
import android.view.View
|
||||
import androidx.core.graphics.applyCanvas
|
||||
|
@ -51,7 +50,8 @@ object ConversationItemSelection {
|
|||
|
||||
val path = Path()
|
||||
|
||||
val yTranslation = -conversationItem.y
|
||||
val xTranslation = -conversationItem.x - conversationItem.bodyBubble.x
|
||||
val yTranslation = -conversationItem.y - conversationItem.bodyBubble.y
|
||||
|
||||
val mp4Projection = conversationItem.getGiphyMp4PlayableProjection(list)
|
||||
var scaledVideoBitmap = videoBitmap
|
||||
|
@ -63,29 +63,30 @@ object ConversationItemSelection {
|
|||
true
|
||||
)
|
||||
|
||||
mp4Projection.translateX(xTranslation)
|
||||
mp4Projection.translateY(yTranslation)
|
||||
mp4Projection.applyToPath(path)
|
||||
}
|
||||
|
||||
projections.use {
|
||||
it.forEach { p ->
|
||||
p.translateX(xTranslation)
|
||||
p.translateY(yTranslation)
|
||||
p.applyToPath(path)
|
||||
}
|
||||
}
|
||||
|
||||
val distanceToBubbleBottom = conversationItem.bodyBubble.height + conversationItem.bodyBubble.y.toInt()
|
||||
return createBitmap(conversationItem.width, distanceToBubbleBottom).applyCanvas {
|
||||
return createBitmap(conversationItem.bodyBubble.width, conversationItem.bodyBubble.height).applyCanvas {
|
||||
if (drawConversationItem) {
|
||||
draw(conversationItem)
|
||||
conversationItem.bodyBubble.draw(this)
|
||||
}
|
||||
|
||||
withClip(path) {
|
||||
withTranslation(y = yTranslation) {
|
||||
withTranslation(x = xTranslation, y = yTranslation) {
|
||||
list.draw(this)
|
||||
|
||||
if (scaledVideoBitmap != null) {
|
||||
drawBitmap(scaledVideoBitmap, mp4Projection.x, mp4Projection.y - yTranslation, null)
|
||||
drawBitmap(scaledVideoBitmap, mp4Projection.x - xTranslation, mp4Projection.y - yTranslation, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -96,11 +97,4 @@ object ConversationItemSelection {
|
|||
conversationItem.bodyBubble.scaleY = originalScale
|
||||
}
|
||||
}
|
||||
|
||||
private fun Canvas.draw(conversationItem: ConversationItem) {
|
||||
val bodyBubble = conversationItem.bodyBubble
|
||||
withTranslation(bodyBubble.x, bodyBubble.y) {
|
||||
bodyBubble.draw(this@draw)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,8 +53,7 @@ import kotlin.Unit;
|
|||
|
||||
public final class ConversationReactionOverlay extends RelativeLayout {
|
||||
|
||||
private static final Interpolator INTERPOLATOR = new DecelerateInterpolator();
|
||||
private static final long TRANSITION_Y_DURATION = 150;
|
||||
private static final Interpolator INTERPOLATOR = new DecelerateInterpolator();
|
||||
|
||||
private final Rect emojiViewGlobalRect = new Rect();
|
||||
private final Rect emojiStripViewBounds = new Rect();
|
||||
|
@ -89,10 +88,7 @@ public final class ConversationReactionOverlay extends RelativeLayout {
|
|||
private ConversationContextMenu contextMenu;
|
||||
|
||||
private float touchDownDeadZoneSize;
|
||||
private float distanceFromTouchDownPointToTopOfScrubberDeadZone;
|
||||
private float distanceFromTouchDownPointToBottomOfScrubberDeadZone;
|
||||
private int scrubberDistanceFromTouchDown;
|
||||
private int scrubberHeight;
|
||||
private int scrubberWidth;
|
||||
private int selectedVerticalTranslation;
|
||||
private int scrubberHorizontalMargin;
|
||||
|
@ -137,15 +133,12 @@ public final class ConversationReactionOverlay extends RelativeLayout {
|
|||
|
||||
customEmojiIndex = emojiViews.length - 1;
|
||||
|
||||
distanceFromTouchDownPointToTopOfScrubberDeadZone = getResources().getDimensionPixelSize(R.dimen.conversation_reaction_scrub_deadzone_distance_from_touch_top);
|
||||
distanceFromTouchDownPointToBottomOfScrubberDeadZone = getResources().getDimensionPixelSize(R.dimen.conversation_reaction_scrub_deadzone_distance_from_touch_bottom);
|
||||
|
||||
touchDownDeadZoneSize = getResources().getDimensionPixelSize(R.dimen.conversation_reaction_touch_deadzone_size);
|
||||
scrubberDistanceFromTouchDown = getResources().getDimensionPixelOffset(R.dimen.conversation_reaction_scrubber_distance);
|
||||
scrubberHeight = getResources().getDimensionPixelOffset(R.dimen.conversation_reaction_scrubber_height);
|
||||
scrubberWidth = getResources().getDimensionPixelOffset(R.dimen.reaction_scrubber_width);
|
||||
selectedVerticalTranslation = getResources().getDimensionPixelOffset(R.dimen.conversation_reaction_scrub_vertical_translation);
|
||||
scrubberHorizontalMargin = getResources().getDimensionPixelOffset(R.dimen.conversation_reaction_scrub_horizontal_margin);
|
||||
touchDownDeadZoneSize = getResources().getDimensionPixelSize(R.dimen.conversation_reaction_touch_deadzone_size);
|
||||
scrubberWidth = getResources().getDimensionPixelOffset(R.dimen.reaction_scrubber_width);
|
||||
selectedVerticalTranslation = getResources().getDimensionPixelOffset(R.dimen.conversation_reaction_scrub_vertical_translation);
|
||||
scrubberHorizontalMargin = getResources().getDimensionPixelOffset(R.dimen.conversation_reaction_scrub_horizontal_margin);
|
||||
|
||||
animationEmojiStartDelayFactor = getResources().getInteger(R.integer.reaction_scrubber_emoji_reveal_duration_start_delay_factor);
|
||||
|
||||
|
@ -180,7 +173,7 @@ public final class ConversationReactionOverlay extends RelativeLayout {
|
|||
}
|
||||
|
||||
ViewGroup.LayoutParams layoutParams = inputShade.getLayoutParams();
|
||||
layoutParams.height = activity.findViewById(R.id.bottom_panel).getHeight();
|
||||
layoutParams.height = getInputPanelHeight(activity);
|
||||
inputShade.setLayoutParams(layoutParams);
|
||||
|
||||
toolbarShade.setVisibility(VISIBLE);
|
||||
|
@ -191,12 +184,8 @@ public final class ConversationReactionOverlay extends RelativeLayout {
|
|||
conversationItem.setLayoutParams(new LayoutParams(conversationItemSnapshot.getWidth(), conversationItemSnapshot.getHeight()));
|
||||
conversationItem.setBackground(new BitmapDrawable(getResources(), conversationItemSnapshot));
|
||||
|
||||
float initialX = selectedConversationModel.getBitmapX();
|
||||
boolean isMessageOnLeft = selectedConversationModel.isOutgoing() ^ ViewUtil.isLtr(this);
|
||||
|
||||
conversationItem.setX(initialX);
|
||||
conversationItem.setY(selectedConversationModel.getBitmapY() - statusBarHeight);
|
||||
|
||||
conversationItem.setScaleX(ConversationItem.LONG_PRESS_SCALE_FACTOR);
|
||||
conversationItem.setScaleY(ConversationItem.LONG_PRESS_SCALE_FACTOR);
|
||||
|
||||
|
@ -208,58 +197,70 @@ public final class ConversationReactionOverlay extends RelativeLayout {
|
|||
});
|
||||
}
|
||||
|
||||
private int getInputPanelHeight(@NonNull Activity activity) {
|
||||
View bottomPanel = activity.findViewById(R.id.bottom_panel);
|
||||
View emojiDrawer = activity.findViewById(R.id.emoji_drawer);
|
||||
|
||||
return bottomPanel.getHeight() + (emojiDrawer != null && emojiDrawer.getVisibility() == VISIBLE ? emojiDrawer.getHeight() : 0);
|
||||
}
|
||||
|
||||
private void showAfterLayout(@NonNull Activity activity,
|
||||
@NonNull ConversationMessage conversationMessage,
|
||||
@NonNull PointF lastSeenDownPoint,
|
||||
boolean isMessageOnLeft) {
|
||||
contextMenu = new ConversationContextMenu(dropdownAnchor, getMenuActionItems(conversationMessage));
|
||||
|
||||
conversationItem.setX(selectedConversationModel.getBubbleX());
|
||||
conversationItem.setY(selectedConversationModel.getItemY() + selectedConversationModel.getBubbleY() - statusBarHeight);
|
||||
|
||||
Bitmap conversationItemSnapshot = selectedConversationModel.getBitmap();
|
||||
boolean isWideLayout = contextMenu.getMaxWidth() + scrubberWidth < getWidth();
|
||||
|
||||
int bubbleWidth = selectedConversationModel.getBubbleWidth();
|
||||
|
||||
float endX = selectedConversationModel.getBitmapX();
|
||||
float endX = selectedConversationModel.getBubbleX();
|
||||
float endY = conversationItem.getY();
|
||||
float endApparentTop = endY;
|
||||
float endScale = 1f;
|
||||
|
||||
float menuPadding = DimensionUnit.DP.toPixels(12f);
|
||||
int reactionBarHeight = backgroundView.getHeight();
|
||||
float menuPadding = DimensionUnit.DP.toPixels(12f);
|
||||
float reactionBarTopPadding = DimensionUnit.DP.toPixels(32f);
|
||||
int reactionBarHeight = backgroundView.getHeight();
|
||||
|
||||
float reactionBarBackgroundY;
|
||||
|
||||
if (isWideLayout) {
|
||||
boolean everythingFitsVertically = scrubberHeight + conversationItemSnapshot.getHeight() < getHeight();
|
||||
boolean everythingFitsVertically = reactionBarHeight + menuPadding + reactionBarTopPadding + conversationItemSnapshot.getHeight() < getHeight();
|
||||
if (everythingFitsVertically) {
|
||||
boolean reactionBarFitsAboveItem = conversationItem.getY() > scrubberHeight;
|
||||
boolean reactionBarFitsAboveItem = conversationItem.getY() > reactionBarHeight + menuPadding + reactionBarTopPadding;
|
||||
|
||||
if (reactionBarFitsAboveItem) {
|
||||
reactionBarBackgroundY = conversationItem.getY() - menuPadding - reactionBarHeight;
|
||||
} else {
|
||||
endY = reactionBarHeight + menuPadding;
|
||||
reactionBarBackgroundY = 0f;
|
||||
endY = reactionBarHeight + menuPadding + reactionBarTopPadding;
|
||||
reactionBarBackgroundY = reactionBarTopPadding;
|
||||
}
|
||||
} else {
|
||||
float spaceAvailableForItem = getHeight() - reactionBarHeight - menuPadding;
|
||||
float spaceAvailableForItem = getHeight() - reactionBarHeight - menuPadding * 2;
|
||||
|
||||
endScale = spaceAvailableForItem / conversationItem.getHeight();
|
||||
endY = reactionBarHeight + menuPadding - Util.halfOffsetFromScale(conversationItemSnapshot.getHeight(), endScale);
|
||||
reactionBarBackgroundY = 0f;
|
||||
endX += Util.halfOffsetFromScale(conversationItemSnapshot.getWidth(), endScale) * (isMessageOnLeft ? -1 : 1);
|
||||
endY = reactionBarHeight + menuPadding + reactionBarTopPadding - Util.halfOffsetFromScale(conversationItemSnapshot.getHeight(), endScale);
|
||||
reactionBarBackgroundY = reactionBarTopPadding;
|
||||
}
|
||||
} else {
|
||||
boolean everythingFitsVertically = contextMenu.getMaxHeight() + conversationItemSnapshot.getHeight() + menuPadding + reactionBarHeight < getHeight();
|
||||
boolean everythingFitsVertically = contextMenu.getMaxHeight() + conversationItemSnapshot.getHeight() + menuPadding + reactionBarHeight + reactionBarTopPadding < getHeight();
|
||||
|
||||
if (everythingFitsVertically) {
|
||||
float itemBottom = selectedConversationModel.getBitmapY() + conversationItemSnapshot.getHeight();
|
||||
boolean menuFitsBelowItem = itemBottom + menuPadding + contextMenu.getMaxHeight() <= getHeight();
|
||||
float bubbleBottom = selectedConversationModel.getItemY() + selectedConversationModel.getBubbleY() + conversationItemSnapshot.getHeight();
|
||||
boolean menuFitsBelowItem = bubbleBottom + menuPadding + contextMenu.getMaxHeight() <= getHeight() + statusBarHeight;
|
||||
|
||||
if (menuFitsBelowItem) {
|
||||
reactionBarBackgroundY = conversationItem.getY() - menuPadding - reactionBarHeight;
|
||||
|
||||
if (reactionBarBackgroundY < 0) {
|
||||
endY = backgroundView.getHeight();
|
||||
reactionBarBackgroundY = 0f;
|
||||
if (reactionBarBackgroundY < reactionBarTopPadding) {
|
||||
endY = backgroundView.getHeight() + menuPadding;
|
||||
reactionBarBackgroundY = reactionBarTopPadding;
|
||||
}
|
||||
} else {
|
||||
endY = getHeight() - contextMenu.getMaxHeight() - menuPadding - conversationItemSnapshot.getHeight();
|
||||
|
@ -267,30 +268,44 @@ public final class ConversationReactionOverlay extends RelativeLayout {
|
|||
}
|
||||
|
||||
endApparentTop = endY;
|
||||
} else if (reactionBarHeight + contextMenu.getMaxHeight() + menuPadding < getHeight()) {
|
||||
float spaceAvailableForItem = (float) getHeight() - contextMenu.getMaxHeight() - menuPadding - reactionBarHeight;
|
||||
} else if (reactionBarHeight + contextMenu.getMaxHeight() + menuPadding * 2 < getHeight()) {
|
||||
float spaceAvailableForItem = (float) getHeight() - contextMenu.getMaxHeight() - menuPadding * 2 - reactionBarHeight - reactionBarTopPadding;
|
||||
|
||||
endScale = spaceAvailableForItem / conversationItemSnapshot.getHeight();
|
||||
endY = reactionBarHeight - Util.halfOffsetFromScale(conversationItemSnapshot.getHeight(), endScale);
|
||||
reactionBarBackgroundY = 0f;
|
||||
endApparentTop = reactionBarHeight;
|
||||
endX += Util.halfOffsetFromScale(conversationItemSnapshot.getWidth(), endScale) * (isMessageOnLeft ? -1 : 1);
|
||||
endY = reactionBarHeight - Util.halfOffsetFromScale(conversationItemSnapshot.getHeight(), endScale) + menuPadding + reactionBarTopPadding;
|
||||
reactionBarBackgroundY = reactionBarTopPadding;
|
||||
endApparentTop = reactionBarHeight + menuPadding + reactionBarTopPadding;
|
||||
} else {
|
||||
contextMenu.setHeight(contextMenu.getMaxHeight() / 2);
|
||||
|
||||
int menuHeight = contextMenu.getHeight();
|
||||
boolean fitsVertically = menuHeight + conversationItem.getHeight() + menuPadding + reactionBarHeight < getHeight();
|
||||
boolean fitsVertically = menuHeight + conversationItem.getHeight() + menuPadding * 2 + reactionBarHeight + reactionBarTopPadding < getHeight();
|
||||
|
||||
if (fitsVertically) {
|
||||
endY = getHeight() - menuHeight - menuPadding - conversationItemSnapshot.getHeight();
|
||||
reactionBarBackgroundY = endY - reactionBarHeight;
|
||||
float bubbleBottom = selectedConversationModel.getItemY() + selectedConversationModel.getBubbleY() + conversationItemSnapshot.getHeight();
|
||||
boolean menuFitsBelowItem = bubbleBottom + menuPadding + menuHeight <= getHeight() + statusBarHeight;
|
||||
|
||||
if (menuFitsBelowItem) {
|
||||
reactionBarBackgroundY = conversationItem.getY() - menuPadding - reactionBarHeight;
|
||||
|
||||
if (reactionBarBackgroundY < reactionBarTopPadding) {
|
||||
endY = reactionBarTopPadding + reactionBarHeight + menuPadding;
|
||||
reactionBarBackgroundY = reactionBarTopPadding;
|
||||
}
|
||||
} else {
|
||||
endY = getHeight() - menuHeight - menuPadding - conversationItemSnapshot.getHeight();
|
||||
reactionBarBackgroundY = endY - reactionBarHeight - menuPadding;
|
||||
}
|
||||
endApparentTop = endY;
|
||||
} else {
|
||||
float spaceAvailableForItem = (float) getHeight() - menuHeight - menuPadding - reactionBarHeight;
|
||||
float spaceAvailableForItem = (float) getHeight() - menuHeight - menuPadding * 2 - reactionBarHeight - reactionBarTopPadding;
|
||||
|
||||
endScale = spaceAvailableForItem / conversationItemSnapshot.getHeight();
|
||||
endY = reactionBarHeight - Util.halfOffsetFromScale(conversationItemSnapshot.getHeight(), endScale);
|
||||
reactionBarBackgroundY = 0f;
|
||||
endApparentTop = reactionBarHeight;
|
||||
endX += Util.halfOffsetFromScale(conversationItemSnapshot.getWidth(), endScale) * (isMessageOnLeft ? -1 : 1);
|
||||
endY = reactionBarHeight - Util.halfOffsetFromScale(conversationItemSnapshot.getHeight(), endScale) + menuPadding + reactionBarTopPadding;
|
||||
reactionBarBackgroundY = reactionBarTopPadding;
|
||||
endApparentTop = reactionBarHeight + menuPadding + reactionBarTopPadding;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -331,21 +346,21 @@ public final class ConversationReactionOverlay extends RelativeLayout {
|
|||
float offsetX = isMessageOnLeft ? scrubberRight + menuPadding : scrubberX - contextMenu.getMaxWidth() - menuPadding;
|
||||
contextMenu.show((int) offsetX, (int) Math.min(backgroundView.getY(), getHeight() - contextMenu.getMaxHeight()));
|
||||
} else {
|
||||
float contentX = selectedConversationModel.getContentX();
|
||||
float offsetX = isMessageOnLeft ? contentX : - contextMenu.getMaxWidth() + contentX + bubbleWidth;
|
||||
float contentX = selectedConversationModel.getBubbleX();
|
||||
float offsetX = isMessageOnLeft ? contentX : -contextMenu.getMaxWidth() + contentX + bubbleWidth;
|
||||
|
||||
float menuTop = endApparentTop + (conversationItemSnapshot.getHeight() * endScale);
|
||||
contextMenu.show((int) offsetX, (int) (menuTop + menuPadding));
|
||||
}
|
||||
|
||||
conversationItem.animate()
|
||||
.x(endX)
|
||||
.scaleX(endScale)
|
||||
.scaleY(endScale);
|
||||
int revealDuration = getContext().getResources().getInteger(R.integer.reaction_scrubber_reveal_duration);
|
||||
|
||||
conversationItem.animate()
|
||||
.x(endX)
|
||||
.y(endY)
|
||||
.setDuration(TRANSITION_Y_DURATION);
|
||||
.scaleX(endScale)
|
||||
.scaleY(endScale)
|
||||
.setDuration(revealDuration);
|
||||
}
|
||||
|
||||
@RequiresApi(api = 21)
|
||||
|
@ -375,7 +390,7 @@ public final class ConversationReactionOverlay extends RelativeLayout {
|
|||
private void hideInternal(@Nullable OnHideListener onHideListener) {
|
||||
overlayState = OverlayState.HIDDEN;
|
||||
|
||||
int duration = getContext().getResources().getInteger(R.integer.reaction_scrubber_reveal_duration);
|
||||
int duration = getContext().getResources().getInteger(R.integer.reaction_scrubber_hide_duration);
|
||||
|
||||
List<Animator> hides = new ArrayList<>(hideAnimators);
|
||||
|
||||
|
@ -395,16 +410,16 @@ public final class ConversationReactionOverlay extends RelativeLayout {
|
|||
|
||||
ObjectAnimator itemXAnim = new ObjectAnimator();
|
||||
itemXAnim.setProperty(View.X);
|
||||
itemXAnim.setFloatValues(selectedConversationModel.getBitmapX());
|
||||
itemXAnim.setFloatValues(selectedConversationModel.getBubbleX());
|
||||
itemXAnim.setTarget(conversationItem);
|
||||
itemXAnim.setDuration(duration);
|
||||
hides.add(itemXAnim);
|
||||
|
||||
ObjectAnimator itemYAnim = new ObjectAnimator();
|
||||
itemYAnim.setProperty(View.Y);
|
||||
itemYAnim.setFloatValues(selectedConversationModel.getBitmapY() - statusBarHeight);
|
||||
itemYAnim.setFloatValues(selectedConversationModel.getItemY() + selectedConversationModel.getBubbleY() - statusBarHeight);
|
||||
itemYAnim.setTarget(conversationItem);
|
||||
itemYAnim.setDuration(TRANSITION_Y_DURATION);
|
||||
itemYAnim.setDuration(duration);
|
||||
hides.add(itemYAnim);
|
||||
|
||||
hideAnimatorSet.playTogether(hides);
|
||||
|
@ -720,29 +735,28 @@ public final class ConversationReactionOverlay extends RelativeLayout {
|
|||
|
||||
private void initAnimators() {
|
||||
|
||||
int duration = getContext().getResources().getInteger(R.integer.reaction_scrubber_reveal_duration);
|
||||
int revealDuration = getContext().getResources().getInteger(R.integer.reaction_scrubber_reveal_duration);
|
||||
int revealOffset = getContext().getResources().getInteger(R.integer.reaction_scrubber_reveal_offset);
|
||||
|
||||
List<Animator> reveals = Stream.of(emojiViews)
|
||||
.mapIndexed((idx, v) -> {
|
||||
Animator anim = AnimatorInflaterCompat.loadAnimator(getContext(), R.animator.reactions_scrubber_reveal);
|
||||
anim.setTarget(v);
|
||||
anim.setStartDelay(idx * animationEmojiStartDelayFactor);
|
||||
anim.setStartDelay(revealOffset + idx * animationEmojiStartDelayFactor);
|
||||
return anim;
|
||||
})
|
||||
.toList();
|
||||
|
||||
Animator overlayRevealAnim = AnimatorInflaterCompat.loadAnimator(getContext(), android.R.animator.fade_in);
|
||||
overlayRevealAnim.setDuration(duration);
|
||||
reveals.add(overlayRevealAnim);
|
||||
|
||||
Animator backgroundRevealAnim = AnimatorInflaterCompat.loadAnimator(getContext(), android.R.animator.fade_in);
|
||||
backgroundRevealAnim.setTarget(backgroundView);
|
||||
backgroundRevealAnim.setDuration(duration);
|
||||
backgroundRevealAnim.setDuration(revealDuration);
|
||||
backgroundRevealAnim.setStartDelay(revealOffset);
|
||||
reveals.add(backgroundRevealAnim);
|
||||
|
||||
Animator selectedRevealAnim = AnimatorInflaterCompat.loadAnimator(getContext(), android.R.animator.fade_in);
|
||||
selectedRevealAnim.setTarget(selectedView);
|
||||
selectedRevealAnim.setDuration(duration);
|
||||
backgroundRevealAnim.setDuration(revealDuration);
|
||||
backgroundRevealAnim.setStartDelay(revealOffset);
|
||||
reveals.add(selectedRevealAnim);
|
||||
|
||||
revealAnimatorSet.setInterpolator(INTERPOLATOR);
|
||||
|
@ -757,18 +771,20 @@ public final class ConversationReactionOverlay extends RelativeLayout {
|
|||
})
|
||||
.toList();
|
||||
|
||||
int hideDuration = getContext().getResources().getInteger(R.integer.reaction_scrubber_hide_duration);
|
||||
|
||||
Animator overlayHideAnim = AnimatorInflaterCompat.loadAnimator(getContext(), android.R.animator.fade_out);
|
||||
overlayHideAnim.setDuration(duration);
|
||||
overlayHideAnim.setDuration(hideDuration);
|
||||
hideAnimators.add(overlayHideAnim);
|
||||
|
||||
Animator backgroundHideAnim = AnimatorInflaterCompat.loadAnimator(getContext(), android.R.animator.fade_out);
|
||||
backgroundHideAnim.setTarget(backgroundView);
|
||||
backgroundHideAnim.setDuration(duration);
|
||||
backgroundHideAnim.setDuration(hideDuration);
|
||||
hideAnimators.add(backgroundHideAnim);
|
||||
|
||||
Animator selectedHideAnim = AnimatorInflaterCompat.loadAnimator(getContext(), android.R.animator.fade_out);
|
||||
selectedHideAnim.setTarget(selectedView);
|
||||
selectedHideAnim.setDuration(duration);
|
||||
selectedHideAnim.setDuration(hideDuration);
|
||||
hideAnimators.add(selectedHideAnim);
|
||||
|
||||
hideAnimatorSet = newHideAnimatorSet();
|
||||
|
|
|
@ -9,9 +9,10 @@ import android.net.Uri
|
|||
*/
|
||||
data class SelectedConversationModel(
|
||||
val bitmap: Bitmap,
|
||||
val bitmapX: Float,
|
||||
val bitmapY: Float,
|
||||
val contentX: Float,
|
||||
val itemX: Float,
|
||||
val itemY: Float,
|
||||
val bubbleX: Float,
|
||||
val bubbleY: Float,
|
||||
val bubbleWidth: Int,
|
||||
val audioUri: Uri? = null,
|
||||
val isOutgoing: Boolean,
|
||||
|
|
|
@ -118,13 +118,16 @@ class RecyclerViewColorizer(private val recyclerView: RecyclerView) {
|
|||
colorPaint.xfermode = noLayerXfermode
|
||||
}
|
||||
|
||||
val firstColor: Int
|
||||
val lastColor: Int
|
||||
if (chatColors.isGradient()) {
|
||||
val mask = chatColors.chatBubbleMask as RotatableGradientDrawable
|
||||
mask.setXfermode(colorPaint.xfermode)
|
||||
mask.setBounds(0, 0, parent.width, parent.height)
|
||||
mask.draw(canvas)
|
||||
|
||||
outOfBoundsPaint.color = chatColors.getColors().last()
|
||||
firstColor = chatColors.getColors().first()
|
||||
lastColor = chatColors.getColors().last()
|
||||
} else {
|
||||
colorPaint.color = chatColors.asSingleColor()
|
||||
canvas.drawRect(
|
||||
|
@ -135,9 +138,16 @@ class RecyclerViewColorizer(private val recyclerView: RecyclerView) {
|
|||
colorPaint
|
||||
)
|
||||
|
||||
outOfBoundsPaint.color = chatColors.asSingleColor()
|
||||
firstColor = chatColors.asSingleColor()
|
||||
lastColor = chatColors.asSingleColor()
|
||||
}
|
||||
|
||||
outOfBoundsPaint.color = firstColor
|
||||
canvas.drawRect(
|
||||
0f, -parent.height.toFloat(), parent.width.toFloat(), 0f, outOfBoundsPaint
|
||||
)
|
||||
|
||||
outOfBoundsPaint.color = lastColor
|
||||
canvas.drawRect(
|
||||
0f, parent.height.toFloat(), parent.width.toFloat(), parent.height * 2f, outOfBoundsPaint
|
||||
)
|
||||
|
|
23
app/src/main/res/anim/delay_grow_fade_in.xml
Normal file
23
app/src/main/res/anim/delay_grow_fade_in.xml
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shareInterpolator="false">
|
||||
|
||||
<scale
|
||||
android:interpolator="@android:interpolator/decelerate_quint"
|
||||
android:fromXScale="0.9"
|
||||
android:toXScale="1.0"
|
||||
android:fromYScale="0.9"
|
||||
android:toYScale="1.0"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="0%"
|
||||
android:duration="220"
|
||||
android:startOffset="150" />
|
||||
|
||||
<alpha
|
||||
android:interpolator="@android:interpolator/decelerate_cubic"
|
||||
android:fromAlpha="0.0"
|
||||
android:toAlpha="1.0"
|
||||
android:duration="150"
|
||||
android:startOffset="150" />
|
||||
</set>
|
30
app/src/main/res/anim/shrink_fade_out.xml
Normal file
30
app/src/main/res/anim/shrink_fade_out.xml
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/* //device/apps/common/res/anim/fade_out.xml
|
||||
**
|
||||
** Copyright 2007, The Android Open Source Project
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
** You may obtain a copy of the License at
|
||||
**
|
||||
** http://www.apache.org/licenses/LICENSE-2.0
|
||||
**
|
||||
** Unless required by applicable law or agreed to in writing, software
|
||||
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
** See the License for the specific language governing permissions and
|
||||
** limitations under the License.
|
||||
*/
|
||||
-->
|
||||
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false">
|
||||
<scale android:interpolator="@android:interpolator/decelerate_quint"
|
||||
android:fromXScale="1.0" android:toXScale="0.9"
|
||||
android:fromYScale="1.0" android:toYScale="0.9"
|
||||
android:pivotX="50%" android:pivotY="0%"
|
||||
android:duration="220" />
|
||||
<alpha android:interpolator="@android:interpolator/decelerate_cubic"
|
||||
android:fromAlpha="1.0" android:toAlpha="0.0"
|
||||
android:duration="150" />
|
||||
</set>
|
|
@ -2,6 +2,7 @@
|
|||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<objectAnimator
|
||||
android:duration="@integer/reaction_scrubber_reveal_duration"
|
||||
android:startOffset="@integer/reaction_scrubber_reveal_offset"
|
||||
android:interpolator="@android:anim/linear_interpolator"
|
||||
android:propertyName="translationY"
|
||||
android:valueFrom="@dimen/reaction_scrubber_anim_start_translation_y"
|
||||
|
@ -9,6 +10,7 @@
|
|||
|
||||
<objectAnimator
|
||||
android:duration="@integer/reaction_scrubber_reveal_duration"
|
||||
android:startOffset="@integer/reaction_scrubber_reveal_offset"
|
||||
android:interpolator="@android:anim/linear_interpolator"
|
||||
android:propertyName="alpha"
|
||||
android:valueFrom="0"
|
||||
|
|
|
@ -19,4 +19,9 @@
|
|||
<item name="android:windowEnterAnimation">@anim/fade_in</item>
|
||||
<item name="android:windowExitAnimation">@anim/fade_out</item>
|
||||
</style>
|
||||
|
||||
<style name="ConversationContextMenuAnimation" parent="@android:style/Animation">
|
||||
<item name="android:windowEnterAnimation">@anim/fade_in</item>
|
||||
<item name="android:windowExitAnimation">@anim/shrink_fade_out</item>
|
||||
</style>
|
||||
</resources>
|
|
@ -12,6 +12,7 @@
|
|||
<color name="transparent_black_20">#33000000</color>
|
||||
<color name="transparent_black_25">#40000000</color>
|
||||
<color name="transparent_black_40">#66000000</color>
|
||||
<color name="transparent_black_50">#80000000</color>
|
||||
<color name="transparent_black_60">#99000000</color>
|
||||
<color name="transparent_black_80">#CC000000</color>
|
||||
<color name="transparent_black_90">#e6000000</color>
|
||||
|
|
|
@ -149,9 +149,7 @@
|
|||
<dimen name="contact_selection_item_height">@dimen/selection_item_header_height</dimen>
|
||||
|
||||
<dimen name="conversation_reaction_scrubber_height">136dp</dimen>
|
||||
<dimen name="conversation_reaction_scrubber_distance">25dp</dimen>
|
||||
<dimen name="conversation_reaction_touch_deadzone_size">40dp</dimen>
|
||||
<dimen name="conversation_reaction_scrub_deadzone_distance_from_touch_top">136dp</dimen>
|
||||
<dimen name="conversation_reaction_scrub_deadzone_distance_from_touch_bottom">30dp</dimen>
|
||||
<dimen name="conversation_reaction_scrub_vertical_translation">25dp</dimen>
|
||||
<dimen name="conversation_reaction_scrub_horizontal_margin">16dp</dimen>
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<integer name="reaction_scrubber_reveal_duration">400</integer>
|
||||
<integer name="reaction_scrubber_reveal_duration">200</integer>
|
||||
<integer name="reaction_scrubber_reveal_offset">100</integer>
|
||||
<integer name="reaction_scrubber_hide_duration">150</integer>
|
||||
<integer name="reaction_scrubber_reveal_emoji_duration">380</integer>
|
||||
<integer name="reaction_scrubber_emoji_reveal_duration_start_delay_factor">10</integer>
|
||||
</resources>
|
|
@ -126,8 +126,8 @@
|
|||
|
||||
<color name="reactions_pill_text_color">@color/core_grey_60</color>
|
||||
<color name="reactions_pill_selected_text_color">@color/core_grey_75</color>
|
||||
<color name="reactions_screen_shade_color">@color/transparent_black_40</color>
|
||||
<color name="reactions_status_bar_shade">#999999</color>
|
||||
<color name="reactions_screen_shade_color">@color/transparent_black_50</color>
|
||||
<color name="reactions_status_bar_shade">#808080</color>
|
||||
|
||||
<color name="recipient_contact_button_color">@color/core_grey_02</color>
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue