diff options
author | Treehugger Robot <treehugger-gerrit@google.com> | 2017-12-18 23:22:56 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2017-12-18 23:22:56 +0000 |
commit | 4c82c25182adb9892e8cb770bfc6003b59c53cc9 (patch) | |
tree | a2fc282d10c9f5f7e562ed22e1d872d6baedca02 /java/com/android/newbubble/NewBubble.java | |
parent | 01de7aaefd290672eeb1ff0c6601466590cd9563 (diff) | |
parent | d3d268c5c38f12e79d3da0161bb19da4de232f3f (diff) |
Merge changes I05dc2ddd,Ic6bfd4ab,I58557f77
* changes:
Added tests for latin smart dial map.
Upgrade target SDK version to 27.
Bubble v2 animation change.
Diffstat (limited to 'java/com/android/newbubble/NewBubble.java')
-rw-r--r-- | java/com/android/newbubble/NewBubble.java | 259 |
1 files changed, 146 insertions, 113 deletions
diff --git a/java/com/android/newbubble/NewBubble.java b/java/com/android/newbubble/NewBubble.java index 34a9585c1..d13952ba5 100644 --- a/java/com/android/newbubble/NewBubble.java +++ b/java/com/android/newbubble/NewBubble.java @@ -25,8 +25,8 @@ import android.annotation.SuppressLint; import android.app.PendingIntent.CanceledException; import android.content.Context; import android.content.Intent; -import android.graphics.Path; import android.graphics.PixelFormat; +import android.graphics.Rect; import android.graphics.drawable.Animatable; import android.graphics.drawable.Drawable; import android.net.Uri; @@ -38,7 +38,6 @@ import android.support.annotation.Nullable; import android.support.annotation.VisibleForTesting; import android.support.v4.graphics.ColorUtils; import android.support.v4.os.BuildCompat; -import android.support.v4.view.animation.FastOutLinearInInterpolator; import android.support.v4.view.animation.LinearOutSlowInInterpolator; import android.transition.TransitionManager; import android.transition.TransitionValues; @@ -55,6 +54,7 @@ import android.view.WindowManager; import android.view.WindowManager.LayoutParams; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; +import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.AnticipateInterpolator; import android.view.animation.OvershootInterpolator; import android.widget.ImageView; @@ -87,8 +87,13 @@ public class NewBubble { // This ensures the new window has had time to draw first. private static final int WINDOW_REDRAW_DELAY_MILLIS = 50; + private static final int EXPAND_AND_COLLAPSE_ANIMATION_DURATION = 200; + private static Boolean canShowBubblesForTesting = null; + private final AccelerateDecelerateInterpolator accelerateDecelerateInterpolator = + new AccelerateDecelerateInterpolator(); + private final Context context; private final WindowManager windowManager; @@ -233,7 +238,12 @@ public class NewBubble { } setPrimaryButtonAccessibilityAction( context.getString(R.string.a11y_bubble_primary_button_collapse_action)); + viewHolder.setDrawerVisibility(View.INVISIBLE); + viewHolder.getArrow().setVisibility(View.INVISIBLE); + // No click during animation to avoid jank. + viewHolder.setPrimaryButtonClickable(false); + View expandedView = viewHolder.getExpandedView(); expandedView .getViewTreeObserver() @@ -247,47 +257,46 @@ public class NewBubble { savedYPosition = windowParams.y; } - // Calculate the move-to-middle distance + // Animation 1: animate x-move and y-move (if needed) together int deltaX = (int) viewHolder.getRoot().findViewById(R.id.bubble_primary_container).getX(); - float k = (float) moveUpDistance / deltaX; + float k = -(float) moveUpDistance / deltaX; if (isDrawingFromRight()) { deltaX = -deltaX; } - - // Do X-move and Y-move together - - final int startX = windowParams.x - deltaX; - final int startY = windowParams.y; - ValueAnimator animator = ValueAnimator.ofFloat(startX, windowParams.x); - animator.setInterpolator(new LinearOutSlowInInterpolator()); - animator.addUpdateListener( - (valueAnimator) -> { - // Update windowParams and the root layout. - // We can't do ViewPropertyAnimation since it clips children. - float newX = (float) valueAnimator.getAnimatedValue(); - if (moveUpDistance != 0) { - windowParams.y = startY - (int) (Math.abs(newX - (float) startX) * k); - } - windowParams.x = (int) newX; - windowManager.updateViewLayout(viewHolder.getRoot(), windowParams); - }); - animator.addListener( + ValueAnimator xValueAnimator = + createBubbleMoveAnimator( + windowParams.x - deltaX, windowParams.x, windowParams.y, k); + + // Show expanded view + expandedView.setVisibility(View.VISIBLE); + + // Animator 2: reveal expanded view from top left or top right + View expandedMenu = viewHolder.getRoot().findViewById(R.id.bubble_expanded_menu); + ValueAnimator revealAnim = + createOpenCloseOutlineProvider(expandedMenu) + .createRevealAnimator(expandedMenu, false); + revealAnim.setInterpolator(accelerateDecelerateInterpolator); + + // Animator 3: expanded view fade in + Animator fadeIn = ObjectAnimator.ofFloat(expandedView, "alpha", 0, 1); + fadeIn.setInterpolator(accelerateDecelerateInterpolator); + + // Play all animation together + AnimatorSet expandAnimatorSet = new AnimatorSet(); + expandAnimatorSet.playTogether(revealAnim, fadeIn, xValueAnimator); + expandAnimatorSet.setDuration(EXPAND_AND_COLLAPSE_ANIMATION_DURATION); + expandAnimatorSet.addListener( new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - // Show expanded view - expandedView.setVisibility(View.VISIBLE); - expandedView.setTranslationY(-expandedView.getHeight()); - expandedView.setAlpha(0); - expandedView - .animate() - .setInterpolator(new LinearOutSlowInInterpolator()) - .translationY(0) - .alpha(1); + // Show arrow after animation + viewHolder.getArrow().setVisibility(View.VISIBLE); + // Safe to click primary button now + viewHolder.setPrimaryButtonClickable(true); } }); - animator.start(); + expandAnimatorSet.start(); expandedView.getViewTreeObserver().removeOnPreDrawListener(this); return false; @@ -315,89 +324,79 @@ public class NewBubble { if (isUserAction && collapseEndAction == CollapseEnd.NOTHING) { logBasicOrCallImpression(DialerImpression.Type.BUBBLE_COLLAPSE_BY_USER); } + setPrimaryButtonAccessibilityAction( context.getString(R.string.a11y_bubble_primary_button_expand_action)); - // Animate expanded view to move from its position to above primary button and hide - collapseAnimation = - expandedView - .animate() - .translationY(-expandedView.getHeight()) - .alpha(0) - .setInterpolator(new FastOutLinearInInterpolator()) - .withEndAction( - () -> { - collapseAnimation = null; - expanded = false; - if (textShowing) { - // Will do resize once the text is done. - return; - } + // Hide arrow before animation + viewHolder.getArrow().setVisibility(View.INVISIBLE); + + // No click during animation to avoid jank. + viewHolder.setPrimaryButtonClickable(false); - // Set drawer visibility to INVISIBLE instead of GONE to keep primary button fixed - viewHolder.setDrawerVisibility(View.INVISIBLE); - - // Do X-move and Y-move together - int deltaX = - (int) viewHolder.getRoot().findViewById(R.id.bubble_primary_container).getX(); - int startX = windowParams.x; - int startY = windowParams.y; - float k = - (savedYPosition != -1 && shouldRecoverYPosition) - ? (savedYPosition - startY) / (float) deltaX - : 0; - Path path = new Path(); - path.moveTo(windowParams.x, windowParams.y); - path.lineTo( - windowParams.x - deltaX, - (savedYPosition != -1 && shouldRecoverYPosition) - ? savedYPosition - : windowParams.y); - // The position is not useful after collapse - savedYPosition = -1; - - ValueAnimator animator = ValueAnimator.ofFloat(startX, startX - deltaX); - animator.setInterpolator(new LinearOutSlowInInterpolator()); - animator.addUpdateListener( - (valueAnimator) -> { - // Update windowParams and the root layout. - // We can't do ViewPropertyAnimation since it clips children. - float newX = (float) valueAnimator.getAnimatedValue(); - if (k != 0) { - windowParams.y = startY + (int) (Math.abs(newX - (float) startX) * k); - } - windowParams.x = (int) newX; - windowManager.updateViewLayout(viewHolder.getRoot(), windowParams); - }); - animator.addListener( - new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - // If collapse on the right side, the primary button move left a bit after - // drawer - // visibility becoming GONE. To avoid it, we create a new ViewHolder. - replaceViewHolder(); - } - }); - animator.start(); - - // If this collapse was to come before a hide, do it now. - if (collapseEndAction == CollapseEnd.HIDE) { - hide(); + // Calculate animation values + int deltaX = (int) viewHolder.getRoot().findViewById(R.id.bubble_primary_container).getX(); + float k = + (savedYPosition != -1 && shouldRecoverYPosition) + ? (savedYPosition - windowParams.y) / (float) deltaX + : 0; + // The position is not useful after collapse + savedYPosition = -1; + + // Animation 1: animate x-move and y-move (if needed) together + ValueAnimator xValueAnimator = + createBubbleMoveAnimator(windowParams.x, windowParams.x - deltaX, windowParams.y, k); + + // Animator 2: hide expanded view to top left or top right + View expandedMenu = viewHolder.getRoot().findViewById(R.id.bubble_expanded_menu); + ValueAnimator revealAnim = + createOpenCloseOutlineProvider(expandedMenu).createRevealAnimator(expandedMenu, true); + revealAnim.setInterpolator(accelerateDecelerateInterpolator); + + // Animator 3: expanded view fade out + Animator fadeOut = ObjectAnimator.ofFloat(expandedView, "alpha", 1, 0); + fadeOut.setInterpolator(accelerateDecelerateInterpolator); + + // Play all animation together + AnimatorSet collapseAnimatorSet = new AnimatorSet(); + collapseAnimatorSet.setDuration(EXPAND_AND_COLLAPSE_ANIMATION_DURATION); + collapseAnimatorSet.playTogether(revealAnim, fadeOut, xValueAnimator); + collapseAnimatorSet.addListener( + new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + collapseAnimation = null; + expanded = false; + + if (textShowing) { + // Will do resize once the text is done. + return; + } + + // If this collapse was to come before a hide, do it now. + if (collapseEndAction == CollapseEnd.HIDE) { + hide(); + } + collapseEndAction = CollapseEnd.NOTHING; + + // If collapse on the right side, the primary button move left a bit after drawer + // visibility becoming GONE. To avoid it, we create a new ViewHolder. + // It also set primary button clickable back to true, so no need to reset manually. + replaceViewHolder(); + + // Resume normal gravity after any resizing is done. + handler.postDelayed( + () -> { + overrideGravity = null; + if (!viewHolder.isMoving()) { + viewHolder.undoGravityOverride(); } - collapseEndAction = CollapseEnd.NOTHING; - - // Resume normal gravity after any resizing is done. - handler.postDelayed( - () -> { - overrideGravity = null; - if (!viewHolder.isMoving()) { - viewHolder.undoGravityOverride(); - } - }, - // Need to wait twice as long for resize and layout - WINDOW_REDRAW_DELAY_MILLIS * 2); - }); + }, + // Need to wait twice as long for resize and layout + WINDOW_REDRAW_DELAY_MILLIS * 2); + } + }); + collapseAnimatorSet.start(); } /** @@ -917,18 +916,45 @@ public class NewBubble { }); } + private RoundedRectRevealOutlineProvider createOpenCloseOutlineProvider(View view) { + int startRectX = isDrawingFromRight() ? view.getMeasuredWidth() : 0; + Rect startRect = new Rect(startRectX, 0, startRectX, 0); + Rect endRect = new Rect(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight()); + + float bubbleRadius = context.getResources().getDimension(R.dimen.bubble_radius); + return new RoundedRectRevealOutlineProvider(bubbleRadius, bubbleRadius, startRect, endRect); + } + + private ValueAnimator createBubbleMoveAnimator(int startX, int endX, int startY, float k) { + ValueAnimator xValueAnimator = ValueAnimator.ofFloat(startX, endX); + xValueAnimator.setInterpolator(new LinearOutSlowInInterpolator()); + xValueAnimator.addUpdateListener( + (valueAnimator) -> { + // Update windowParams and the root layout. + // We can't do ViewPropertyAnimation since it clips children. + float newX = (float) valueAnimator.getAnimatedValue(); + if (k != 0) { + windowParams.y = startY + (int) (Math.abs(newX - (float) startX) * k); + } + windowParams.x = (int) newX; + windowManager.updateViewLayout(viewHolder.getRoot(), windowParams); + }); + return xValueAnimator; + } + @VisibleForTesting class ViewHolder { public static final int CHILD_INDEX_AVATAR_AND_ICON = 0; public static final int CHILD_INDEX_TEXT = 1; - private final NewMoveHandler moveHandler; + private NewMoveHandler moveHandler; private final NewWindowRoot root; private final ViewAnimator primaryButton; private final ImageView primaryIcon; private final ImageView primaryAvatar; private final TextView primaryText; + private final View arrow; private final NewCheckableButton fullScreenButton; private final NewCheckableButton muteButton; @@ -946,6 +972,7 @@ public class NewBubble { primaryAvatar = contentView.findViewById(R.id.bubble_icon_avatar); primaryIcon = contentView.findViewById(R.id.bubble_icon_primary); primaryText = contentView.findViewById(R.id.bubble_text); + arrow = contentView.findViewById(R.id.bubble_triangle); fullScreenButton = contentView.findViewById(R.id.bubble_button_full_screen); muteButton = contentView.findViewById(R.id.bubble_button_mute); @@ -985,8 +1012,10 @@ public class NewBubble { muteButton.setClickable(clickable); audioRouteButton.setClickable(clickable); endCallButton.setClickable(clickable); + setPrimaryButtonClickable(clickable); + } - // For primaryButton + private void setPrimaryButtonClickable(boolean clickable) { moveHandler.setClickable(clickable); } @@ -1024,6 +1053,10 @@ public class NewBubble { return expandedView; } + public View getArrow() { + return arrow; + } + public NewCheckableButton getFullScreenButton() { return fullScreenButton; } |