Merge "Fix icon shape refresh for widgets list / legacy shortcut black bg issue" into ub-launcher3-master
diff --git a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
index 051c80f..d1d697c 100644
--- a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -173,8 +173,10 @@
// 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 @@
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 447e7e7..f2ca368 100644
--- a/go/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/go/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -67,9 +67,7 @@
@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/fallback/GoRecentsActivityRootView.java b/go/quickstep/src/com/android/quickstep/fallback/GoRecentsActivityRootView.java
index d748e89..c0ebcb5 100644
--- a/go/quickstep/src/com/android/quickstep/fallback/GoRecentsActivityRootView.java
+++ b/go/quickstep/src/com/android/quickstep/fallback/GoRecentsActivityRootView.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.util.AttributeSet;
+import com.android.launcher3.util.TouchController;
import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.RecentsActivity;
@@ -27,5 +28,7 @@
public final class GoRecentsActivityRootView extends BaseDragLayer<RecentsActivity> {
public GoRecentsActivityRootView(Context context, AttributeSet attrs) {
super(context, attrs, 1 /* alphaChannelCount */);
+ // Go leaves touch control to the view itself.
+ mControllers = new TouchController[0];
}
}
diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
index 1e01725..5bb4c5a 100644
--- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
+++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
@@ -89,6 +89,7 @@
private RecyclerView mTaskRecyclerView;
private View mEmptyView;
private View mContentView;
+ private View mClearAllView;
private boolean mTransitionedFromApp;
public IconRecentsView(Context context, AttributeSet attrs) {
@@ -125,12 +126,22 @@
updateContentViewVisibility();
}
});
-
- View clearAllView = findViewById(R.id.clear_all_button);
- clearAllView.setOnClickListener(v -> animateClearAllTasks());
+ mClearAllView = findViewById(R.id.clear_all_button);
+ mClearAllView.setOnClickListener(v -> animateClearAllTasks());
}
}
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+ TaskItemView[] itemViews = getTaskViews();
+ for (TaskItemView itemView : itemViews) {
+ itemView.setEnabled(enabled);
+ }
+ mClearAllView.setEnabled(enabled);
+ }
+
/**
* Set activity helper for the view to callback to.
*
@@ -144,8 +155,6 @@
* 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
@@ -206,6 +215,7 @@
* Clear all tasks and animate out.
*/
private void animateClearAllTasks() {
+ setEnabled(false);
TaskItemView[] itemViews = getTaskViews();
AnimatorSet clearAnim = new AnimatorSet();
@@ -251,6 +261,7 @@
itemView.setTranslationX(0);
itemView.setAlpha(1.0f);
}
+ setEnabled(true);
mContentView.setVisibility(GONE);
mTaskActionController.clearAllTasks();
}
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 36014a9..f507d0f 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.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 @@
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 0000000..673beff
--- /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/FallbackActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java
index ef46b3b..73fcf78 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java
@@ -16,6 +16,7 @@
package com.android.quickstep;
import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
import android.animation.Animator;
@@ -59,7 +60,8 @@
@Override
public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect) {
LayoutUtils.calculateFallbackTaskSize(context, dp, outRect);
- if (dp.isVerticalBarLayout()) {
+ if (dp.isVerticalBarLayout()
+ && SysUINavigationMode.INSTANCE.get(context).getMode() != NO_BUTTON) {
Rect targetInsets = dp.getInsets();
int hotseatInset = dp.isSeascape() ? targetInsets.left : targetInsets.right;
return dp.hotseatBarSizePx + hotseatInset;
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 9763063..df2b687 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
@@ -24,6 +24,7 @@
import static com.android.launcher3.allapps.AllAppsTransitionController.SPRING_STIFFNESS;
import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -74,7 +75,8 @@
@Override
public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect) {
LayoutUtils.calculateLauncherTaskSize(context, dp, outRect);
- if (dp.isVerticalBarLayout()) {
+ if (dp.isVerticalBarLayout()
+ && SysUINavigationMode.INSTANCE.get(context).getMode() != NO_BUTTON) {
Rect targetInsets = dp.getInsets();
int hotseatInset = dp.isSeascape() ? targetInsets.left : targetInsets.right;
return dp.hotseatBarSizePx + hotseatInset;
@@ -113,8 +115,7 @@
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/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java
index c8dcf80..63c2e5d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java
@@ -24,6 +24,7 @@
import static com.android.launcher3.util.RaceConditionTracker.ENTER;
import static com.android.launcher3.util.RaceConditionTracker.EXIT;
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
+import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
@@ -284,11 +285,13 @@
}
private boolean isNavBarOnRight() {
- return mDisplayRotation == Surface.ROTATION_90 && mStableInsets.right > 0;
+ return SysUINavigationMode.INSTANCE.get(getBaseContext()).getMode() != NO_BUTTON
+ && mDisplayRotation == Surface.ROTATION_90 && mStableInsets.right > 0;
}
private boolean isNavBarOnLeft() {
- return mDisplayRotation == Surface.ROTATION_270 && mStableInsets.left > 0;
+ return SysUINavigationMode.INSTANCE.get(getBaseContext()).getMode() != NO_BUTTON
+ && mDisplayRotation == Surface.ROTATION_270 && mStableInsets.left > 0;
}
private void startTouchTrackingForWindowAnimation(long touchTimeMs) {
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 15072a2..0065cb5 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.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 @@
// 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 @@
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 c5475d6..f77bd65 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_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.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.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 @@
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 @@
// 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 @@
}
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 @@
}
});
}
- anim.play(getOpeningWindowAnimators(v, targets, windowTargetBounds));
}
/**
@@ -398,124 +403,13 @@
float[] alphas, float[] trans);
/**
- * Animators for the "floating view" of the view used to launch the target.
- */
- 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();
- 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);
-
- // 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);
-
- // 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 scale = Math.max(maxScaleX, maxScaleY);
- float startScale = 1f;
- if (isBubbleTextView && !(v.getParent() instanceof DeepShortcutView)) {
- Drawable dr = ((BubbleTextView) v).getIcon();
- if (dr instanceof FastBitmapDrawable) {
- startScale = ((FastBitmapDrawable) dr).getAnimatedScale();
- }
- }
-
- 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);
-
- appOpenAnimator.addListener(mFloatingView);
- appOpenAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- // Reset launcher to normal state
- if (isBubbleTextView) {
- ((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 windowTargetBounds, boolean toggleVisibility) {
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];
-
+ mFloatingView = FloatingIconView.getFloatingIconView(mLauncher, v, toggleVisibility,
+ bounds, true /* isOpening */, mFloatingView);
Rect crop = new Rect();
Matrix matrix = new Matrix();
@@ -526,37 +420,99 @@
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) 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 (v instanceof BubbleTextView && !(v.getParent() instanceof DeepShortcutView)) {
+ Drawable dr = ((BubbleTextView) v).getIcon();
+ if (dr instanceof FastBitmapDrawable) {
+ startScale = ((FastBitmapDrawable) dr).getAnimatedScale();
+ }
+ }
+ final float initialStartScale = startScale;
+
+ 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;
+
+ 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) {
+ if (v instanceof BubbleTextView) {
+ ((BubbleTextView) v).setStayPressed(false);
+ }
+ }
+ });
+
+ 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 @@
.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 @@
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/quickstep/src/com/android/quickstep/SysUINavigationMode.java b/quickstep/src/com/android/quickstep/SysUINavigationMode.java
index 3773143..1953ecb 100644
--- a/quickstep/src/com/android/quickstep/SysUINavigationMode.java
+++ b/quickstep/src/com/android/quickstep/SysUINavigationMode.java
@@ -35,14 +35,16 @@
public class SysUINavigationMode {
public enum Mode {
- THREE_BUTTONS(false),
- TWO_BUTTONS(true),
- NO_BUTTON(true);
+ THREE_BUTTONS(false, 0),
+ TWO_BUTTONS(true, 1),
+ NO_BUTTON(true, 2);
public final boolean hasGestures;
+ public final int resValue;
- Mode(boolean hasGestures) {
+ Mode(boolean hasGestures, int resValue) {
this.hasGestures = hasGestures;
+ this.resValue = resValue;
}
}
@@ -80,13 +82,10 @@
private void initializeMode() {
int modeInt = getSystemIntegerRes(mContext, NAV_BAR_INTERACTION_MODE_RES_NAME);
-
- if (QuickStepContract.isGesturalMode(modeInt)) {
- mMode = Mode.NO_BUTTON;
- } else if (QuickStepContract.isSwipeUpMode(modeInt)) {
- mMode = Mode.TWO_BUTTONS;
- } else {
- mMode = Mode.THREE_BUTTONS;
+ for(Mode m : Mode.values()) {
+ if (m.resValue == modeInt) {
+ mMode = m;
+ }
}
}
diff --git a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
index 8633b21..f5ac9c9 100644
--- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
+++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
@@ -17,7 +17,6 @@
package com.android.quickstep;
import static androidx.test.InstrumentationRegistry.getInstrumentation;
-
import static com.android.quickstep.NavigationModeSwitchRule.Mode.ALL;
import static com.android.quickstep.NavigationModeSwitchRule.Mode.THREE_BUTTON;
import static com.android.quickstep.NavigationModeSwitchRule.Mode.TWO_BUTTON;
@@ -27,22 +26,17 @@
import android.content.Context;
import android.util.Log;
-
import androidx.test.uiautomator.UiDevice;
-
import com.android.launcher3.tapl.LauncherInstrumentation;
import com.android.launcher3.tapl.TestHelpers;
-import com.android.systemui.shared.system.QuickStepContract;
-
-import org.junit.Assert;
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import org.junit.Assert;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
/**
* Test rule that allows executing a test with Quickstep on and then Quickstep off.
@@ -78,9 +72,9 @@
@Override
public void evaluate() throws Throwable {
final Context context = getInstrumentation().getContext();
- final String prevOverlayPkg = QuickStepContract.isGesturalMode(context)
+ final String prevOverlayPkg = LauncherInstrumentation.isGesturalMode(context)
? NAV_BAR_MODE_GESTURAL_OVERLAY
- : QuickStepContract.isSwipeUpMode(context)
+ : LauncherInstrumentation.isSwipeUpMode(context)
? NAV_BAR_MODE_2BUTTON_OVERLAY
: NAV_BAR_MODE_3BUTTON_OVERLAY;
final LauncherInstrumentation.NavigationModel originalMode =
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 6397e14..6a3a26f 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -51,6 +51,9 @@
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 @@
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 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/TestProtocol.java b/src/com/android/launcher3/TestProtocol.java
index 49a736e..7d3715e 100644
--- a/src/com/android/launcher3/TestProtocol.java
+++ b/src/com/android/launcher3/TestProtocol.java
@@ -33,6 +33,27 @@
public static final int ALL_APPS_STATE_ORDINAL = 5;
public static final int BACKGROUND_APP_STATE_ORDINAL = 6;
+ public static String stateOrdinalToString(int ordinal) {
+ switch (ordinal) {
+ case NORMAL_STATE_ORDINAL:
+ return "Normal";
+ case SPRING_LOADED_STATE_ORDINAL:
+ return "SpringLoaded";
+ case OVERVIEW_STATE_ORDINAL:
+ return "Overview";
+ case OVERVIEW_PEEK_STATE_ORDINAL:
+ return "OverviewPeek";
+ case QUICK_SWITCH_STATE_ORDINAL:
+ return "QuickSwitch";
+ case ALL_APPS_STATE_ORDINAL:
+ return "AllApps";
+ case BACKGROUND_APP_STATE_ORDINAL:
+ return "Background";
+ default:
+ return null;
+ }
+ }
+
public static final String TEST_INFO_RESPONSE_FIELD = "response";
public static final String REQUEST_HOME_TO_OVERVIEW_SWIPE_HEIGHT =
"home-to-overview-swipe-height";
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index c403e76..0274de3 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -224,7 +224,8 @@
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 @@
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 49ec292..5889468 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.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 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 @@
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 @@
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 @@
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 @@
}
@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 @@
}
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 @@
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);
+ mStartRevealRect.set(0, 0, originalWidth, originalHeight);
+
+ if (!isFolderIcon) {
+ mStartRevealRect.inset(mBlurSizeOutline, mBlurSizeOutline);
+ }
+
+ float aspectRatio = launcher.getDeviceProfile().aspectRatio;
+ if (launcher.getDeviceProfile().isVerticalBarLayout()) {
+ lp.width = (int) Math.max(lp.width, lp.height * aspectRatio);
} else {
- mStartRevealRect.set(mBlurSizeOutline, mBlurSizeOutline,
- lp.width - mBlurSizeOutline, mOriginalHeight - mBlurSizeOutline);
- }
-
- if (aspectRatio > 0) {
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;
- setBackgroundDrawableBounds(mBgDrawableStartScale);
+ layout(lp.leftMargin, lp.topMargin, lp.leftMargin + lp.width, lp.topMargin
+ + lp.height);
- // Set up outline
- mOutline.set(0, 0, lp.width, 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);
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 @@
setBackground(finalDrawable);
}
+ onIconLoadedRunnable.run();
invalidate();
invalidateOutline();
});
@@ -350,6 +358,9 @@
}
@Override
+ public void onAnimationStart(Animator animator) {}
+
+ @Override
public void onAnimationCancel(Animator animator) {}
@Override
@@ -357,17 +368,16 @@
/**
* 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 @@
// 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 @@
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 @@
}
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;
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 00257a5..a4b4171 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -16,27 +16,26 @@
package com.android.launcher3.tapl;
-import static com.android.launcher3.TestProtocol.BACKGROUND_APP_STATE_ORDINAL;
-
import android.app.ActivityManager;
import android.app.Instrumentation;
import android.app.UiAutomation;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.content.res.Resources;
import android.graphics.Point;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcelable;
import android.os.SystemClock;
+import android.text.TextUtils;
import android.util.Log;
import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.ViewConfiguration;
import android.view.accessibility.AccessibilityEvent;
-
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.test.uiautomator.By;
@@ -45,18 +44,15 @@
import androidx.test.uiautomator.UiDevice;
import androidx.test.uiautomator.UiObject2;
import androidx.test.uiautomator.Until;
-
import com.android.launcher3.TestProtocol;
import com.android.systemui.shared.system.QuickStepContract;
-
-import org.junit.Assert;
-
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeoutException;
+import org.junit.Assert;
/**
* The main tapl object. The only object that can be explicitly constructed by the using code. It
@@ -65,6 +61,8 @@
public final class LauncherInstrumentation {
private static final String TAG = "Tapl";
+ private static final String NAV_BAR_INTERACTION_MODE_RES_NAME =
+ "config_navBarInteractionMode";
private static final int ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME = 20;
// Types for launcher containers that the user is interacting with. "Background" is a
@@ -171,11 +169,11 @@
// Workaround, use constructed context because both the instrumentation context and the
// app context are not constructed with resources that take overlays into account
final Context ctx = baseContext.createPackageContext("android", 0);
- if (QuickStepContract.isGesturalMode(ctx)) {
+ if (isGesturalMode(ctx)) {
return NavigationModel.ZERO_BUTTON;
- } else if (QuickStepContract.isSwipeUpMode(ctx)) {
+ } else if (isSwipeUpMode(ctx)) {
return NavigationModel.TWO_BUTTON;
- } else if (QuickStepContract.isLegacyMode(ctx)) {
+ } else if (isLegacyMode(ctx)) {
return NavigationModel.THREE_BUTTON;
} else {
fail("Can't detect navigation mode");
@@ -225,6 +223,12 @@
}
}
+ private void assertEquals(String message, String expected, String actual) {
+ if (!TextUtils.equals(expected, actual)) {
+ fail(message + " expected: '" + expected + "' but was: '" + actual + "'");
+ }
+ }
+
void assertNotEquals(String message, int unexpected, int actual) {
if (unexpected == actual) {
failEquals(message, actual);
@@ -539,8 +543,9 @@
event -> TestProtocol.SWITCHED_TO_STATE_MESSAGE.equals(event.getClassName()),
"Swipe failed to receive an event for the swipe end: " + startX + ", " + startY
+ ", " + endX + ", " + endY);
- assertEquals("Swipe switched launcher to a wrong state",
- expectedState, parcel.getInt(TestProtocol.STATE_FIELD));
+ assertEquals("Swipe switched launcher to a wrong state;",
+ TestProtocol.stateOrdinalToString(expectedState),
+ TestProtocol.stateOrdinalToString(parcel.getInt(TestProtocol.STATE_FIELD)));
}
void waitForIdle() {
@@ -595,6 +600,33 @@
}
}
+ public static boolean isGesturalMode(Context context) {
+ return QuickStepContract.isGesturalMode(
+ getSystemIntegerRes(context, NAV_BAR_INTERACTION_MODE_RES_NAME));
+ }
+
+ public static boolean isSwipeUpMode(Context context) {
+ return QuickStepContract.isSwipeUpMode(
+ getSystemIntegerRes(context, NAV_BAR_INTERACTION_MODE_RES_NAME));
+ }
+
+ public static boolean isLegacyMode(Context context) {
+ return QuickStepContract.isLegacyMode(
+ getSystemIntegerRes(context, NAV_BAR_INTERACTION_MODE_RES_NAME));
+ }
+
+ private static int getSystemIntegerRes(Context context, String resName) {
+ Resources res = context.getResources();
+ int resId = res.getIdentifier(resName, "integer", "android");
+
+ if (resId != 0) {
+ return res.getInteger(resId);
+ } else {
+ Log.e(TAG, "Failed to get system resource ID. Incompatible framework version?");
+ return -1;
+ }
+ }
+
static void sleep(int duration) {
try {
Thread.sleep(duration);