diff options
author | Jon Miranda <jonmiranda@google.com> | 2019-04-16 20:53:24 -0700 |
---|---|---|
committer | Jon Miranda <jonmiranda@google.com> | 2019-04-19 15:13:10 -0700 |
commit | de0093dfa5e5ffe128a64ad7b767b6b3e650473b (patch) | |
tree | c696ad8c300a0fded0c6d1cd888b4d6c9ca3f304 /src | |
parent | 0f8c7c0e91c7d5edb4193e436ad1d2a5671bb96c (diff) | |
download | android_packages_apps_Trebuchet-de0093dfa5e5ffe128a64ad7b767b6b3e650473b.tar.gz android_packages_apps_Trebuchet-de0093dfa5e5ffe128a64ad7b767b6b3e650473b.tar.bz2 android_packages_apps_Trebuchet-de0093dfa5e5ffe128a64ad7b767b6b3e650473b.zip |
Add ListenerView so that we can fast finish the FloatingIconView fade anim.
This change allows us to quickly end the animation if the user begins
interacting with launcher before the animation is over. This can currently
happen when the user swipes up to go home, and then quickly swipes up again
to enter overview.
Bug: 129421279
Change-Id: I88c7d55ef8ac09f999c082317de3bb3693c11466
Diffstat (limited to 'src')
-rw-r--r-- | src/com/android/launcher3/AbstractFloatingView.java | 11 | ||||
-rw-r--r-- | src/com/android/launcher3/views/FloatingIconView.java | 131 | ||||
-rw-r--r-- | src/com/android/launcher3/views/ListenerView.java | 86 |
3 files changed, 181 insertions, 47 deletions
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java index 0e082760a..3cb6ba67f 100644 --- a/src/com/android/launcher3/AbstractFloatingView.java +++ b/src/com/android/launcher3/AbstractFloatingView.java @@ -30,6 +30,7 @@ import android.util.AttributeSet; import android.util.Pair; import android.view.MotionEvent; import android.view.View; +import android.view.ViewGroup; import android.widget.LinearLayout; import androidx.annotation.IntDef; @@ -58,6 +59,7 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch TYPE_ON_BOARD_POPUP, TYPE_DISCOVERY_BOUNCE, TYPE_SNACKBAR, + TYPE_LISTENER, TYPE_TASK_MENU, TYPE_OPTIONS_POPUP @@ -72,15 +74,16 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch public static final int TYPE_ON_BOARD_POPUP = 1 << 5; public static final int TYPE_DISCOVERY_BOUNCE = 1 << 6; public static final int TYPE_SNACKBAR = 1 << 7; + public static final int TYPE_LISTENER = 1 << 8; // Popups related to quickstep UI - public static final int TYPE_TASK_MENU = 1 << 8; - public static final int TYPE_OPTIONS_POPUP = 1 << 9; + public static final int TYPE_TASK_MENU = 1 << 9; + public static final int TYPE_OPTIONS_POPUP = 1 << 10; public static final int TYPE_ALL = TYPE_FOLDER | TYPE_ACTION_POPUP | TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_WIDGETS_FULL_SHEET | TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE | TYPE_TASK_MENU - | TYPE_OPTIONS_POPUP | TYPE_SNACKBAR; + | TYPE_OPTIONS_POPUP | TYPE_SNACKBAR | TYPE_LISTENER; // Type of popups which should be kept open during launcher rebind public static final int TYPE_REBIND_SAFE = TYPE_WIDGETS_FULL_SHEET @@ -90,7 +93,7 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch public static final int TYPE_HIDE_BACK_BUTTON = TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE | TYPE_SNACKBAR; - public static final int TYPE_ACCESSIBLE = TYPE_ALL & ~TYPE_DISCOVERY_BOUNCE; + public static final int TYPE_ACCESSIBLE = TYPE_ALL & ~TYPE_DISCOVERY_BOUNCE & ~TYPE_LISTENER; // These view all have particular operation associated with swipe down interaction. public static final int TYPE_STATUS_BAR_SWIPE_DOWN_DISALLOW = TYPE_WIDGETS_BOTTOM_SHEET | diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java index f96652eba..f2fc7182a 100644 --- a/src/com/android/launcher3/views/FloatingIconView.java +++ b/src/com/android/launcher3/views/FloatingIconView.java @@ -70,7 +70,7 @@ import static com.android.launcher3.Utilities.mapToRange; public class FloatingIconView extends View implements Animator.AnimatorListener, ClipPathView { public static final float SHAPE_PROGRESS_DURATION = 0.15f; - + private static final int FADE_DURATION_MS = 200; private static final Rect sTmpRect = new Rect(); private Runnable mEndRunnable; @@ -93,10 +93,15 @@ public class FloatingIconView extends View implements Animator.AnimatorListener, private float mBgDrawableStartScale = 1f; private float mBgDrawableEndScale = 1f; + private AnimatorSet mFadeAnimatorSet; + private ListenerView mListenerView; + private FloatingIconView(Context context) { super(context); + mBlurSizeOutline = context.getResources().getDimensionPixelSize( R.dimen.blur_size_medium_outline); + mListenerView = new ListenerView(context, null); } /** @@ -138,6 +143,12 @@ public class FloatingIconView extends View implements Animator.AnimatorListener, if (mRevealAnimator == null) { mRevealAnimator = (ValueAnimator) FolderShape.getShape().createRevealAnimator(this, mStartRevealRect, mEndRevealRect, mTaskCornerRadius / scale, !isOpening); + mRevealAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mRevealAnimator = null; + } + }); mRevealAnimator.start(); // We pause here so we can set the current fraction ourselves. mRevealAnimator.pause(); @@ -314,7 +325,7 @@ public class FloatingIconView extends View implements Animator.AnimatorListener, @WorkerThread private int getOffsetForIconBounds(Drawable drawable) { - if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O || + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O || !(drawable instanceof AdaptiveIconDrawable)) { return 0; } @@ -364,6 +375,18 @@ public class FloatingIconView extends View implements Animator.AnimatorListener, } } + public void onListenerViewClosed() { + // Fast finish here. + if (mEndRunnable != null) { + mEndRunnable.run(); + mEndRunnable = null; + } + if (mFadeAnimatorSet != null) { + mFadeAnimatorSet.end(); + mFadeAnimatorSet = null; + } + } + @Override public void onAnimationStart(Animator animator) {} @@ -410,52 +433,69 @@ public class FloatingIconView extends View implements Animator.AnimatorListener, // We need to add it to the overlay, but keep it invisible until animation starts.. final DragLayer dragLayer = launcher.getDragLayer(); view.setVisibility(INVISIBLE); - ((ViewGroup) dragLayer.getParent()).getOverlay().add(view); + ((ViewGroup) dragLayer.getParent()).addView(view); + dragLayer.addView(view.mListenerView); + view.mListenerView.setListener(view::onListenerViewClosed); - if (hideOriginal) { - view.mEndRunnable = () -> { - AnimatorSet fade = new AnimatorSet(); - fade.setDuration(200); - fade.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - originalView.setVisibility(VISIBLE); - } + view.mEndRunnable = () -> { + view.mEndRunnable = null; - @Override - public void onAnimationEnd(Animator animation) { - ((ViewGroup) dragLayer.getParent()).getOverlay().remove(view); + if (hideOriginal) { + if (isOpening) { + originalView.setVisibility(VISIBLE); + view.finish(dragLayer); + } else { + view.mFadeAnimatorSet = view.createFadeAnimation(originalView, dragLayer); + view.mFadeAnimatorSet.start(); + } + } else { + view.finish(dragLayer); + } + }; + return view; + } - if (view.mRevealAnimator != null) { - view.mRevealAnimator.end(); - } - } - }); + private AnimatorSet createFadeAnimation(View originalView, DragLayer dragLayer) { + AnimatorSet fade = new AnimatorSet(); + fade.setDuration(FADE_DURATION_MS); + fade.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + originalView.setVisibility(VISIBLE); + } - if (originalView instanceof FolderIcon) { - FolderIcon folderIcon = (FolderIcon) originalView; - folderIcon.setBackgroundVisible(false); - folderIcon.getFolderName().setTextVisibility(false); - fade.play(folderIcon.getFolderName().createTextAlphaAnimator(true)); - fade.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - folderIcon.setBackgroundVisible(true); - folderIcon.animateBgShadowAndStroke(); - if (folderIcon.hasDot()) { - folderIcon.animateDotScale(0, 1f); - } - } - }); - } else { - fade.play(ObjectAnimator.ofFloat(originalView, ALPHA, 0f, 1f)); + @Override + public void onAnimationEnd(Animator animation) { + finish(dragLayer); + } + }); + + if (originalView instanceof FolderIcon) { + FolderIcon folderIcon = (FolderIcon) originalView; + folderIcon.setBackgroundVisible(false); + folderIcon.getFolderName().setTextVisibility(false); + fade.play(folderIcon.getFolderName().createTextAlphaAnimator(true)); + fade.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + folderIcon.setBackgroundVisible(true); + folderIcon.animateBgShadowAndStroke(); + if (folderIcon.hasDot()) { + folderIcon.animateDotScale(0, 1f); + } } - fade.start(); - // TODO: Do not run fade animation until we fix b/129421279. - fade.end(); - }; + }); + } else { + fade.play(ObjectAnimator.ofFloat(originalView, ALPHA, 0f, 1f)); } - return view; + + return fade; + } + + private void finish(DragLayer dragLayer) { + ((ViewGroup) dragLayer.getParent()).removeView(this); + dragLayer.removeView(mListenerView); + recycle(); } private void recycle() { @@ -475,10 +515,15 @@ public class FloatingIconView extends View implements Animator.AnimatorListener, mBackground = null; mClipPath = null; mFinalDrawableBounds.setEmpty(); - mBgDrawableBounds.setEmpty();; + mBgDrawableBounds.setEmpty(); if (mRevealAnimator != null) { mRevealAnimator.cancel(); } mRevealAnimator = null; + if (mFadeAnimatorSet != null) { + mFadeAnimatorSet.cancel(); + } + mFadeAnimatorSet = null; + mListenerView.setListener(null); } } diff --git a/src/com/android/launcher3/views/ListenerView.java b/src/com/android/launcher3/views/ListenerView.java new file mode 100644 index 000000000..263f7c4c3 --- /dev/null +++ b/src/com/android/launcher3/views/ListenerView.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2019 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. + */ +package com.android.launcher3.views; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; + +import com.android.launcher3.AbstractFloatingView; + +/** + * An invisible AbstractFloatingView that can run a callback when it is being closed. + */ +public class ListenerView extends AbstractFloatingView { + + public Runnable mCloseListener; + + public ListenerView(Context context, AttributeSet attrs) { + super(context, attrs); + setVisibility(View.GONE); + } + + public void setListener(Runnable listener) { + mCloseListener = listener; + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + mIsOpen = true; + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + mIsOpen = false; + } + + @Override + protected void handleClose(boolean animate) { + if (mIsOpen) { + if (mCloseListener != null) { + mCloseListener.run(); + } else { + if (getParent() instanceof ViewGroup) { + ((ViewGroup) getParent()).removeView(this); + } + } + } + mIsOpen = false; + } + + @Override + public void logActionCommand(int command) { + // Users do not interact with FloatingIconView, so there is nothing to log here. + } + + @Override + protected boolean isOfType(int type) { + return (type & TYPE_LISTENER) != 0; + } + + @Override + public boolean onControllerInterceptTouchEvent(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + handleClose(false); + } + // We want other views to be able to intercept the touch so we return false here. + return false; + } +} |