summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java6
-rw-r--r--go/quickstep/src/com/android/quickstep/RecentsActivity.java4
-rw-r--r--go/quickstep/src/com/android/quickstep/views/IconRecentsView.java2
-rw-r--r--quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java2
-rw-r--r--quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java141
-rw-r--r--quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java3
-rw-r--r--quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java5
-rw-r--r--quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java241
-rw-r--r--src/com/android/launcher3/DeviceProfile.java11
-rw-r--r--src/com/android/launcher3/touch/AbstractStateChangeTouchController.java5
-rw-r--r--src/com/android/launcher3/views/FloatingIconView.java167
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;
+ }
}