diff options
11 files changed, 353 insertions, 234 deletions
diff --git a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java index 051c80f97..d1d697c0c 100644 --- a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java +++ b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java @@ -173,8 +173,10 @@ final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> imple // Keep recents visible throughout the animation. SurfaceParams[] params = new SurfaceParams[2]; + // Closing app should stay on top. + int boostedMode = MODE_CLOSING; params[0] = new SurfaceParams(recentsTarget.leash, 1f, null /* matrix */, - null /* windowCrop */, getLayer(recentsTarget, MODE_OPENING), 0 /* cornerRadius */); + null /* windowCrop */, getLayer(recentsTarget, boostedMode), 0 /* cornerRadius */); valueAnimator.addUpdateListener(new MultiValueUpdateListener() { private final FloatProp mScaleX; @@ -214,7 +216,7 @@ final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> imple m.postTranslate(mTranslationX.value, mTranslationY.value); params[1] = new SurfaceParams(appTarget.leash, mAlpha.value, m, - null /* windowCrop */, getLayer(appTarget, MODE_CLOSING), + null /* windowCrop */, getLayer(appTarget, boostedMode), 0 /* cornerRadius */); surfaceApplier.scheduleApply(params); } diff --git a/go/quickstep/src/com/android/quickstep/RecentsActivity.java b/go/quickstep/src/com/android/quickstep/RecentsActivity.java index 447e7e723..f2ca368e8 100644 --- a/go/quickstep/src/com/android/quickstep/RecentsActivity.java +++ b/go/quickstep/src/com/android/quickstep/RecentsActivity.java @@ -67,9 +67,7 @@ public final class RecentsActivity extends BaseRecentsActivity { @Override protected void onStart() { - // Set the alpha to 1 before calling super, as it may get set back to 0 due to - // onActivityStart callback. - mIconRecentsView.setAlpha(0); + mIconRecentsView.onBeginTransitionToOverview(); super.onStart(); } } diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java index e841f488a..5bb4c5aaa 100644 --- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java +++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java @@ -155,8 +155,6 @@ public final class IconRecentsView extends FrameLayout { * Logic for when we know we are going to overview/recents and will be putting up the recents * view. This should be used to prepare recents (e.g. load any task data, etc.) before it * becomes visible. - * - * TODO: Hook this up for fallback recents activity as well */ public void onBeginTransitionToOverview() { // Load any task changes diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java index 36014a9b5..f507d0f9d 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java @@ -30,6 +30,7 @@ import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.uioverrides.touchcontrollers.FlingAndHoldTouchController; import com.android.launcher3.uioverrides.touchcontrollers.LandscapeEdgeSwipeController; +import com.android.launcher3.uioverrides.touchcontrollers.NavBarToHomeTouchController; import com.android.launcher3.uioverrides.touchcontrollers.OverviewToAllAppsTouchController; import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController; import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchController; @@ -64,6 +65,7 @@ public abstract class RecentsUiFactory { list.add(launcher.getDragController()); if (mode == Mode.NO_BUTTON) { list.add(new QuickSwitchTouchController(launcher)); + list.add(new NavBarToHomeTouchController(launcher)); list.add(new FlingAndHoldTouchController(launcher)); } else { if (launcher.getDeviceProfile().isVerticalBarLayout()) { diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java new file mode 100644 index 000000000..673beff85 --- /dev/null +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java @@ -0,0 +1,141 @@ +/* + * 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.uioverrides.touchcontrollers; + +import static com.android.launcher3.LauncherState.ALL_APPS; +import static com.android.launcher3.LauncherState.NORMAL; +import static com.android.launcher3.LauncherState.OVERVIEW; +import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS; +import static com.android.launcher3.anim.Interpolators.DEACCEL_3; + +import android.animation.Animator; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.view.MotionEvent; +import android.view.View; +import android.view.animation.Interpolator; + +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherState; +import com.android.launcher3.LauncherStateManager.AnimationConfig; +import com.android.launcher3.Utilities; +import com.android.launcher3.allapps.AllAppsTransitionController; +import com.android.launcher3.anim.AnimatorPlaybackController; +import com.android.launcher3.anim.AnimatorSetBuilder; +import com.android.launcher3.anim.Interpolators; +import com.android.launcher3.touch.AbstractStateChangeTouchController; +import com.android.launcher3.touch.SwipeDetector; +import com.android.launcher3.userevent.nano.LauncherLogProto; +import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; +import com.android.quickstep.views.RecentsView; + +/** + * Handles swiping up on the nav bar to go home from overview or all apps. + */ +public class NavBarToHomeTouchController extends AbstractStateChangeTouchController { + + private static final Interpolator PULLBACK_INTERPOLATOR = DEACCEL_3; + + public NavBarToHomeTouchController(Launcher launcher) { + super(launcher, SwipeDetector.VERTICAL); + } + + @Override + protected boolean canInterceptTouch(MotionEvent ev) { + boolean cameFromNavBar = (ev.getEdgeFlags() & Utilities.EDGE_NAV_BAR) != 0; + return cameFromNavBar && (mLauncher.isInState(OVERVIEW) || mLauncher.isInState(ALL_APPS)); + } + + @Override + protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) { + return isDragTowardPositive ? NORMAL : fromState; + } + + @Override + protected float initCurrentAnimation(int animComponents) { + long accuracy = (long) (getShiftRange() * 2); + final AnimatorSet anim; + if (mFromState == OVERVIEW) { + anim = new AnimatorSet(); + RecentsView recentsView = mLauncher.getOverviewPanel(); + float pullbackDistance = recentsView.getPaddingStart() / 2; + if (!recentsView.isRtl()) { + pullbackDistance = -pullbackDistance; + } + anim.play(ObjectAnimator.ofFloat(recentsView, View.TRANSLATION_X, pullbackDistance)); + anim.setInterpolator(PULLBACK_INTERPOLATOR); + } else { // if (mFromState == ALL_APPS) + AnimatorSetBuilder builder = new AnimatorSetBuilder(); + AllAppsTransitionController allAppsController = mLauncher.getAllAppsController(); + final float pullbackDistance = mLauncher.getDeviceProfile().allAppsIconSizePx / 2; + Animator allAppsProgress = ObjectAnimator.ofFloat(allAppsController, ALL_APPS_PROGRESS, + -pullbackDistance / allAppsController.getShiftRange()); + allAppsProgress.setInterpolator(PULLBACK_INTERPOLATOR); + builder.play(allAppsProgress); + // Slightly fade out all apps content to further distinguish from scrolling. + builder.setInterpolator(AnimatorSetBuilder.ANIM_ALL_APPS_FADE, Interpolators + .mapToProgress(PULLBACK_INTERPOLATOR, 0, 0.5f)); + AnimationConfig config = new AnimationConfig(); + config.duration = accuracy; + allAppsController.setAlphas(mToState.getVisibleElements(mLauncher), config, builder); + anim = builder.build(); + } + anim.setDuration(accuracy); + mCurrentAnimation = AnimatorPlaybackController.wrap(anim, accuracy, this::clearState); + return -1 / getShiftRange(); + } + + @Override + public void onDragStart(boolean start) { + super.onDragStart(start); + mStartContainerType = LauncherLogProto.ContainerType.NAVBAR; + } + + @Override + public void onDragEnd(float velocity, boolean fling) { + final int logAction = fling ? Touch.FLING : Touch.SWIPE; + float interpolatedProgress = PULLBACK_INTERPOLATOR.getInterpolation( + mCurrentAnimation.getProgressFraction()); + if (interpolatedProgress >= SUCCESS_TRANSITION_PROGRESS || velocity < 0 && fling) { + mLauncher.getStateManager().goToState(mToState, true, + () -> onSwipeInteractionCompleted(mToState, logAction)); + } else { + // Quickly return to the state we came from (we didn't move far). + AnimatorPlaybackController anim = mLauncher.getStateManager() + .createAnimationToNewWorkspace(mFromState, 80); + anim.setEndAction(() -> onSwipeInteractionCompleted(mFromState, logAction)); + anim.start(); + } + mCurrentAnimation.dispatchOnCancel(); + } + + @Override + protected int getDirectionForLog() { + return LauncherLogProto.Action.Direction.UP; + } + + @Override + protected boolean goingBetweenNormalAndOverview(LauncherState fromState, + LauncherState toState) { + // We don't want to create an atomic animation to/from overview. + return false; + } + + @Override + protected int getLogContainerTypeForNormalState() { + return LauncherLogProto.ContainerType.NAVBAR; + } +} diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java index 9763063b6..69f3338e1 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java @@ -113,8 +113,7 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe final Rect iconLocation = new Rect(); final FloatingIconView floatingView = workspaceView == null ? null : FloatingIconView.getFloatingIconView(activity, workspaceView, - true /* hideOriginal */, false /* useDrawableAsIs */, - activity.getDeviceProfile().getAspectRatioWithInsets(), iconLocation, null); + true /* hideOriginal */, iconLocation, false /* isOpening */, null /* recycle */); return new HomeAnimationFactory() { @Nullable diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java index 15072a254..0065cb5cf 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java @@ -28,6 +28,7 @@ import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS; import static com.android.launcher3.config.FeatureFlags.SWIPE_HOME; import static com.android.launcher3.util.RaceConditionTracker.ENTER; import static com.android.launcher3.util.RaceConditionTracker.EXIT; +import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION; import static com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState.HIDE; import static com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState.PEEK; import static com.android.quickstep.MultiStateCallback.DEBUG_STATES; @@ -945,7 +946,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> // We want the window alpha to be 0 once this threshold is met, so that the // FolderIconView can be seen morphing into the icon shape. - final float windowAlphaThreshold = isFloatingIconView ? 0.75f : 1f; + final float windowAlphaThreshold = isFloatingIconView ? 1f - SHAPE_PROGRESS_DURATION : 1f; anim.addOnUpdateListener((currentRect, progress) -> { float interpolatedProgress = Interpolators.ACCEL_1_5.getInterpolation(progress); @@ -959,7 +960,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> if (isFloatingIconView) { ((FloatingIconView) floatingView).update(currentRect, iconAlpha, progress, - windowAlphaThreshold); + windowAlphaThreshold, mClipAnimationHelper.getCurrentCornerRadius(), false); } }); diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java index c5475d687..f77bd6513 100644 --- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java +++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java @@ -20,15 +20,16 @@ import static com.android.launcher3.BaseActivity.INVISIBLE_ALL; import static com.android.launcher3.BaseActivity.INVISIBLE_BY_APP_TRANSITIONS; import static com.android.launcher3.BaseActivity.INVISIBLE_BY_PENDING_FLAGS; import static com.android.launcher3.BaseActivity.PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION; -import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.Utilities.postAsyncCallback; import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS; import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE; import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7; +import static com.android.launcher3.anim.Interpolators.EXAGGERATED_EASE; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_TRANSITIONS; +import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION; import static com.android.quickstep.TaskUtils.taskIsATargetWithMode; import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING; import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING; @@ -45,6 +46,7 @@ import android.content.pm.PackageManager; import android.content.res.Resources; import android.graphics.Matrix; import android.graphics.Rect; +import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.CancellationSignal; @@ -52,10 +54,8 @@ import android.os.Handler; import android.os.Looper; import android.util.Pair; import android.view.View; -import android.view.ViewGroup; import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener; -import com.android.launcher3.InsettableFrameLayout.LayoutParams; import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.dragndrop.DragLayer; @@ -103,13 +103,18 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans private static final String CONTROL_REMOTE_APP_TRANSITION_PERMISSION = "android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"; - private static final int APP_LAUNCH_DURATION = 500; + private static final long APP_LAUNCH_DURATION = 500; // Use a shorter duration for x or y translation to create a curve effect - private static final int APP_LAUNCH_CURVED_DURATION = APP_LAUNCH_DURATION / 2; + private static final long APP_LAUNCH_CURVED_DURATION = APP_LAUNCH_DURATION / 2; + private static final long APP_LAUNCH_ALPHA_DURATION = 50; + // We scale the durations for the downward app launch animations (minus the scale animation). private static final float APP_LAUNCH_DOWN_DUR_SCALE_FACTOR = 0.8f; - private static final int APP_LAUNCH_ALPHA_START_DELAY = 32; - private static final int APP_LAUNCH_ALPHA_DURATION = 50; + private static final long APP_LAUNCH_DOWN_DURATION = + (long) (APP_LAUNCH_DURATION * APP_LAUNCH_DOWN_DUR_SCALE_FACTOR); + private static final long APP_LAUNCH_DOWN_CURVED_DURATION = APP_LAUNCH_DOWN_DURATION / 2; + private static final long APP_LAUNCH_ALPHA_DOWN_DURATION = + (long) (APP_LAUNCH_ALPHA_DURATION * APP_LAUNCH_DOWN_DUR_SCALE_FACTOR); public static final int RECENTS_LAUNCH_DURATION = 336; private static final int LAUNCHER_RESUME_START_DELAY = 100; @@ -207,11 +212,11 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans // Note that this duration is a guess as we do not know if the animation will be a // recents launch or not for sure until we know the opening app targets. - int duration = fromRecents + long duration = fromRecents ? RECENTS_LAUNCH_DURATION : APP_LAUNCH_DURATION; - int statusBarTransitionDelay = duration - STATUS_BAR_TRANSITION_DURATION + long statusBarTransitionDelay = duration - STATUS_BAR_TRANSITION_DURATION - STATUS_BAR_TRANSITION_PRE_DELAY; return ActivityOptionsCompat.makeRemoteAnimation(new RemoteAnimationAdapterCompat( runner, duration, statusBarTransitionDelay)); @@ -266,7 +271,8 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans } if (!isAllOpeningTargetTrs) break; } - playIconAnimators(anim, v, windowTargetBounds, !isAllOpeningTargetTrs); + anim.play(getOpeningWindowAnimators(v, targets, windowTargetBounds, + !isAllOpeningTargetTrs)); if (launcherClosing) { Pair<AnimatorSet, Runnable> launcherContentAnimator = getLauncherContentAnimator(true /* isAppOpening */, @@ -279,7 +285,6 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans } }); } - anim.play(getOpeningWindowAnimators(v, targets, windowTargetBounds)); } /** @@ -398,165 +403,116 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans float[] alphas, float[] trans); /** - * Animators for the "floating view" of the view used to launch the target. + * @return Animator that controls the window of the opening targets. */ - private void playIconAnimators(AnimatorSet appOpenAnimator, View v, Rect windowTargetBounds, - boolean toggleVisibility) { - final boolean isBubbleTextView = v instanceof BubbleTextView; - if (mFloatingView != null) { - mFloatingView.setTranslationX(0); - mFloatingView.setTranslationY(0); - mFloatingView.setScaleX(1); - mFloatingView.setScaleY(1); - mFloatingView.setAlpha(1); - mFloatingView.setBackground(null); - } - Rect rect = new Rect(); + private ValueAnimator getOpeningWindowAnimators(View v, RemoteAnimationTargetCompat[] targets, + Rect windowTargetBounds, boolean toggleVisibility) { + Rect bounds = new Rect(); mFloatingView = FloatingIconView.getFloatingIconView(mLauncher, v, toggleVisibility, - true /* useDrawableAsIs */, -1 /* aspectRatio */, rect, mFloatingView); - - int viewLocationStart = mIsRtl ? windowTargetBounds.width() - rect.right : rect.left; - LayoutParams lp = (LayoutParams) mFloatingView.getLayoutParams(); - // Special RTL logic is needed to handle the window target bounds. - lp.leftMargin = mIsRtl ? windowTargetBounds.width() - rect.right : rect.left; - mFloatingView.setLayoutParams(lp); - - int[] dragLayerBounds = new int[2]; - mDragLayer.getLocationOnScreen(dragLayerBounds); - - // Animate the app icon to the center of the window bounds in screen coordinates. - float centerX = windowTargetBounds.centerX() - dragLayerBounds[0]; - float centerY = windowTargetBounds.centerY() - dragLayerBounds[1]; - - float xPosition = mIsRtl - ? windowTargetBounds.width() - lp.getMarginStart() - rect.width() - : lp.getMarginStart(); - float dX = centerX - xPosition - (lp.width / 2f); - float dY = centerY - lp.topMargin - (lp.height / 2f); - - ObjectAnimator x = ObjectAnimator.ofFloat(mFloatingView, View.TRANSLATION_X, 0f, dX); - ObjectAnimator y = ObjectAnimator.ofFloat(mFloatingView, View.TRANSLATION_Y, 0f, dY); + bounds, true /* isOpening */, mFloatingView); + Rect crop = new Rect(); + Matrix matrix = new Matrix(); - // Use upward animation for apps that are either on the bottom half of the screen, or are - // relatively close to the center. - boolean useUpwardAnimation = lp.topMargin > centerY - || Math.abs(dY) < mLauncher.getDeviceProfile().cellHeightPx; - if (useUpwardAnimation) { - x.setDuration(APP_LAUNCH_CURVED_DURATION); - y.setDuration(APP_LAUNCH_DURATION); - } else { - x.setDuration((long) (APP_LAUNCH_DOWN_DUR_SCALE_FACTOR * APP_LAUNCH_DURATION)); - y.setDuration((long) (APP_LAUNCH_DOWN_DUR_SCALE_FACTOR * APP_LAUNCH_CURVED_DURATION)); - } - x.setInterpolator(AGGRESSIVE_EASE); - y.setInterpolator(AGGRESSIVE_EASE); - appOpenAnimator.play(x); - appOpenAnimator.play(y); + RemoteAnimationTargetSet openingTargets = new RemoteAnimationTargetSet(targets, + MODE_OPENING); + RemoteAnimationTargetSet closingTargets = new RemoteAnimationTargetSet(targets, + MODE_CLOSING); + SyncRtSurfaceTransactionApplierCompat surfaceApplier = + new SyncRtSurfaceTransactionApplierCompat(mFloatingView); // Scale the app icon to take up the entire screen. This simplifies the math when // animating the app window position / scale. - float maxScaleX = windowTargetBounds.width() / (float) rect.width(); - float maxScaleY = windowTargetBounds.height() / (float) rect.height(); + float maxScaleX = windowTargetBounds.width() / (float) bounds.width(); + // We use windowTargetBounds.width for scaleY too since we start off the animation where the + // window is clipped to a square. + float maxScaleY = windowTargetBounds.width() / (float) bounds.height(); float scale = Math.max(maxScaleX, maxScaleY); float startScale = 1f; - if (isBubbleTextView && !(v.getParent() instanceof DeepShortcutView)) { + if (v instanceof BubbleTextView && !(v.getParent() instanceof DeepShortcutView)) { Drawable dr = ((BubbleTextView) v).getIcon(); if (dr instanceof FastBitmapDrawable) { startScale = ((FastBitmapDrawable) dr).getAnimatedScale(); } } + final float initialStartScale = startScale; - ObjectAnimator scaleAnim = ObjectAnimator - .ofFloat(mFloatingView, SCALE_PROPERTY, startScale, scale); - scaleAnim.setDuration(APP_LAUNCH_DURATION) - .setInterpolator(Interpolators.EXAGGERATED_EASE); - appOpenAnimator.play(scaleAnim); - - // Fade out the app icon. - ObjectAnimator alpha = ObjectAnimator.ofFloat(mFloatingView, View.ALPHA, 1f, 0f); - if (useUpwardAnimation) { - alpha.setStartDelay(APP_LAUNCH_ALPHA_START_DELAY); - alpha.setDuration(APP_LAUNCH_ALPHA_DURATION); - } else { - alpha.setStartDelay((long) (APP_LAUNCH_DOWN_DUR_SCALE_FACTOR - * APP_LAUNCH_ALPHA_START_DELAY)); - alpha.setDuration((long) (APP_LAUNCH_DOWN_DUR_SCALE_FACTOR * APP_LAUNCH_ALPHA_DURATION)); - } - alpha.setInterpolator(LINEAR); - appOpenAnimator.play(alpha); + int[] dragLayerBounds = new int[2]; + mDragLayer.getLocationOnScreen(dragLayerBounds); + + // Animate the app icon to the center of the window bounds in screen coordinates. + float centerX = windowTargetBounds.centerX() - dragLayerBounds[0]; + float centerY = windowTargetBounds.centerY() - dragLayerBounds[1]; + + float dX = centerX - bounds.centerX(); + float dY = centerY - bounds.centerY(); + + boolean useUpwardAnimation = bounds.top > centerY + || Math.abs(dY) < mLauncher.getDeviceProfile().cellHeightPx; + final long xDuration = useUpwardAnimation ? APP_LAUNCH_CURVED_DURATION + : APP_LAUNCH_DOWN_DURATION; + final long yDuration = useUpwardAnimation ? APP_LAUNCH_DURATION + : APP_LAUNCH_DOWN_CURVED_DURATION; + final long alphaDuration = useUpwardAnimation ? APP_LAUNCH_ALPHA_DURATION + : APP_LAUNCH_ALPHA_DOWN_DURATION; - appOpenAnimator.addListener(mFloatingView); - appOpenAnimator.addListener(new AnimatorListenerAdapter() { + RectF targetBounds = new RectF(windowTargetBounds); + RectF currentBounds = new RectF(); + RectF temp = new RectF(); + + ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1); + appAnimator.setDuration(APP_LAUNCH_DURATION); + appAnimator.setInterpolator(LINEAR); + appAnimator.addListener(mFloatingView); + appAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - // Reset launcher to normal state - if (isBubbleTextView) { + if (v instanceof BubbleTextView) { ((BubbleTextView) v).setStayPressed(false); } - v.setVisibility(View.VISIBLE); - ((ViewGroup) mDragLayer.getParent()).getOverlay().remove(mFloatingView); } }); - } - /** - * @return Animator that controls the window of the opening targets. - */ - private ValueAnimator getOpeningWindowAnimators(View v, RemoteAnimationTargetCompat[] targets, - Rect windowTargetBounds) { - Rect bounds = new Rect(); - if (v.getParent() instanceof DeepShortcutView) { - // Deep shortcut views have their icon drawn in a separate view. - DeepShortcutView view = (DeepShortcutView) v.getParent(); - mDragLayer.getDescendantRectRelativeToSelf(view.getIconView(), bounds); - } else if (v instanceof BubbleTextView) { - ((BubbleTextView) v).getIconBounds(bounds); - } else { - mDragLayer.getDescendantRectRelativeToSelf(v, bounds); - } - int[] floatingViewBounds = new int[2]; - - Rect crop = new Rect(); - Matrix matrix = new Matrix(); - - RemoteAnimationTargetSet openingTargets = new RemoteAnimationTargetSet(targets, - MODE_OPENING); - RemoteAnimationTargetSet closingTargets = new RemoteAnimationTargetSet(targets, - MODE_CLOSING); - SyncRtSurfaceTransactionApplierCompat surfaceApplier = - new SyncRtSurfaceTransactionApplierCompat(mFloatingView); - - ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1); - appAnimator.setDuration(APP_LAUNCH_DURATION); + float shapeRevealDuration = APP_LAUNCH_DURATION * SHAPE_PROGRESS_DURATION; appAnimator.addUpdateListener(new MultiValueUpdateListener() { - // Fade alpha for the app window. - FloatProp mAlpha = new FloatProp(0f, 1f, 0, 60, LINEAR); + FloatProp mDx = new FloatProp(0, dX, 0, xDuration, AGGRESSIVE_EASE); + FloatProp mDy = new FloatProp(0, dY, 0, yDuration, AGGRESSIVE_EASE); + FloatProp mIconScale = new FloatProp(initialStartScale, scale, 0, APP_LAUNCH_DURATION, + EXAGGERATED_EASE); + FloatProp mIconAlpha = new FloatProp(1f, 0f, shapeRevealDuration, alphaDuration, + LINEAR); + FloatProp mCropHeight = new FloatProp(windowTargetBounds.width(), + windowTargetBounds.height(), 0, shapeRevealDuration, AGGRESSIVE_EASE); @Override public void onUpdate(float percent) { - final float easePercent = AGGRESSIVE_EASE.getInterpolation(percent); - // Calculate app icon size. - float iconWidth = bounds.width() * mFloatingView.getScaleX(); - float iconHeight = bounds.height() * mFloatingView.getScaleY(); + float iconWidth = bounds.width() * mIconScale.value; + float iconHeight = bounds.height() * mIconScale.value; + + // Animate the window crop so that it starts off as a square, and then reveals + // horizontally. + int windowWidth = windowTargetBounds.width(); + int windowHeight = (int) mCropHeight.value; + crop.set(0, 0, windowWidth, windowHeight); // Scale the app window to match the icon size. - float scaleX = iconWidth / windowTargetBounds.width(); - float scaleY = iconHeight / windowTargetBounds.height(); - float scale = Math.min(1f, Math.min(scaleX, scaleY)); + float scaleX = iconWidth / windowWidth; + float scaleY = iconHeight / windowHeight; + float scale = Math.min(1f, Math.max(scaleX, scaleY)); - // Position the scaled window on top of the icon - int windowWidth = windowTargetBounds.width(); - int windowHeight = windowTargetBounds.height(); float scaledWindowWidth = windowWidth * scale; float scaledWindowHeight = windowHeight * scale; float offsetX = (scaledWindowWidth - iconWidth) / 2; float offsetY = (scaledWindowHeight - iconHeight) / 2; - mFloatingView.getLocationOnScreen(floatingViewBounds); - float transX0 = floatingViewBounds[0] - offsetX; - float transY0 = floatingViewBounds[1] - offsetY; + // Calculate the window position + temp.set(bounds); + temp.offset(dragLayerBounds[0], dragLayerBounds[1]); + temp.offset(mDx.value, mDy.value); + Utilities.scaleRectFAboutCenter(temp, mIconScale.value); + float transX0 = temp.left - offsetX; + float transY0 = temp.top - offsetY; float windowRadius = 0; if (!mDeviceProfile.isMultiWindowMode && @@ -565,19 +521,9 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans .getWindowCornerRadius(); } - // Animate the window crop so that it starts off as a square, and then reveals - // horizontally. - float cropHeight = windowHeight * easePercent + windowWidth * (1 - easePercent); - float initialTop = (windowHeight - windowWidth) / 2f; - crop.left = 0; - crop.top = (int) (initialTop * (1 - easePercent)); - crop.right = windowWidth; - crop.bottom = (int) (crop.top + cropHeight); - SurfaceParams[] params = new SurfaceParams[targets.length]; for (int i = targets.length - 1; i >= 0; i--) { RemoteAnimationTargetCompat target = targets[i]; - Rect targetCrop; final float alpha; final float cornerRadius; @@ -585,12 +531,15 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans matrix.setScale(scale, scale); matrix.postTranslate(transX0, transY0); targetCrop = crop; - alpha = mAlpha.value; + alpha = 1f - mIconAlpha.value; cornerRadius = windowRadius; + matrix.mapRect(currentBounds, targetBounds); + mFloatingView.update(currentBounds, mIconAlpha.value, percent, 0f, + cornerRadius * scale, true /* isOpening */); } else { matrix.setTranslate(target.position.x, target.position.y); - alpha = 1f; targetCrop = target.sourceContainerBounds; + alpha = 1f; cornerRadius = 0; } diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 6397e14ac..6a3a26f77 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -51,6 +51,9 @@ public class DeviceProfile { public final int heightPx; public final int availableWidthPx; public final int availableHeightPx; + + public final float aspectRatio; + /** * The maximum amount of left/right workspace padding as a percentage of the screen width. * To be clear, this means that up to 7% of the screen width can be used as left padding, and @@ -160,7 +163,7 @@ public class DeviceProfile { isTablet = res.getBoolean(R.bool.is_tablet); isLargeTablet = res.getBoolean(R.bool.is_large_tablet); isPhone = !isTablet && !isLargeTablet; - float aspectRatio = ((float) Math.max(widthPx, heightPx)) / Math.min(widthPx, heightPx); + aspectRatio = ((float) Math.max(widthPx, heightPx)) / Math.min(widthPx, heightPx); boolean isTallDevice = Float.compare(aspectRatio, TALL_DEVICE_ASPECT_RATIO_THRESHOLD) >= 0; // Some more constants @@ -618,12 +621,6 @@ public class DeviceProfile { } } - public float getAspectRatioWithInsets() { - int w = widthPx - mInsets.left - mInsets.right; - int h = heightPx - mInsets.top - mInsets.bottom; - return ((float) Math.max(w, h)) / Math.min(w, h); - } - private static Context getContext(Context c, int orientation) { Configuration context = new Configuration(c.getResources().getConfiguration()); context.orientation = orientation; diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java index c403e76ee..0274de35c 100644 --- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java +++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java @@ -224,7 +224,8 @@ public abstract class AbstractStateChangeTouchController return true; } - private boolean goingBetweenNormalAndOverview(LauncherState fromState, LauncherState toState) { + protected boolean goingBetweenNormalAndOverview(LauncherState fromState, + LauncherState toState) { return (fromState == NORMAL || fromState == OVERVIEW) && (toState == NORMAL || toState == OVERVIEW) && mPendingAnimation == null; @@ -242,7 +243,7 @@ public abstract class AbstractStateChangeTouchController mStartContainerType = LauncherLogProto.ContainerType.ALLAPPS; } else if (mStartState == NORMAL) { mStartContainerType = getLogContainerTypeForNormalState(); - } else if (mStartState == OVERVIEW){ + } else if (mStartState == OVERVIEW){ mStartContainerType = LauncherLogProto.ContainerType.TASKSWITCHER; } if (mCurrentAnimation == null) { diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java index 49ec29269..5889468c5 100644 --- a/src/com/android/launcher3/views/FloatingIconView.java +++ b/src/com/android/launcher3/views/FloatingIconView.java @@ -15,6 +15,7 @@ */ package com.android.launcher3.views; +import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM; import android.animation.Animator; @@ -47,7 +48,6 @@ import com.android.launcher3.Launcher; import com.android.launcher3.LauncherModel; import com.android.launcher3.R; import com.android.launcher3.Utilities; -import com.android.launcher3.anim.Interpolators; import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.dragndrop.FolderAdaptiveIcon; import com.android.launcher3.folder.FolderIcon; @@ -60,18 +60,20 @@ import com.android.launcher3.shortcuts.DeepShortcutView; import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; +import static com.android.launcher3.Utilities.mapToRange; + /** * A view that is created to look like another view with the purpose of creating fluid animations. */ public class FloatingIconView extends View implements Animator.AnimatorListener, ClipPathView { + public static final float SHAPE_PROGRESS_DURATION = 0.15f; + private static final Rect sTmpRect = new Rect(); - private Runnable mStartRunnable; private Runnable mEndRunnable; - private int mOriginalHeight; private final int mBlurSizeOutline; private boolean mIsAdaptiveIcon = false; @@ -82,30 +84,28 @@ public class FloatingIconView extends View implements Animator.AnimatorListener, private final Rect mStartRevealRect = new Rect(); private final Rect mEndRevealRect = new Rect(); private Path mClipPath; - protected final Rect mOutline = new Rect(); - private final float mTaskCornerRadius; + private float mTaskCornerRadius; private final Rect mFinalDrawableBounds = new Rect(); private final Rect mBgDrawableBounds = new Rect(); private float mBgDrawableStartScale = 1f; + private float mBgDrawableEndScale = 1f; private FloatingIconView(Context context) { super(context); - mBlurSizeOutline = context.getResources().getDimensionPixelSize( R.dimen.blur_size_medium_outline); - - mTaskCornerRadius = 0; // TODO } /** * Positions this view to match the size and location of {@param rect}. - * * @param alpha The alpha to set this view. * @param progress A value from [0, 1] that represents the animation progress. - * @param windowAlphaThreshold The value at which the window alpha is 0. + * @param shapeProgressStart The progress value at which to start the shape reveal. + * @param cornerRadius The corner radius of {@param rect}. */ - public void update(RectF rect, float alpha, float progress, float windowAlphaThreshold) { + public void update(RectF rect, float alpha, float progress, float shapeProgressStart, + float cornerRadius, boolean isOpening) { setAlpha(alpha); LayoutParams lp = (LayoutParams) getLayoutParams(); @@ -116,49 +116,42 @@ public class FloatingIconView extends View implements Animator.AnimatorListener, float scaleX = rect.width() / (float) lp.width; float scaleY = rect.height() / (float) lp.height; - float scale = mIsAdaptiveIcon ? Math.max(scaleX, scaleY) : Math.min(scaleX, scaleY); + float scale = mIsAdaptiveIcon && !isOpening ? Math.max(scaleX, scaleY) + : Math.min(scaleX, scaleY); + scale = Math.max(1f, scale); + setPivotX(0); setPivotY(0); setScaleX(scale); setScaleY(scale); - // Wait until the window is no longer visible before morphing the icon into its final shape. - float shapeRevealProgress = Utilities.mapToRange(Math.max(windowAlphaThreshold, progress), - windowAlphaThreshold, 1f, 0f, 1, Interpolators.LINEAR); - if (mIsAdaptiveIcon && shapeRevealProgress > 0) { + // shapeRevealProgress = 1 when progress = shapeProgressStart + SHAPE_PROGRESS_DURATION + float toMax = isOpening ? 1 / SHAPE_PROGRESS_DURATION : 1f; + float shapeRevealProgress = Utilities.boundToRange(mapToRange( + Math.max(shapeProgressStart, progress), shapeProgressStart, 1f, 0, toMax, + LINEAR), 0, 1); + + mTaskCornerRadius = cornerRadius; + if (mIsAdaptiveIcon && shapeRevealProgress >= 0) { if (mRevealAnimator == null) { - mEndRevealRect.set(mOutline); - // We play the reveal animation in reverse so that we end with the icon shape. mRevealAnimator = (ValueAnimator) FolderShape.getShape().createRevealAnimator(this, - mStartRevealRect, mEndRevealRect, mTaskCornerRadius / scale, true); - mRevealAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mRevealAnimator = null; - } - }); + mStartRevealRect, mEndRevealRect, mTaskCornerRadius / scale, !isOpening); mRevealAnimator.start(); // We pause here so we can set the current fraction ourselves. mRevealAnimator.pause(); } - float bgScale = shapeRevealProgress + mBgDrawableStartScale * (1 - shapeRevealProgress); - setBackgroundDrawableBounds(bgScale); - mRevealAnimator.setCurrentFraction(shapeRevealProgress); + + float bgScale = (mBgDrawableEndScale * shapeRevealProgress) + mBgDrawableStartScale + * (1 - shapeRevealProgress); + setBackgroundDrawableBounds(bgScale); } invalidate(); invalidateOutline(); } @Override - public void onAnimationStart(Animator animator) { - if (mStartRunnable != null) { - mStartRunnable.run(); - } - } - - @Override public void onAnimationEnd(Animator animator) { if (mEndRunnable != null) { mEndRunnable.run(); @@ -180,7 +173,6 @@ public class FloatingIconView extends View implements Animator.AnimatorListener, Utilities.getLocationBoundsForView(launcher, v, positionOut); final LayoutParams lp = new LayoutParams(positionOut.width(), positionOut.height()); lp.ignoreInsets = true; - mOriginalHeight = lp.height; // Position the floating view exactly on top of the original lp.leftMargin = positionOut.left; @@ -193,11 +185,11 @@ public class FloatingIconView extends View implements Animator.AnimatorListener, } @WorkerThread - private void getIcon(Launcher launcher, View v, ItemInfo info, boolean useDrawableAsIs, - float aspectRatio) { + private void getIcon(Launcher launcher, View v, ItemInfo info, boolean isOpening, + Runnable onIconLoadedRunnable) { final LayoutParams lp = (LayoutParams) getLayoutParams(); Drawable drawable = null; - boolean supportsAdaptiveIcons = ADAPTIVE_ICON_WINDOW_ANIM.get() && !useDrawableAsIs + boolean supportsAdaptiveIcons = ADAPTIVE_ICON_WINDOW_ANIM.get() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O; if (!supportsAdaptiveIcons && v instanceof BubbleTextView) { // Similar to DragView, we simply use the BubbleTextView icon here. @@ -214,7 +206,7 @@ public class FloatingIconView extends View implements Animator.AnimatorListener, } if (drawable == null) { drawable = Utilities.getFullDrawable(launcher, info, lp.width, lp.height, - useDrawableAsIs, new Object[1]); + false, new Object[1]); } Drawable finalDrawable = drawable == null ? null @@ -247,35 +239,50 @@ public class FloatingIconView extends View implements Animator.AnimatorListener, sbd.setShiftY(sbd.getShiftY() - sTmpRect.top); } + final int originalHeight = lp.height; + final int originalWidth = lp.width; + int blurMargin = mBlurSizeOutline / 2; - mFinalDrawableBounds.set(0, 0, lp.width, mOriginalHeight); + mFinalDrawableBounds.set(0, 0, originalWidth, originalHeight); if (!isFolderIcon) { mFinalDrawableBounds.inset(iconOffset - blurMargin, iconOffset - blurMargin); } mForeground.setBounds(mFinalDrawableBounds); mBackground.setBounds(mFinalDrawableBounds); - if (isFolderIcon) { - mStartRevealRect.set(0, 0, lp.width, mOriginalHeight); - } else { - mStartRevealRect.set(mBlurSizeOutline, mBlurSizeOutline, - lp.width - mBlurSizeOutline, mOriginalHeight - mBlurSizeOutline); + mStartRevealRect.set(0, 0, originalWidth, originalHeight); + + if (!isFolderIcon) { + mStartRevealRect.inset(mBlurSizeOutline, mBlurSizeOutline); } - if (aspectRatio > 0) { + float aspectRatio = launcher.getDeviceProfile().aspectRatio; + if (launcher.getDeviceProfile().isVerticalBarLayout()) { + lp.width = (int) Math.max(lp.width, lp.height * aspectRatio); + } else { lp.height = (int) Math.max(lp.height, lp.width * aspectRatio); - layout(lp.leftMargin, lp.topMargin, lp.leftMargin + lp.width, lp.topMargin - + lp.height); } - mBgDrawableStartScale = (float) lp.height / mOriginalHeight; + layout(lp.leftMargin, lp.topMargin, lp.leftMargin + lp.width, lp.topMargin + + lp.height); + + Rect rectOutline = new Rect(); + float scale = Math.max((float) lp.height / originalHeight, + (float) lp.width / originalWidth); + if (isOpening) { + mBgDrawableStartScale = 1f; + mBgDrawableEndScale = scale; + rectOutline.set(0, 0, originalWidth, originalHeight); + } else { + mBgDrawableStartScale = scale; + mBgDrawableEndScale = 1f; + rectOutline.set(0, 0, lp.width, lp.height); + } + mEndRevealRect.set(0, 0, lp.width, lp.height); setBackgroundDrawableBounds(mBgDrawableStartScale); - - // Set up outline - mOutline.set(0, 0, lp.width, lp.height); setOutlineProvider(new ViewOutlineProvider() { @Override public void getOutline(View view, Outline outline) { - outline.setRoundRect(mOutline, mTaskCornerRadius); + outline.setRoundRect(rectOutline, mTaskCornerRadius); } }); setClipToOutline(true); @@ -283,6 +290,7 @@ public class FloatingIconView extends View implements Animator.AnimatorListener, setBackground(finalDrawable); } + onIconLoadedRunnable.run(); invalidate(); invalidateOutline(); }); @@ -350,6 +358,9 @@ public class FloatingIconView extends View implements Animator.AnimatorListener, } @Override + public void onAnimationStart(Animator animator) {} + + @Override public void onAnimationCancel(Animator animator) {} @Override @@ -357,17 +368,16 @@ public class FloatingIconView extends View implements Animator.AnimatorListener, /** * Creates a floating icon view for {@param originalView}. - * * @param originalView The view to copy * @param hideOriginal If true, it will hide {@param originalView} while this view is visible. - * @param useDrawableAsIs If true, we do not separate the foreground/background of adaptive - * icons. TODO(b/122843905): We can remove this once app opening uses new animation. - * @param aspectRatio If >= 0, we will use this aspect ratio for the initial adaptive icon size. * @param positionOut Rect that will hold the size and position of v. + * @param isOpening True if this view replaces the icon for app open animation. */ public static FloatingIconView getFloatingIconView(Launcher launcher, View originalView, - boolean hideOriginal, boolean useDrawableAsIs, float aspectRatio, Rect positionOut, - FloatingIconView recycle) { + boolean hideOriginal, Rect positionOut, boolean isOpening, FloatingIconView recycle) { + if (recycle != null) { + recycle.recycle(); + } FloatingIconView view = recycle != null ? recycle : new FloatingIconView(launcher); // Match the position of the original view. @@ -376,9 +386,16 @@ public class FloatingIconView extends View implements Animator.AnimatorListener, // Get the drawable on the background thread // Must be called after matchPositionOf so that we know what size to load. if (originalView.getTag() instanceof ItemInfo) { + Runnable onIconLoaded = () -> { + // Delay swapping views until the icon is loaded to prevent a flash. + view.setVisibility(VISIBLE); + if (hideOriginal) { + originalView.setVisibility(INVISIBLE); + } + }; new Handler(LauncherModel.getWorkerLooper()).postAtFrontOfQueue(() -> { - view.getIcon(launcher, originalView, (ItemInfo) originalView.getTag(), - useDrawableAsIs, aspectRatio); + view.getIcon(launcher, originalView, (ItemInfo) originalView.getTag(), isOpening, + onIconLoaded); }); } @@ -387,12 +404,6 @@ public class FloatingIconView extends View implements Animator.AnimatorListener, view.setVisibility(INVISIBLE); ((ViewGroup) dragLayer.getParent()).getOverlay().add(view); - view.mStartRunnable = () -> { - view.setVisibility(VISIBLE); - if (hideOriginal) { - originalView.setVisibility(INVISIBLE); - } - }; if (hideOriginal) { view.mEndRunnable = () -> { AnimatorSet fade = new AnimatorSet(); @@ -442,4 +453,24 @@ public class FloatingIconView extends View implements Animator.AnimatorListener, } return view; } + + private void recycle() { + setTranslationX(0); + setTranslationY(0); + setScaleX(1); + setScaleY(1); + setAlpha(1); + setBackground(null); + mEndRunnable = null; + mIsAdaptiveIcon = false; + mForeground = null; + mBackground = null; + mClipPath = null; + mFinalDrawableBounds.setEmpty(); + mBgDrawableBounds.setEmpty();; + if (mRevealAnimator != null) { + mRevealAnimator.cancel(); + } + mRevealAnimator = null; + } } |