Merge "Merging from ub-launcher3-master @ build 6304996" into rvc-dev am: c83758f223

Change-Id: I5a6d4f5198e8aace4ad3a6c27b8d5be5893f7755
This commit is contained in:
Zak Cohen
2020-03-18 18:11:14 +00:00
committed by Automerger Merge Worker
59 changed files with 1904 additions and 796 deletions
@@ -20,15 +20,14 @@ import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.HOTSEAT_ICONS; import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherStateManager.ANIM_ALL_COMPONENTS;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_SCALE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_TRANSLATE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS;
import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE; import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
import static com.android.launcher3.anim.Interpolators.DEACCEL_3; import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2; import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_SCALE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_TRANSLATE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch; import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch;
import static com.android.quickstep.TaskViewUtils.getRecentsWindowAnimator; import static com.android.quickstep.TaskViewUtils.getRecentsWindowAnimator;
@@ -47,9 +46,9 @@ import androidx.annotation.Nullable;
import com.android.launcher3.LauncherState.ScaleAndTranslation; import com.android.launcher3.LauncherState.ScaleAndTranslation;
import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.SpringAnimationBuilder; import com.android.launcher3.anim.SpringAnimationBuilder;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.touch.PagedOrientationHandler; import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.quickstep.util.AppWindowAnimationHelper; import com.android.quickstep.util.AppWindowAnimationHelper;
import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.RecentsView;
@@ -210,17 +209,20 @@ public final class LauncherAppTransitionManagerImpl extends QuickstepAppTransiti
.setValues(values) .setValues(values)
.build(mLauncher); .build(mLauncher);
case INDEX_PAUSE_TO_OVERVIEW_ANIM: { case INDEX_PAUSE_TO_OVERVIEW_ANIM: {
AnimatorSetBuilder builder = new AnimatorSetBuilder(); StateAnimationConfig config = new StateAnimationConfig();
builder.setInterpolator(ANIM_VERTICAL_PROGRESS, OVERSHOOT_1_2); config.duration = ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW;
builder.setInterpolator(ANIM_ALL_APPS_FADE, DEACCEL_3);
config.setInterpolator(ANIM_VERTICAL_PROGRESS, OVERSHOOT_1_2);
config.setInterpolator(ANIM_ALL_APPS_FADE, DEACCEL_3);
if ((OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) { if ((OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) {
builder.setInterpolator(ANIM_HOTSEAT_SCALE, OVERSHOOT_1_2); config.setInterpolator(ANIM_HOTSEAT_SCALE, OVERSHOOT_1_2);
builder.setInterpolator(ANIM_HOTSEAT_TRANSLATE, OVERSHOOT_1_2); config.setInterpolator(ANIM_HOTSEAT_TRANSLATE, OVERSHOOT_1_2);
} }
LauncherStateManager stateManager = mLauncher.getStateManager(); LauncherStateManager stateManager = mLauncher.getStateManager();
return stateManager.createAtomicAnimation( return stateManager.createAtomicAnimation(
stateManager.getCurrentStableState(), OVERVIEW, builder, stateManager.getCurrentStableState(), OVERVIEW, config);
ANIM_ALL_COMPONENTS, ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW);
} }
default: default:
@@ -20,7 +20,6 @@ import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA; import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS; import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
import android.animation.ValueAnimator;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.os.Build; import android.os.Build;
import android.util.FloatProperty; import android.util.FloatProperty;
@@ -29,10 +28,10 @@ import androidx.annotation.NonNull;
import com.android.launcher3.Launcher; import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.PropertySetter; import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.quickstep.views.ClearAllButton; import com.android.quickstep.views.ClearAllButton;
import com.android.quickstep.views.LauncherRecentsView; import com.android.quickstep.views.LauncherRecentsView;
import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.RecentsView;
@@ -61,25 +60,22 @@ public final class RecentsViewStateController extends
} }
@Override @Override
void setStateWithAnimationInternal(@NonNull final LauncherState toState, void setStateWithAnimationInternal(@NonNull LauncherState toState,
@NonNull AnimatorSetBuilder builder, @NonNull AnimationConfig config) { @NonNull StateAnimationConfig config, @NonNull PendingAnimation builder) {
super.setStateWithAnimationInternal(toState, builder, config); super.setStateWithAnimationInternal(toState, config, builder);
ValueAnimator updateAnim = ValueAnimator.ofFloat(0, 1).setDuration(config.duration);
if (toState.overviewUi) { if (toState.overviewUi) {
// While animating into recents, update the visible task data as needed // While animating into recents, update the visible task data as needed
updateAnim.addUpdateListener(valueAnimator -> mRecentsView.loadVisibleTaskData()); builder.addOnFrameCallback(mRecentsView::loadVisibleTaskData);
mRecentsView.updateEmptyMessage(); mRecentsView.updateEmptyMessage();
} else { } else {
updateAnim.addListener( builder.getAnim().addListener(
AnimationSuccessListener.forRunnable(mRecentsView::resetTaskVisuals)); AnimationSuccessListener.forRunnable(mRecentsView::resetTaskVisuals));
} }
builder.play(updateAnim);
PropertySetter propertySetter = config.getPropertySetter(builder); setAlphas(builder, toState.getVisibleElements(mLauncher));
setAlphas(propertySetter, toState.getVisibleElements(mLauncher)); builder.setFloat(mRecentsView, FULLSCREEN_PROGRESS,
float fullscreenProgress = toState.getOverviewFullscreenProgress(); toState.getOverviewFullscreenProgress(), LINEAR);
propertySetter.setFloat(mRecentsView, FULLSCREEN_PROGRESS, fullscreenProgress, LINEAR);
} }
private void setAlphas(PropertySetter propertySetter, int visibleElements) { private void setAlphas(PropertySetter propertySetter, int visibleElements) {
@@ -14,17 +14,18 @@
* limitations under the License. * limitations under the License.
*/ */
package com.android.launcher3.uioverrides.states; package com.android.launcher3.uioverrides.states;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCRIM_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_X;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.INSTANT; import static com.android.launcher3.anim.Interpolators.INSTANT;
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_7; import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_7;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCRIM_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
import com.android.launcher3.Launcher; import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherState;
import com.android.launcher3.R; import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.states.StateAnimationConfig;
public class OverviewPeekState extends OverviewState { public class OverviewPeekState extends OverviewState {
public OverviewPeekState(int id) { public OverviewPeekState(int id) {
@@ -41,11 +42,11 @@ public class OverviewPeekState extends OverviewState {
@Override @Override
public void prepareForAtomicAnimation(Launcher launcher, LauncherState fromState, public void prepareForAtomicAnimation(Launcher launcher, LauncherState fromState,
AnimatorSetBuilder builder) { StateAnimationConfig config) {
if (this == OVERVIEW_PEEK && fromState == NORMAL) { if (this == OVERVIEW_PEEK && fromState == NORMAL) {
builder.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT); config.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT);
builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_7); config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_7);
builder.setInterpolator(ANIM_OVERVIEW_SCRIM_FADE, FAST_OUT_SLOW_IN); config.setInterpolator(ANIM_OVERVIEW_SCRIM_FADE, FAST_OUT_SLOW_IN);
} }
} }
} }
@@ -18,13 +18,6 @@ package com.android.launcher3.uioverrides.states;
import static android.view.View.VISIBLE; import static android.view.View.VISIBLE;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_X;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_Y;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE;
import static com.android.launcher3.anim.Interpolators.ACCEL; import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL_2; import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2; import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
@@ -32,6 +25,13 @@ import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_7;
import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS; import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget; import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE; import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCALE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON; import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview; import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
@@ -48,8 +48,8 @@ import com.android.launcher3.LauncherState;
import com.android.launcher3.R; import com.android.launcher3.R;
import com.android.launcher3.Workspace; import com.android.launcher3.Workspace;
import com.android.launcher3.allapps.DiscoveryBounce; import com.android.launcher3.allapps.DiscoveryBounce;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.compat.AccessibilityManagerCompat; import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action; import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.quickstep.SysUINavigationMode; import com.android.quickstep.SysUINavigationMode;
@@ -225,14 +225,14 @@ public class OverviewState extends LauncherState {
@Override @Override
public void prepareForAtomicAnimation(Launcher launcher, LauncherState fromState, public void prepareForAtomicAnimation(Launcher launcher, LauncherState fromState,
AnimatorSetBuilder builder) { StateAnimationConfig config) {
if ((fromState == NORMAL || fromState == HINT_STATE) && this == OVERVIEW) { if ((fromState == NORMAL || fromState == HINT_STATE) && this == OVERVIEW) {
if (SysUINavigationMode.getMode(launcher) == NO_BUTTON) { if (SysUINavigationMode.getMode(launcher) == NO_BUTTON) {
builder.setInterpolator(ANIM_WORKSPACE_SCALE, config.setInterpolator(ANIM_WORKSPACE_SCALE,
fromState == NORMAL ? ACCEL : OVERSHOOT_1_2); fromState == NORMAL ? ACCEL : OVERSHOOT_1_2);
builder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL); config.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL);
} else { } else {
builder.setInterpolator(ANIM_WORKSPACE_SCALE, OVERSHOOT_1_2); config.setInterpolator(ANIM_WORKSPACE_SCALE, OVERSHOOT_1_2);
// Scale up the recents, if it is not coming from the side // Scale up the recents, if it is not coming from the side
RecentsView overview = launcher.getOverviewPanel(); RecentsView overview = launcher.getOverviewPanel();
@@ -240,15 +240,15 @@ public class OverviewState extends LauncherState {
SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE); SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE);
} }
} }
builder.setInterpolator(ANIM_WORKSPACE_FADE, OVERSHOOT_1_2); config.setInterpolator(ANIM_WORKSPACE_FADE, OVERSHOOT_1_2);
builder.setInterpolator(ANIM_OVERVIEW_SCALE, OVERSHOOT_1_2); config.setInterpolator(ANIM_OVERVIEW_SCALE, OVERSHOOT_1_2);
Interpolator translationInterpolator = ENABLE_OVERVIEW_ACTIONS.get() Interpolator translationInterpolator = ENABLE_OVERVIEW_ACTIONS.get()
&& removeShelfFromOverview(launcher) && removeShelfFromOverview(launcher)
? OVERSHOOT_1_2 ? OVERSHOOT_1_2
: OVERSHOOT_1_7; : OVERSHOOT_1_7;
builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, translationInterpolator); config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, translationInterpolator);
builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, translationInterpolator); config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, translationInterpolator);
builder.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2); config.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2);
} }
} }
@@ -21,16 +21,16 @@ import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherState.OVERVIEW_PEEK; import static com.android.launcher3.LauncherState.OVERVIEW_PEEK;
import static com.android.launcher3.LauncherStateManager.PLAY_ATOMIC_OVERVIEW_PEEK;
import static com.android.launcher3.LauncherStateManager.SKIP_OVERVIEW;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_HEADER_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE;
import static com.android.launcher3.anim.Interpolators.ACCEL; import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL; import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL_3; import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_HEADER_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCALE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_PEEK;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC; import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
@@ -44,10 +44,10 @@ import android.view.ViewConfiguration;
import com.android.launcher3.Launcher; import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppTransitionManagerImpl; import com.android.launcher3.LauncherAppTransitionManagerImpl;
import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager.AnimationFlags;
import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.util.VibratorWrapper; import com.android.launcher3.util.VibratorWrapper;
import com.android.quickstep.SystemUiProxy; import com.android.quickstep.SystemUiProxy;
@@ -105,8 +105,12 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController {
LauncherState fromState = isPaused ? NORMAL : OVERVIEW_PEEK; LauncherState fromState = isPaused ? NORMAL : OVERVIEW_PEEK;
LauncherState toState = isPaused ? OVERVIEW_PEEK : NORMAL; LauncherState toState = isPaused ? OVERVIEW_PEEK : NORMAL;
long peekDuration = isPaused ? PEEK_IN_ANIM_DURATION : PEEK_OUT_ANIM_DURATION; long peekDuration = isPaused ? PEEK_IN_ANIM_DURATION : PEEK_OUT_ANIM_DURATION;
mPeekAnim = mLauncher.getStateManager().createAtomicAnimation(fromState, toState,
new AnimatorSetBuilder(), PLAY_ATOMIC_OVERVIEW_PEEK, peekDuration); StateAnimationConfig config = new StateAnimationConfig();
config.duration = peekDuration;
config.animFlags = PLAY_ATOMIC_OVERVIEW_PEEK;
mPeekAnim = mLauncher.getStateManager().createAtomicAnimation(
fromState, toState, config);
mPeekAnim.addListener(new AnimatorListenerAdapter() { mPeekAnim.addListener(new AnimatorListenerAdapter() {
@Override @Override
public void onAnimationEnd(Animator animation) { public void onAnimationEnd(Animator animation) {
@@ -130,10 +134,10 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController {
} }
@Override @Override
protected AnimatorSetBuilder getAnimatorSetBuilderForStates(LauncherState fromState, protected StateAnimationConfig getConfigForStates(
LauncherState toState) { LauncherState fromState, LauncherState toState) {
if (fromState == NORMAL && toState == ALL_APPS) { if (fromState == NORMAL && toState == ALL_APPS) {
AnimatorSetBuilder builder = new AnimatorSetBuilder(); StateAnimationConfig builder = new StateAnimationConfig();
// Fade in prediction icons quickly, then rest of all apps after reaching overview. // Fade in prediction icons quickly, then rest of all apps after reaching overview.
float progressToReachOverview = NORMAL.getVerticalProgress(mLauncher) float progressToReachOverview = NORMAL.getVerticalProgress(mLauncher)
- OVERVIEW.getVerticalProgress(mLauncher); - OVERVIEW.getVerticalProgress(mLauncher);
@@ -152,7 +156,7 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController {
builder.setInterpolator(ANIM_WORKSPACE_FADE, DEACCEL_3); builder.setInterpolator(ANIM_WORKSPACE_FADE, DEACCEL_3);
return builder; return builder;
} else if (fromState == ALL_APPS && toState == NORMAL) { } else if (fromState == ALL_APPS && toState == NORMAL) {
AnimatorSetBuilder builder = new AnimatorSetBuilder(); StateAnimationConfig builder = new StateAnimationConfig();
// Keep all apps/predictions opaque until the very end of the transition. // Keep all apps/predictions opaque until the very end of the transition.
float progressToReachOverview = OVERVIEW.getVerticalProgress(mLauncher); float progressToReachOverview = OVERVIEW.getVerticalProgress(mLauncher);
builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress( builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(
@@ -165,7 +169,7 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController {
1)); 1));
return builder; return builder;
} }
return super.getAnimatorSetBuilderForStates(fromState, toState); return super.getConfigForStates(fromState, toState);
} }
@Override @Override
@@ -211,9 +215,11 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController {
public void onAnimationEnd(Animator animation) { public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation); super.onAnimationEnd(animation);
if (mCancelled) { if (mCancelled) {
mPeekAnim = mLauncher.getStateManager().createAtomicAnimation(mFromState, StateAnimationConfig config = new StateAnimationConfig();
mToState, new AnimatorSetBuilder(), PLAY_ATOMIC_OVERVIEW_PEEK, config.animFlags = PLAY_ATOMIC_OVERVIEW_PEEK;
PEEK_OUT_ANIM_DURATION); config.duration = PEEK_OUT_ANIM_DURATION;
mPeekAnim = mLauncher.getStateManager().createAtomicAnimation(
mFromState, mToState, config);
mPeekAnim.start(); mPeekAnim.start();
} }
mAtomicAnim = null; mAtomicAnim = null;
@@ -15,8 +15,7 @@
*/ */
package com.android.launcher3.uioverrides.touchcontrollers; package com.android.launcher3.uioverrides.touchcontrollers;
import static android.view.View.TRANSLATION_X; import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.LauncherState.OVERVIEW;
@@ -26,9 +25,6 @@ import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TI
import static com.android.launcher3.touch.AbstractStateChangeTouchController.SUCCESS_TRANSITION_PROGRESS; import static com.android.launcher3.touch.AbstractStateChangeTouchController.SUCCESS_TRANSITION_PROGRESS;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator; import android.animation.ValueAnimator;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.animation.Interpolator; import android.view.animation.Interpolator;
@@ -36,16 +32,16 @@ import android.view.animation.Interpolator;
import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Launcher; import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.R; import com.android.launcher3.R;
import com.android.launcher3.Utilities; import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.compat.AccessibilityManagerCompat; import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.touch.SingleAxisSwipeDetector; import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
@@ -132,46 +128,37 @@ public class NavBarToHomeTouchController implements TouchController,
private void initCurrentAnimation() { private void initCurrentAnimation() {
long accuracy = (long) (getShiftRange() * 2); long accuracy = (long) (getShiftRange() * 2);
final AnimatorSet anim = new AnimatorSet(); final PendingAnimation builder = new PendingAnimation(accuracy);
if (mStartState == OVERVIEW) { if (mStartState == OVERVIEW) {
RecentsView recentsView = mLauncher.getOverviewPanel(); RecentsView recentsView = mLauncher.getOverviewPanel();
float pullbackDist = mPullbackDistance; float pullbackDist = mPullbackDistance;
if (!recentsView.isRtl()) { if (!recentsView.isRtl()) {
pullbackDist = -pullbackDist; pullbackDist = -pullbackDist;
} }
ObjectAnimator pullback = ObjectAnimator.ofFloat(recentsView, TRANSLATION_X,
pullbackDist); builder.setFloat(recentsView, VIEW_TRANSLATE_X, pullbackDist, PULLBACK_INTERPOLATOR);
pullback.setInterpolator(PULLBACK_INTERPOLATOR);
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
pullback.addUpdateListener( builder.addOnFrameCallback(
valueAnimator -> recentsView.redrawLiveTile(false /* mightNeedToRefill */)); () -> recentsView.redrawLiveTile(false /* mightNeedToRefill */));
} }
anim.play(pullback);
} else if (mStartState == ALL_APPS) { } else if (mStartState == ALL_APPS) {
AnimatorSetBuilder builder = new AnimatorSetBuilder();
AllAppsTransitionController allAppsController = mLauncher.getAllAppsController(); AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
Animator allAppsProgress = ObjectAnimator.ofFloat(allAppsController, ALL_APPS_PROGRESS, builder.setFloat(allAppsController, ALL_APPS_PROGRESS,
-mPullbackDistance / allAppsController.getShiftRange()); -mPullbackDistance / allAppsController.getShiftRange(), PULLBACK_INTERPOLATOR);
allAppsProgress.setInterpolator(PULLBACK_INTERPOLATOR);
builder.play(allAppsProgress);
// Slightly fade out all apps content to further distinguish from scrolling. // Slightly fade out all apps content to further distinguish from scrolling.
builder.setInterpolator(AnimatorSetBuilder.ANIM_ALL_APPS_FADE, Interpolators StateAnimationConfig config = new StateAnimationConfig();
.mapToProgress(PULLBACK_INTERPOLATOR, 0, 0.5f));
AnimationConfig config = new AnimationConfig();
config.duration = accuracy; config.duration = accuracy;
allAppsController.setAlphas(mEndState.getVisibleElements(mLauncher), config, builder); config.setInterpolator(StateAnimationConfig.ANIM_ALL_APPS_FADE, Interpolators
anim.play(builder.build()); .mapToProgress(PULLBACK_INTERPOLATOR, 0, 0.5f));
allAppsController.setAlphas(mEndState, config, builder);
} }
AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mLauncher); AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mLauncher);
if (topView != null) { if (topView != null) {
Animator hintCloseAnim = topView.createHintCloseAnim(mPullbackDistance); topView.addHintCloseAnim(mPullbackDistance, PULLBACK_INTERPOLATOR, builder);
if (hintCloseAnim != null) {
hintCloseAnim.setInterpolator(PULLBACK_INTERPOLATOR);
anim.play(hintCloseAnim);
}
} }
anim.setDuration(accuracy); mCurrentAnimation = builder.createPlaybackController()
mCurrentAnimation = AnimatorPlaybackController.wrap(anim, accuracy)
.setOnCancelRunnable(this::clearState); .setOnCancelRunnable(this::clearState);
} }
@@ -19,9 +19,9 @@ package com.android.launcher3.uioverrides.touchcontrollers;
import static com.android.launcher3.LauncherState.HINT_STATE; import static com.android.launcher3.LauncherState.HINT_STATE;
import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherStateManager.PLAY_ATOMIC_OVERVIEW_PEEK;
import static com.android.launcher3.Utilities.EDGE_NAV_BAR; import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL; import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_PEEK;
import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC; import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
import android.animation.AnimatorSet; import android.animation.AnimatorSet;
@@ -34,7 +34,7 @@ import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager; import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.Utilities; import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.util.VibratorWrapper; import com.android.launcher3.util.VibratorWrapper;
import com.android.quickstep.util.StaggeredWorkspaceAnim; import com.android.quickstep.util.StaggeredWorkspaceAnim;
@@ -171,11 +171,11 @@ public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchCo
// StaggeredWorkspaceAnim doesn't animate overview, so we handle it here. // StaggeredWorkspaceAnim doesn't animate overview, so we handle it here.
stateManager.cancelAnimation(); stateManager.cancelAnimation();
AnimatorSetBuilder builder = new AnimatorSetBuilder(); StateAnimationConfig config = new StateAnimationConfig();
long duration = OVERVIEW.getTransitionDuration(mLauncher); config.duration = OVERVIEW.getTransitionDuration(mLauncher);
config.animFlags = PLAY_ATOMIC_OVERVIEW_PEEK;
AnimatorSet anim = stateManager.createAtomicAnimation( AnimatorSet anim = stateManager.createAtomicAnimation(
stateManager.getState(), NORMAL, builder, stateManager.getState(), NORMAL, config);
PLAY_ATOMIC_OVERVIEW_PEEK, duration);
anim.addListener(AnimationSuccessListener.forRunnable( anim.addListener(AnimationSuccessListener.forRunnable(
() -> onSwipeInteractionCompleted(NORMAL, Touch.SWIPE))); () -> onSwipeInteractionCompleted(NORMAL, Touch.SWIPE)));
anim.start(); anim.start();
@@ -21,18 +21,18 @@ import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherState.QUICK_SWITCH; import static com.android.launcher3.LauncherState.QUICK_SWITCH;
import static com.android.launcher3.LauncherStateManager.ANIM_ALL_COMPONENTS;
import static com.android.launcher3.LauncherStateManager.SKIP_OVERVIEW;
import static com.android.launcher3.anim.AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD; import static com.android.launcher3.anim.AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE;
import static com.android.launcher3.anim.Interpolators.ACCEL_0_75; import static com.android.launcher3.anim.Interpolators.ACCEL_0_75;
import static com.android.launcher3.anim.Interpolators.DEACCEL; import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL_5; import static com.android.launcher3.anim.Interpolators.DEACCEL_5;
import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity; import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_RIGHT; import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_RIGHT;
import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_UP; import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_UP;
import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs; import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
@@ -55,15 +55,13 @@ import android.view.animation.Interpolator;
import com.android.launcher3.BaseQuickstepLauncher; import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.LauncherStateManager.AnimationFlags;
import com.android.launcher3.R; import com.android.launcher3.R;
import com.android.launcher3.Utilities; import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.OverviewScrim; import com.android.launcher3.graphics.OverviewScrim;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.touch.BaseSwipeDetector; import com.android.launcher3.touch.BaseSwipeDetector;
import com.android.launcher3.touch.BothAxesSwipeDetector; import com.android.launcher3.touch.BothAxesSwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -192,11 +190,9 @@ public class NoButtonQuickSwitchTouchController implements TouchController,
ShelfAnimState shelfState = isPaused ? PEEK : HIDE; ShelfAnimState shelfState = isPaused ? PEEK : HIDE;
if (shelfState == PEEK) { if (shelfState == PEEK) {
// Some shelf elements (e.g. qsb) were hidden, but we need them visible when peeking. // Some shelf elements (e.g. qsb) were hidden, but we need them visible when peeking.
AnimatorSetBuilder builder = new AnimatorSetBuilder();
AllAppsTransitionController allAppsController = mLauncher.getAllAppsController(); AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
allAppsController.setAlphas(NORMAL.getVisibleElements(mLauncher), allAppsController.setAlphas(
new AnimationConfig(), builder); NORMAL, new StateAnimationConfig(), NO_ANIM_PROPERTY_SETTER);
builder.build().setDuration(0).start();
if ((OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) { if ((OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) {
// Hotseat was hidden, but we need it visible when peeking. // Hotseat was hidden, but we need it visible when peeking.
@@ -209,12 +205,12 @@ public class NoButtonQuickSwitchTouchController implements TouchController,
private void setupAnimators() { private void setupAnimators() {
// Animate the non-overview components (e.g. workspace, shelf) out of the way. // Animate the non-overview components (e.g. workspace, shelf) out of the way.
AnimatorSetBuilder nonOverviewBuilder = new AnimatorSetBuilder(); StateAnimationConfig nonOverviewBuilder = new StateAnimationConfig();
nonOverviewBuilder.setInterpolator(ANIM_WORKSPACE_FADE, FADE_OUT_INTERPOLATOR); nonOverviewBuilder.setInterpolator(ANIM_WORKSPACE_FADE, FADE_OUT_INTERPOLATOR);
nonOverviewBuilder.setInterpolator(ANIM_ALL_APPS_FADE, FADE_OUT_INTERPOLATOR); nonOverviewBuilder.setInterpolator(ANIM_ALL_APPS_FADE, FADE_OUT_INTERPOLATOR);
nonOverviewBuilder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, TRANSLATE_OUT_INTERPOLATOR); nonOverviewBuilder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, TRANSLATE_OUT_INTERPOLATOR);
nonOverviewBuilder.setInterpolator(ANIM_VERTICAL_PROGRESS, TRANSLATE_OUT_INTERPOLATOR); nonOverviewBuilder.setInterpolator(ANIM_VERTICAL_PROGRESS, TRANSLATE_OUT_INTERPOLATOR);
updateNonOverviewAnim(QUICK_SWITCH, nonOverviewBuilder, ANIM_ALL_COMPONENTS); updateNonOverviewAnim(QUICK_SWITCH, nonOverviewBuilder);
mNonOverviewAnim.dispatchOnStart(); mNonOverviewAnim.dispatchOnStart();
if (mRecentsView.getTaskViewCount() == 0) { if (mRecentsView.getTaskViewCount() == 0) {
@@ -230,11 +226,12 @@ public class NoButtonQuickSwitchTouchController implements TouchController,
} }
/** Create state animation to control non-overview components. */ /** Create state animation to control non-overview components. */
private void updateNonOverviewAnim(LauncherState toState, AnimatorSetBuilder builder, private void updateNonOverviewAnim(LauncherState toState, StateAnimationConfig config) {
@AnimationFlags int animComponents) { config.duration = (long) (Math.max(mXRange, mYRange) * 2);
long accuracy = (long) (Math.max(mXRange, mYRange) * 2); config.animFlags = config.animFlags | SKIP_OVERVIEW;
mNonOverviewAnim = mLauncher.getStateManager().createAnimationToNewWorkspace(toState, mNonOverviewAnim = mLauncher.getStateManager()
builder, accuracy, this::clearState, animComponents | SKIP_OVERVIEW); .createAnimationToNewWorkspace(toState, config)
.setOnCancelRunnable(this::clearState);
} }
private void setupOverviewAnimators() { private void setupOverviewAnimators() {
@@ -419,8 +416,10 @@ public class NoButtonQuickSwitchTouchController implements TouchController,
if (flingUpToNormal && !mIsHomeScreenVisible) { if (flingUpToNormal && !mIsHomeScreenVisible) {
// We are flinging to home while workspace is invisible, run the same staggered // We are flinging to home while workspace is invisible, run the same staggered
// animation as from an app. // animation as from an app.
StateAnimationConfig config = new StateAnimationConfig();
// Update mNonOverviewAnim to do nothing so it doesn't interfere. // Update mNonOverviewAnim to do nothing so it doesn't interfere.
updateNonOverviewAnim(targetState, new AnimatorSetBuilder(), 0 /* animComponents */); config.animFlags = 0;
updateNonOverviewAnim(targetState, config);
nonOverviewAnim = mNonOverviewAnim.getAnimationPlayer(); nonOverviewAnim = mNonOverviewAnim.getAnimationPlayer();
new StaggeredWorkspaceAnim(mLauncher, velocity.y, false /* animateOverviewScrim */) new StaggeredWorkspaceAnim(mLauncher, velocity.y, false /* animateOverviewScrim */)
@@ -17,18 +17,17 @@ package com.android.launcher3.uioverrides.touchcontrollers;
import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.QUICK_SWITCH; import static com.android.launcher3.LauncherState.QUICK_SWITCH;
import static com.android.launcher3.LauncherStateManager.ANIM_ALL_COMPONENTS;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_Y;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE;
import static com.android.launcher3.anim.Interpolators.ACCEL_2; import static com.android.launcher3.anim.Interpolators.ACCEL_2;
import static com.android.launcher3.anim.Interpolators.DEACCEL_2; import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
import static com.android.launcher3.anim.Interpolators.INSTANT; import static com.android.launcher3.anim.Interpolators.INSTANT;
import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW; import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD; import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
@@ -41,7 +40,7 @@ import androidx.annotation.Nullable;
import com.android.launcher3.Launcher; import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherState;
import com.android.launcher3.Utilities; import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.touch.AbstractStateChangeTouchController; import com.android.launcher3.touch.AbstractStateChangeTouchController;
import com.android.launcher3.touch.SingleAxisSwipeDetector; import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -108,30 +107,30 @@ public class QuickSwitchTouchController extends AbstractStateChangeTouchControll
@Override @Override
protected float initCurrentAnimation(int animComponents) { protected float initCurrentAnimation(int animComponents) {
AnimatorSetBuilder animatorSetBuilder = new AnimatorSetBuilder(); StateAnimationConfig config = new StateAnimationConfig();
setupInterpolators(animatorSetBuilder); setupInterpolators(config);
long accuracy = (long) (getShiftRange() * 2); config.duration = (long) (getShiftRange() * 2);
mCurrentAnimation = mLauncher.getStateManager().createAnimationToNewWorkspace(mToState, mCurrentAnimation = mLauncher.getStateManager()
animatorSetBuilder, accuracy, this::clearState, ANIM_ALL_COMPONENTS); .createAnimationToNewWorkspace(mToState, config)
mCurrentAnimation.getAnimationPlayer().addUpdateListener(valueAnimator -> { .setOnCancelRunnable(this::clearState);
updateFullscreenProgress((Float) valueAnimator.getAnimatedValue()); mCurrentAnimation.getAnimationPlayer().addUpdateListener(valueAnimator ->
}); updateFullscreenProgress((Float) valueAnimator.getAnimatedValue()));
return 1 / getShiftRange(); return 1 / getShiftRange();
} }
private void setupInterpolators(AnimatorSetBuilder animatorSetBuilder) { private void setupInterpolators(StateAnimationConfig stateAnimationConfig) {
animatorSetBuilder.setInterpolator(ANIM_WORKSPACE_FADE, DEACCEL_2); stateAnimationConfig.setInterpolator(ANIM_WORKSPACE_FADE, DEACCEL_2);
animatorSetBuilder.setInterpolator(ANIM_ALL_APPS_FADE, DEACCEL_2); stateAnimationConfig.setInterpolator(ANIM_ALL_APPS_FADE, DEACCEL_2);
if (SysUINavigationMode.getMode(mLauncher) == Mode.NO_BUTTON) { if (SysUINavigationMode.getMode(mLauncher) == Mode.NO_BUTTON) {
// Overview lives to the left of workspace, so translate down later than over // Overview lives to the left of workspace, so translate down later than over
animatorSetBuilder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL_2); stateAnimationConfig.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL_2);
animatorSetBuilder.setInterpolator(ANIM_VERTICAL_PROGRESS, ACCEL_2); stateAnimationConfig.setInterpolator(ANIM_VERTICAL_PROGRESS, ACCEL_2);
animatorSetBuilder.setInterpolator(ANIM_OVERVIEW_SCALE, ACCEL_2); stateAnimationConfig.setInterpolator(ANIM_OVERVIEW_SCALE, ACCEL_2);
animatorSetBuilder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, ACCEL_2); stateAnimationConfig.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, ACCEL_2);
animatorSetBuilder.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT); stateAnimationConfig.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT);
} else { } else {
animatorSetBuilder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, LINEAR); stateAnimationConfig.setInterpolator(ANIM_WORKSPACE_TRANSLATE, LINEAR);
animatorSetBuilder.setInterpolator(ANIM_VERTICAL_PROGRESS, LINEAR); stateAnimationConfig.setInterpolator(ANIM_VERTICAL_PROGRESS, LINEAR);
} }
} }
@@ -214,7 +214,7 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
if (mCurrentAnimation != null) { if (mCurrentAnimation != null) {
mCurrentAnimation.setOnCancelRunnable(null); mCurrentAnimation.setOnCancelRunnable(null);
} }
mCurrentAnimation = AnimatorPlaybackController.wrap(mPendingAnimation, maxDuration) mCurrentAnimation = mPendingAnimation.createPlaybackController()
.setOnCancelRunnable(this::clearState); .setOnCancelRunnable(this::clearState);
onUserControlledAnimationCreated(mCurrentAnimation); onUserControlledAnimationCreated(mCurrentAnimation);
mCurrentAnimation.getTarget().addListener(this); mCurrentAnimation.getTarget().addListener(this);
@@ -48,7 +48,7 @@ import com.android.systemui.shared.system.TransactionCompat;
* *
* @param <T> activity that contains the overview * @param <T> activity that contains the overview
*/ */
final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> implements final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> extends
RemoteAnimationProvider { RemoteAnimationProvider {
private static final long RECENTS_LAUNCH_DURATION = 250; private static final long RECENTS_LAUNCH_DURATION = 250;
@@ -213,7 +213,7 @@ public final class FallbackActivityInterface implements
@Override @Override
public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTargetCompat target) { public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTargetCompat target) {
// TODO: Remove this once b/77875376 is fixed // TODO: Remove this once b/77875376 is fixed
return target.sourceContainerBounds; return target.screenSpaceBounds;
} }
@Override @Override
@@ -33,6 +33,7 @@ import com.android.launcher3.appprediction.PredictionUiStateManager;
import com.android.launcher3.logging.UserEventDispatcher; import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.quickstep.util.ActivityInitListener; import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.RemoteAnimationProvider;
import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView; import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -187,7 +188,15 @@ public class OverviewCommandHelper {
// Otherwise, start overview. // Otherwise, start overview.
mListener = mActivityInterface.createActivityInitListener(this::onActivityReady); mListener = mActivityInterface.createActivityInitListener(this::onActivityReady);
mListener.registerAndStartActivity(mOverviewComponentObserver.getOverviewIntent(), mListener.registerAndStartActivity(mOverviewComponentObserver.getOverviewIntent(),
this::createWindowAnimation, mContext, MAIN_EXECUTOR.getHandler(), new RemoteAnimationProvider() {
@Override
public AnimatorSet createWindowAnimation(
RemoteAnimationTargetCompat[] appTargets,
RemoteAnimationTargetCompat[] wallpaperTargets) {
return RecentsActivityCommand.this.createWindowAnimation(appTargets,
wallpaperTargets);
}
}, mContext, MAIN_EXECUTOR.getHandler(),
mAnimationProvider.getRecentsLaunchDuration()); mAnimationProvider.getRecentsLaunchDuration());
} }
@@ -50,6 +50,7 @@ import com.android.systemui.shared.system.WindowManagerWrapper;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius; import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius;
import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows; import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING; import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING; import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
@@ -119,7 +120,7 @@ public class AppWindowAnimationHelper {
private void updateSourceStack(RemoteAnimationTargetCompat target) { private void updateSourceStack(RemoteAnimationTargetCompat target) {
mSourceInsets.set(target.contentInsets); mSourceInsets.set(target.contentInsets);
mSourceStackBounds.set(target.sourceContainerBounds); mSourceStackBounds.set(target.screenSpaceBounds);
// TODO: Should sourceContainerBounds already have this offset? // TODO: Should sourceContainerBounds already have this offset?
mSourceStackBounds.offsetTo(target.position.x, target.position.y); mSourceStackBounds.offsetTo(target.position.x, target.position.y);
@@ -199,9 +200,17 @@ public class AppWindowAnimationHelper {
for (int i = 0; i < params.mTargetSet.unfilteredApps.length; i++) { for (int i = 0; i < params.mTargetSet.unfilteredApps.length; i++) {
RemoteAnimationTargetCompat app = params.mTargetSet.unfilteredApps[i]; RemoteAnimationTargetCompat app = params.mTargetSet.unfilteredApps[i];
SurfaceParams.Builder builder = new SurfaceParams.Builder(app.leash); SurfaceParams.Builder builder = new SurfaceParams.Builder(app.leash);
mTmpMatrix.setTranslate(app.position.x, app.position.y); if (app.localBounds != null) {
mTmpMatrix.setTranslate(0, 0);
if (app.activityType == ACTIVITY_TYPE_HOME && app.mode == MODE_CLOSING) {
mTmpMatrix.setTranslate(app.localBounds.left, app.localBounds.top);
}
} else {
mTmpMatrix.setTranslate(app.position.x, app.position.y);
}
Rect crop = mTmpRect; Rect crop = mTmpRect;
crop.set(app.sourceContainerBounds); crop.set(app.screenSpaceBounds);
crop.offsetTo(0, 0); crop.offsetTo(0, 0);
float alpha; float alpha;
float cornerRadius = 0f; float cornerRadius = 0f;
@@ -211,7 +220,11 @@ public class AppWindowAnimationHelper {
alpha = mTaskAlphaCallback.getAlpha(app, params.mTargetAlpha); alpha = mTaskAlphaCallback.getAlpha(app, params.mTargetAlpha);
if (app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) { if (app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
mTmpMatrix.setRectToRect(mSourceRect, mCurrentRect, ScaleToFit.FILL); mTmpMatrix.setRectToRect(mSourceRect, mCurrentRect, ScaleToFit.FILL);
mTmpMatrix.postTranslate(app.position.x, app.position.y); if (app.localBounds != null) {
mTmpMatrix.postTranslate(app.localBounds.left, app.localBounds.top);
} else {
mTmpMatrix.postTranslate(app.position.x, app.position.y);
}
mCurrentClipRectF.roundOut(crop); mCurrentClipRectF.roundOut(crop);
if (mSupportsRoundedCornersOnWindows) { if (mSupportsRoundedCornersOnWindows) {
if (params.mCornerRadius > -1) { if (params.mCornerRadius > -1) {
@@ -18,9 +18,10 @@ package com.android.quickstep.util;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.LauncherState.BACKGROUND_APP; import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherStateManager.ANIM_ALL_COMPONENTS; import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
import static com.android.launcher3.LauncherStateManager.SKIP_OVERVIEW;
import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_COMPONENTS;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
import android.animation.Animator; import android.animation.Animator;
import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorListenerAdapter;
@@ -33,14 +34,13 @@ import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile; import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher; import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.R; import com.android.launcher3.R;
import com.android.launcher3.ShortcutAndWidgetContainer; import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Workspace; import com.android.launcher3.Workspace;
import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.anim.SpringAnimationBuilder; import com.android.launcher3.anim.SpringAnimationBuilder;
import com.android.launcher3.graphics.OverviewScrim; import com.android.launcher3.graphics.OverviewScrim;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.util.DynamicResource; import com.android.launcher3.util.DynamicResource;
import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.RecentsView;
import com.android.systemui.plugins.ResourceProvider; import com.android.systemui.plugins.ResourceProvider;
@@ -143,11 +143,11 @@ public class StaggeredWorkspaceAnim {
* Setup workspace with 0 duration to prepare for our staggered animation. * Setup workspace with 0 duration to prepare for our staggered animation.
*/ */
private void prepareToAnimate(Launcher launcher) { private void prepareToAnimate(Launcher launcher) {
AnimatorSetBuilder builder = new AnimatorSetBuilder(); StateAnimationConfig config = new StateAnimationConfig();
config.animFlags = ANIM_ALL_COMPONENTS | SKIP_OVERVIEW;
config.duration = 0;
// setRecentsAttachedToAppWindow() will animate recents out. // setRecentsAttachedToAppWindow() will animate recents out.
launcher.getStateManager().createAtomicAnimation( launcher.getStateManager().createAtomicAnimation(BACKGROUND_APP, NORMAL, config).start();
BACKGROUND_APP, NORMAL, builder, ANIM_ALL_COMPONENTS | SKIP_OVERVIEW, 0);
builder.build().start();
// Stop scrolling so that it doesn't interfere with the translation offscreen. // Stop scrolling so that it doesn't interfere with the translation offscreen.
launcher.<RecentsView>getOverviewPanel().getScroller().forceFinished(true); launcher.<RecentsView>getOverviewPanel().getScroller().forceFinished(true);
@@ -197,16 +197,12 @@ public class StaggeredWorkspaceAnim {
} }
private void addScrimAnimationForState(Launcher launcher, LauncherState state, long duration) { private void addScrimAnimationForState(Launcher launcher, LauncherState state, long duration) {
AnimatorSetBuilder scrimAnimBuilder = new AnimatorSetBuilder(); PendingAnimation builder = new PendingAnimation(duration, mAnimators);
AnimationConfig scrimAnimConfig = new AnimationConfig(); launcher.getWorkspace().getStateTransitionAnimation().setScrim(builder, state);
scrimAnimConfig.duration = duration; builder.setFloat(
PropertySetter scrimPropertySetter = scrimAnimConfig.getPropertySetter(scrimAnimBuilder); launcher.getDragLayer().getOverviewScrim(),
launcher.getWorkspace().getStateTransitionAnimation().setScrim(scrimPropertySetter, state); OverviewScrim.SCRIM_PROGRESS,
mAnimators.play(scrimAnimBuilder.build()); state.getOverviewScrimAlpha(launcher),
Animator fadeOverviewScrim = ObjectAnimator.ofFloat( ACCEL_DEACCEL);
launcher.getDragLayer().getOverviewScrim(), OverviewScrim.SCRIM_PROGRESS,
state.getOverviewScrimAlpha(launcher));
fadeOverviewScrim.setDuration(duration);
mAnimators.play(fadeOverviewScrim);
} }
} }
@@ -1195,7 +1195,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
if (mPendingAnimation != null) { if (mPendingAnimation != null) {
mPendingAnimation.finish(false, Touch.SWIPE); mPendingAnimation.finish(false, Touch.SWIPE);
} }
PendingAnimation anim = new PendingAnimation(); PendingAnimation anim = new PendingAnimation(duration);
int count = getPageCount(); int count = getPageCount();
if (count == 0) { if (count == 0) {
@@ -1311,7 +1311,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
if (FeatureFlags.IS_STUDIO_BUILD && mPendingAnimation != null) { if (FeatureFlags.IS_STUDIO_BUILD && mPendingAnimation != null) {
throw new IllegalStateException("Another pending animation is still running"); throw new IllegalStateException("Another pending animation is still running");
} }
PendingAnimation anim = new PendingAnimation(); PendingAnimation anim = new PendingAnimation(duration);
int count = getTaskViewCount(); int count = getTaskViewCount();
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
@@ -1345,8 +1345,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
} }
private void runDismissAnimation(PendingAnimation pendingAnim) { private void runDismissAnimation(PendingAnimation pendingAnim) {
AnimatorPlaybackController controller = AnimatorPlaybackController controller = pendingAnim.createPlaybackController();
AnimatorPlaybackController.wrap(pendingAnim, DISMISS_TASK_DURATION);
controller.dispatchOnStart(); controller.dispatchOnStart();
controller.setEndAction(() -> pendingAnim.finish(true, Touch.SWIPE)); controller.setEndAction(() -> pendingAnim.finish(true, Touch.SWIPE));
controller.getAnimationPlayer().setInterpolator(FAST_OUT_SLOW_IN); controller.getAnimationPlayer().setInterpolator(FAST_OUT_SLOW_IN);
@@ -1671,7 +1670,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
int count = getTaskViewCount(); int count = getTaskViewCount();
if (count == 0) { if (count == 0) {
return new PendingAnimation(); return new PendingAnimation(duration);
} }
int targetSysUiFlags = tv.getThumbnail().getSysUiStatusNavFlags(); int targetSysUiFlags = tv.getThumbnail().getSysUiStatusNavFlags();
@@ -1710,10 +1709,9 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
anim.play(backgroundBlur); anim.play(backgroundBlur);
} }
anim.play(progressAnim); anim.play(progressAnim);
anim.setDuration(duration) anim.setDuration(duration).setInterpolator(interpolator);
.setInterpolator(interpolator);
mPendingAnimation = new PendingAnimation(); mPendingAnimation = new PendingAnimation(duration);
mPendingAnimation.add(anim); mPendingAnimation.add(anim);
mPendingAnimation.addEndListener((endState) -> { mPendingAnimation.addEndListener((endState) -> {
if (endState.isSuccess) { if (endState.isSuccess) {
@@ -285,8 +285,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
public AnimatorPlaybackController createLaunchAnimationForRunningTask() { public AnimatorPlaybackController createLaunchAnimationForRunningTask() {
final PendingAnimation pendingAnimation = getRecentsView().createTaskLaunchAnimation( final PendingAnimation pendingAnimation = getRecentsView().createTaskLaunchAnimation(
this, RECENTS_LAUNCH_DURATION, TOUCH_RESPONSE_INTERPOLATOR); this, RECENTS_LAUNCH_DURATION, TOUCH_RESPONSE_INTERPOLATOR);
AnimatorPlaybackController currentAnimation = AnimatorPlaybackController currentAnimation = pendingAnimation.createPlaybackController();
AnimatorPlaybackController.wrap(pendingAnimation, RECENTS_LAUNCH_DURATION);
currentAnimation.setEndAction(() -> { currentAnimation.setEndAction(() -> {
pendingAnimation.finish(true, Touch.SWIPE); pendingAnimation.finish(true, Touch.SWIPE);
launchTask(false); launchTask(false);
@@ -52,10 +52,12 @@ import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.SysUINavigationMode.Mode; import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener; import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
import com.android.quickstep.SystemUiProxy; import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.RemoteAnimationProvider;
import com.android.quickstep.util.RemoteFadeOutAnimationListener; import com.android.quickstep.util.RemoteFadeOutAnimationListener;
import com.android.quickstep.util.ShelfPeekAnim; import com.android.quickstep.util.ShelfPeekAnim;
import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.stream.Stream; import java.util.stream.Stream;
@@ -262,17 +264,21 @@ public abstract class BaseQuickstepLauncher extends Launcher
public void useFadeOutAnimationForLauncherStart(CancellationSignal signal) { public void useFadeOutAnimationForLauncherStart(CancellationSignal signal) {
QuickstepAppTransitionManagerImpl appTransitionManager = QuickstepAppTransitionManagerImpl appTransitionManager =
(QuickstepAppTransitionManagerImpl) getAppTransitionManager(); (QuickstepAppTransitionManagerImpl) getAppTransitionManager();
appTransitionManager.setRemoteAnimationProvider((appTargets, wallpaperTargets) -> { appTransitionManager.setRemoteAnimationProvider(new RemoteAnimationProvider() {
@Override
public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
RemoteAnimationTargetCompat[] wallpaperTargets) {
// On the first call clear the reference. // On the first call clear the reference.
signal.cancel(); signal.cancel();
ValueAnimator fadeAnimation = ValueAnimator.ofFloat(1, 0); ValueAnimator fadeAnimation = ValueAnimator.ofFloat(1, 0);
fadeAnimation.addUpdateListener(new RemoteFadeOutAnimationListener(appTargets, fadeAnimation.addUpdateListener(new RemoteFadeOutAnimationListener(appTargets,
wallpaperTargets)); wallpaperTargets));
AnimatorSet anim = new AnimatorSet(); AnimatorSet anim = new AnimatorSet();
anim.play(fadeAnimation); anim.play(fadeAnimation);
return anim; return anim;
}
}, signal); }, signal);
} }
@@ -34,7 +34,8 @@ import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat; import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@TargetApi(Build.VERSION_CODES.P) @TargetApi(Build.VERSION_CODES.P)
public abstract class LauncherAnimationRunner implements RemoteAnimationRunnerCompat { public abstract class LauncherAnimationRunner implements RemoteAnimationRunnerCompat,
WrappedAnimationRunnerImpl {
private final Handler mHandler; private final Handler mHandler;
private final boolean mStartAtFrontOfQueue; private final boolean mStartAtFrontOfQueue;
@@ -49,6 +50,10 @@ public abstract class LauncherAnimationRunner implements RemoteAnimationRunnerCo
mStartAtFrontOfQueue = startAtFrontOfQueue; mStartAtFrontOfQueue = startAtFrontOfQueue;
} }
public Handler getHandler() {
return mHandler;
}
// Called only in R+ platform // Called only in R+ platform
@BinderThread @BinderThread
public void onAnimationStart(RemoteAnimationTargetCompat[] appTargets, public void onAnimationStart(RemoteAnimationTargetCompat[] appTargets,
@@ -15,6 +15,7 @@
*/ */
package com.android.launcher3; package com.android.launcher3;
import android.animation.AnimatorSet;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@@ -25,6 +26,7 @@ import android.os.Handler;
import com.android.launcher3.util.ActivityTracker; import com.android.launcher3.util.ActivityTracker;
import com.android.quickstep.util.ActivityInitListener; import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.RemoteAnimationProvider; import com.android.quickstep.util.RemoteAnimationProvider;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.function.BiPredicate; import java.util.function.BiPredicate;
@@ -51,17 +53,21 @@ public class LauncherInitListener extends ActivityInitListener<Launcher> {
// Set a one-time animation provider. After the first call, this will get cleared. // Set a one-time animation provider. After the first call, this will get cleared.
// TODO: Probably also check the intended target id. // TODO: Probably also check the intended target id.
CancellationSignal cancellationSignal = new CancellationSignal(); CancellationSignal cancellationSignal = new CancellationSignal();
appTransitionManager.setRemoteAnimationProvider((appTargets, wallpaperTargets) -> { appTransitionManager.setRemoteAnimationProvider(new RemoteAnimationProvider() {
@Override
public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
RemoteAnimationTargetCompat[] wallpaperTargets) {
// On the first call clear the reference. // On the first call clear the reference.
cancellationSignal.cancel(); cancellationSignal.cancel();
RemoteAnimationProvider provider = mRemoteAnimationProvider; RemoteAnimationProvider provider = mRemoteAnimationProvider;
mRemoteAnimationProvider = null; mRemoteAnimationProvider = null;
if (provider != null && launcher.getStateManager().getState().overviewUi) { if (provider != null && launcher.getStateManager().getState().overviewUi) {
return provider.createWindowAnimation(appTargets, wallpaperTargets); return provider.createWindowAnimation(appTargets, wallpaperTargets);
}
return null;
} }
return null;
}, cancellationSignal); }, cancellationSignal);
} }
launcher.deferOverlayCallbacksUntilNextResumeOrStop(); launcher.deferOverlayCallbacksUntilNextResumeOrStop();
@@ -49,6 +49,7 @@ import android.content.Context;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Matrix; import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.RectF; import android.graphics.RectF;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
@@ -86,8 +87,6 @@ import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams; import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import com.android.systemui.shared.system.WindowManagerWrapper; import com.android.systemui.shared.system.WindowManagerWrapper;
import java.lang.ref.WeakReference;
/** /**
* {@link LauncherAppTransitionManager} with Quickstep-specific app transitions for launching from * {@link LauncherAppTransitionManager} with Quickstep-specific app transitions for launching from
* home and/or all-apps. Not used for 3p launchers. * home and/or all-apps. Not used for 3p launchers.
@@ -299,8 +298,12 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
if (mLauncher.isInMultiWindowMode()) { if (mLauncher.isInMultiWindowMode()) {
for (RemoteAnimationTargetCompat target : appTargets) { for (RemoteAnimationTargetCompat target : appTargets) {
if (target.mode == MODE_OPENING) { if (target.mode == MODE_OPENING) {
bounds.set(target.sourceContainerBounds); bounds.set(target.screenSpaceBounds);
bounds.offsetTo(target.position.x, target.position.y); if (target.localBounds != null) {
bounds.set(target.localBounds);
} else {
bounds.offsetTo(target.position.x, target.position.y);
}
return bounds; return bounds;
} }
} }
@@ -460,6 +463,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
RectF targetBounds = new RectF(windowTargetBounds); RectF targetBounds = new RectF(windowTargetBounds);
RectF currentBounds = new RectF(); RectF currentBounds = new RectF();
RectF temp = new RectF(); RectF temp = new RectF();
Point tmpPos = new Point();
AnimatorSet animatorSet = new AnimatorSet(); AnimatorSet animatorSet = new AnimatorSet();
ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1); ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
@@ -547,6 +551,13 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
for (int i = appTargets.length - 1; i >= 0; i--) { for (int i = appTargets.length - 1; i >= 0; i--) {
RemoteAnimationTargetCompat target = appTargets[i]; RemoteAnimationTargetCompat target = appTargets[i];
SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash); SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
tmpPos.set(target.position.x, target.position.y);
if (target.localBounds != null) {
final Rect localBounds = target.localBounds;
tmpPos.set(target.localBounds.left, target.localBounds.top);
}
if (target.mode == MODE_OPENING) { if (target.mode == MODE_OPENING) {
matrix.setScale(scale, scale); matrix.setScale(scale, scale);
matrix.postTranslate(transX0, transY0); matrix.postTranslate(transX0, transY0);
@@ -563,9 +574,9 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
.withAlpha(1f - mIconAlpha.value) .withAlpha(1f - mIconAlpha.value)
.withCornerRadius(mWindowRadius.value); .withCornerRadius(mWindowRadius.value);
} else { } else {
matrix.setTranslate(target.position.x, target.position.y); matrix.setTranslate(tmpPos.x, tmpPos.y);
builder.withMatrix(matrix) builder.withMatrix(matrix)
.withWindowCrop(target.sourceContainerBounds) .withWindowCrop(target.screenSpaceBounds)
.withAlpha(1f); .withAlpha(1f);
} }
builder.withLayer(RemoteAnimationProvider.getLayer(target, MODE_OPENING)); builder.withLayer(RemoteAnimationProvider.getLayer(target, MODE_OPENING));
@@ -662,7 +673,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
RemoteAnimationTargetCompat target = appTargets[i]; RemoteAnimationTargetCompat target = appTargets[i];
params[i] = new SurfaceParams.Builder(target.leash) params[i] = new SurfaceParams.Builder(target.leash)
.withAlpha(1f) .withAlpha(1f)
.withWindowCrop(target.sourceContainerBounds) .withWindowCrop(target.screenSpaceBounds)
.withLayer(RemoteAnimationProvider.getLayer(target, MODE_OPENING)) .withLayer(RemoteAnimationProvider.getLayer(target, MODE_OPENING))
.withCornerRadius(cornerRadius) .withCornerRadius(cornerRadius)
.build(); .build();
@@ -681,6 +692,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
SyncRtSurfaceTransactionApplierCompat surfaceApplier = SyncRtSurfaceTransactionApplierCompat surfaceApplier =
new SyncRtSurfaceTransactionApplierCompat(mDragLayer); new SyncRtSurfaceTransactionApplierCompat(mDragLayer);
Matrix matrix = new Matrix(); Matrix matrix = new Matrix();
Point tmpPos = new Point();
ValueAnimator closingAnimator = ValueAnimator.ofFloat(0, 1); ValueAnimator closingAnimator = ValueAnimator.ofFloat(0, 1);
int duration = CLOSING_TRANSITION_DURATION_MS; int duration = CLOSING_TRANSITION_DURATION_MS;
float windowCornerRadius = mDeviceProfile.isMultiWindowMode float windowCornerRadius = mDeviceProfile.isMultiWindowMode
@@ -697,22 +709,28 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
for (int i = appTargets.length - 1; i >= 0; i--) { for (int i = appTargets.length - 1; i >= 0; i--) {
RemoteAnimationTargetCompat target = appTargets[i]; RemoteAnimationTargetCompat target = appTargets[i];
SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash); SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
tmpPos.set(target.position.x, target.position.y);
if (target.localBounds != null) {
tmpPos.set(target.localBounds.left, target.localBounds.top);
}
if (target.mode == MODE_CLOSING) { if (target.mode == MODE_CLOSING) {
matrix.setScale(mScale.value, mScale.value, matrix.setScale(mScale.value, mScale.value,
target.sourceContainerBounds.centerX(), target.screenSpaceBounds.centerX(),
target.sourceContainerBounds.centerY()); target.screenSpaceBounds.centerY());
matrix.postTranslate(0, mDy.value); matrix.postTranslate(0, mDy.value);
matrix.postTranslate(target.position.x, target.position.y); matrix.postTranslate(tmpPos.x, tmpPos.y);
builder.withMatrix(matrix) builder.withMatrix(matrix)
.withAlpha(mAlpha.value) .withAlpha(mAlpha.value)
.withCornerRadius(windowCornerRadius); .withCornerRadius(windowCornerRadius);
} else { } else {
matrix.setTranslate(target.position.x, target.position.y); matrix.setTranslate(tmpPos.x, tmpPos.y);
builder.withMatrix(matrix) builder.withMatrix(matrix)
.withAlpha(1f); .withAlpha(1f);
} }
params[i] = builder params[i] = builder
.withWindowCrop(target.sourceContainerBounds) .withWindowCrop(target.screenSpaceBounds)
.withLayer(RemoteAnimationProvider.getLayer(target, MODE_CLOSING)) .withLayer(RemoteAnimationProvider.getLayer(target, MODE_CLOSING))
.build(); .build();
} }
@@ -784,52 +802,6 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
== PackageManager.PERMISSION_GRANTED; == PackageManager.PERMISSION_GRANTED;
} }
/**
* Used with WrappedLauncherAnimationRunner as an interface for the runner to call back to the
* implementation.
*/
protected interface WrappedAnimationRunnerImpl {
Handler getHandler();
void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
RemoteAnimationTargetCompat[] wallpaperTargets,
LauncherAnimationRunner.AnimationResult result);
}
/**
* This class is needed to wrap any animation runner that is a part of the
* RemoteAnimationDefinition:
* - Launcher creates a new instance of the LauncherAppTransitionManagerImpl whenever it is
* created, which in turn registers a new definition
* - When the definition is registered, window manager retains a strong binder reference to the
* runner passed in
* - If the Launcher activity is recreated, the new definition registered will replace the old
* reference in the system's activity record, but until the system server is GC'd, the binder
* reference will still exist, which references the runner in the Launcher process, which
* references the (old) Launcher activity through this class
*
* Instead we make the runner provided to the definition static only holding a weak reference to
* the runner implementation. When this animation manager is destroyed, we remove the Launcher
* reference to the runner, leaving only the weak ref from the runner.
*/
protected static class WrappedLauncherAnimationRunner<R extends WrappedAnimationRunnerImpl>
extends LauncherAnimationRunner {
private WeakReference<R> mImpl;
public WrappedLauncherAnimationRunner(R animationRunnerImpl, boolean startAtFrontOfQueue) {
super(animationRunnerImpl.getHandler(), startAtFrontOfQueue);
mImpl = new WeakReference<>(animationRunnerImpl);
}
@Override
public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
RemoteAnimationTargetCompat[] wallpaperTargets, AnimationResult result) {
R animationRunnerImpl = mImpl.get();
if (animationRunnerImpl != null) {
animationRunnerImpl.onCreateAnimation(appTargets, wallpaperTargets, result);
}
}
}
/** /**
* Remote animation runner for animation from the app to Launcher, including recents. * Remote animation runner for animation from the app to Launcher, including recents.
*/ */
@@ -0,0 +1,32 @@
/*
* Copyright (C) 2020 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;
import android.os.Handler;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
/**
* Used with WrappedLauncherAnimationRunner as an interface for the runner to call back to the
* implementation.
*/
public interface WrappedAnimationRunnerImpl {
Handler getHandler();
void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
RemoteAnimationTargetCompat[] wallpaperTargets,
LauncherAnimationRunner.AnimationResult result);
}
@@ -0,0 +1,56 @@
/*
* Copyright (C) 2020 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;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.lang.ref.WeakReference;
/**
* This class is needed to wrap any animation runner that is a part of the
* RemoteAnimationDefinition:
* - Launcher creates a new instance of the LauncherAppTransitionManagerImpl whenever it is
* created, which in turn registers a new definition
* - When the definition is registered, window manager retains a strong binder reference to the
* runner passed in
* - If the Launcher activity is recreated, the new definition registered will replace the old
* reference in the system's activity record, but until the system server is GC'd, the binder
* reference will still exist, which references the runner in the Launcher process, which
* references the (old) Launcher activity through this class
*
* Instead we make the runner provided to the definition static only holding a weak reference to
* the runner implementation. When this animation manager is destroyed, we remove the Launcher
* reference to the runner, leaving only the weak ref from the runner.
*/
public class WrappedLauncherAnimationRunner<R extends WrappedAnimationRunnerImpl>
extends LauncherAnimationRunner {
private WeakReference<R> mImpl;
public WrappedLauncherAnimationRunner(R animationRunnerImpl, boolean startAtFrontOfQueue) {
super(animationRunnerImpl.getHandler(), startAtFrontOfQueue);
mImpl = new WeakReference<>(animationRunnerImpl);
}
@Override
public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
RemoteAnimationTargetCompat[] wallpaperTargets, AnimationResult result) {
R animationRunnerImpl = mImpl.get();
if (animationRunnerImpl != null) {
animationRunnerImpl.onCreateAnimation(appTargets, wallpaperTargets, result);
}
}
}
@@ -16,19 +16,23 @@
package com.android.launcher3.uioverrides; package com.android.launcher3.uioverrides;
import android.animation.ValueAnimator; import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.quickstep.AnimatedFloat.VALUE;
import com.android.launcher3.BaseQuickstepLauncher; import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager; import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.util.UiThreadHelper; import com.android.launcher3.util.UiThreadHelper;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.SysUINavigationMode; import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.SystemUiProxy; import com.android.quickstep.SystemUiProxy;
public class BackButtonAlphaHandler implements LauncherStateManager.StateHandler { public class BackButtonAlphaHandler implements LauncherStateManager.StateHandler {
private final BaseQuickstepLauncher mLauncher; private final BaseQuickstepLauncher mLauncher;
private final AnimatedFloat mBackAlpha = new AnimatedFloat(this::updateBackAlpha);
public BackButtonAlphaHandler(BaseQuickstepLauncher launcher) { public BackButtonAlphaHandler(BaseQuickstepLauncher launcher) {
mLauncher = launcher; mLauncher = launcher;
@@ -38,8 +42,8 @@ public class BackButtonAlphaHandler implements LauncherStateManager.StateHandler
public void setState(LauncherState state) { } public void setState(LauncherState state) { }
@Override @Override
public void setStateWithAnimation(LauncherState toState, public void setStateWithAnimation(LauncherState toState, StateAnimationConfig config,
AnimatorSetBuilder builder, LauncherStateManager.AnimationConfig config) { PendingAnimation animation) {
if (config.onlyPlayAtomicComponent()) { if (config.onlyPlayAtomicComponent()) {
return; return;
} }
@@ -51,17 +55,12 @@ public class BackButtonAlphaHandler implements LauncherStateManager.StateHandler
return; return;
} }
float fromAlpha = SystemUiProxy.INSTANCE.get(mLauncher).getLastBackButtonAlpha(); mBackAlpha.value = SystemUiProxy.INSTANCE.get(mLauncher).getLastBackButtonAlpha();
float toAlpha = toState.hideBackButton ? 0 : 1; animation.setFloat(mBackAlpha, VALUE, toState.hideBackButton ? 0 : 1, LINEAR);
if (Float.compare(fromAlpha, toAlpha) != 0) { }
ValueAnimator anim = ValueAnimator.ofFloat(fromAlpha, toAlpha);
anim.setDuration(config.duration); private void updateBackAlpha() {
anim.addUpdateListener(valueAnimator -> { UiThreadHelper.setBackButtonAlphaAsync(mLauncher,
final float alpha = (float) valueAnimator.getAnimatedValue(); BaseQuickstepLauncher.SET_BACK_BUTTON_ALPHA, mBackAlpha.value, false /* animate */);
UiThreadHelper.setBackButtonAlphaAsync(mLauncher,
BaseQuickstepLauncher.SET_BACK_BUTTON_ALPHA, alpha, false /* animate */);
});
builder.play(anim);
}
} }
} }
@@ -26,8 +26,8 @@ import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager; import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.R; import com.android.launcher3.R;
import com.android.launcher3.Utilities; import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.PropertySetter; import com.android.launcher3.states.StateAnimationConfig;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat; import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.SurfaceControlCompat; import com.android.systemui.shared.system.SurfaceControlCompat;
import com.android.systemui.shared.system.TransactionCompat; import com.android.systemui.shared.system.TransactionCompat;
@@ -133,16 +133,15 @@ public class BackgroundBlurController implements LauncherStateManager.StateHandl
} }
@Override @Override
public void setStateWithAnimation(LauncherState toState, AnimatorSetBuilder builder, public void setStateWithAnimation(LauncherState toState, StateAnimationConfig config,
LauncherStateManager.AnimationConfig config) { PendingAnimation animation) {
if (mSurface == null || config.onlyPlayAtomicComponent()) { if (mSurface == null || config.onlyPlayAtomicComponent()) {
return; return;
} }
int toBackgroundBlurRadius = toState.getBackgroundBlurRadius(mLauncher); int toBackgroundBlurRadius = toState.getBackgroundBlurRadius(mLauncher);
if (mBackgroundBlurRadius != toBackgroundBlurRadius) { if (mBackgroundBlurRadius != toBackgroundBlurRadius) {
PropertySetter propertySetter = config.getPropertySetter(builder); animation.setInt(this, BACKGROUND_BLUR, toBackgroundBlurRadius, LINEAR);
propertySetter.setInt(this, BACKGROUND_BLUR, toBackgroundBlurRadius, LINEAR);
} }
} }
@@ -20,17 +20,17 @@ import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA; import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.LauncherStateManager.PLAY_ATOMIC_OVERVIEW_PEEK;
import static com.android.launcher3.LauncherStateManager.PLAY_ATOMIC_OVERVIEW_SCALE;
import static com.android.launcher3.LauncherStateManager.SKIP_OVERVIEW;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCRIM_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_X;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_Y;
import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT; import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT;
import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.graphics.Scrim.SCRIM_PROGRESS; import static com.android.launcher3.graphics.Scrim.SCRIM_PROGRESS;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCRIM_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_PEEK;
import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_SCALE;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
import android.util.FloatProperty; import android.util.FloatProperty;
import android.view.View; import android.view.View;
@@ -41,11 +41,10 @@ import androidx.annotation.NonNull;
import com.android.launcher3.Launcher; import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherState.ScaleAndTranslation; import com.android.launcher3.LauncherState.ScaleAndTranslation;
import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.LauncherStateManager.StateHandler; import com.android.launcher3.LauncherStateManager.StateHandler;
import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.graphics.OverviewScrim; import com.android.launcher3.graphics.OverviewScrim;
import com.android.launcher3.states.StateAnimationConfig;
/** /**
* State handler for recents view. Manages UI changes and animations for recents view based off the * State handler for recents view. Manages UI changes and animations for recents view based off the
@@ -86,8 +85,8 @@ public abstract class BaseRecentsViewStateController<T extends View>
} }
@Override @Override
public final void setStateWithAnimation(@NonNull final LauncherState toState, public void setStateWithAnimation(LauncherState toState, StateAnimationConfig config,
@NonNull AnimatorSetBuilder builder, @NonNull AnimationConfig config) { PendingAnimation builder) {
if (!config.hasAnimationFlag(PLAY_ATOMIC_OVERVIEW_PEEK | PLAY_ATOMIC_OVERVIEW_SCALE)) { if (!config.hasAnimationFlag(PLAY_ATOMIC_OVERVIEW_PEEK | PLAY_ATOMIC_OVERVIEW_SCALE)) {
// The entire recents animation is played atomically. // The entire recents animation is played atomically.
return; return;
@@ -95,25 +94,24 @@ public abstract class BaseRecentsViewStateController<T extends View>
if (config.hasAnimationFlag(SKIP_OVERVIEW)) { if (config.hasAnimationFlag(SKIP_OVERVIEW)) {
return; return;
} }
setStateWithAnimationInternal(toState, builder, config); setStateWithAnimationInternal(toState, config, builder);
} }
/** /**
* Core logic for animating the recents view UI. * Core logic for animating the recents view UI.
* *
* @param toState state to animate to * @param toState state to animate to
* @param builder animator set builder
* @param config current animation config * @param config current animation config
* @param setter animator set builder
*/ */
void setStateWithAnimationInternal(@NonNull final LauncherState toState, void setStateWithAnimationInternal(@NonNull final LauncherState toState,
@NonNull AnimatorSetBuilder builder, @NonNull AnimationConfig config) { @NonNull StateAnimationConfig config, @NonNull PendingAnimation setter) {
PropertySetter setter = config.getPropertySetter(builder);
ScaleAndTranslation scaleAndTranslation = toState.getOverviewScaleAndTranslation(mLauncher); ScaleAndTranslation scaleAndTranslation = toState.getOverviewScaleAndTranslation(mLauncher);
Interpolator scaleInterpolator = builder.getInterpolator(ANIM_OVERVIEW_SCALE, LINEAR); Interpolator scaleInterpolator = config.getInterpolator(ANIM_OVERVIEW_SCALE, LINEAR);
setter.setFloat(mRecentsView, SCALE_PROPERTY, scaleAndTranslation.scale, scaleInterpolator); setter.setFloat(mRecentsView, SCALE_PROPERTY, scaleAndTranslation.scale, scaleInterpolator);
Interpolator translateXInterpolator = builder.getInterpolator( Interpolator translateXInterpolator = config.getInterpolator(
ANIM_OVERVIEW_TRANSLATE_X, LINEAR); ANIM_OVERVIEW_TRANSLATE_X, LINEAR);
Interpolator translateYInterpolator = builder.getInterpolator( Interpolator translateYInterpolator = config.getInterpolator(
ANIM_OVERVIEW_TRANSLATE_Y, LINEAR); ANIM_OVERVIEW_TRANSLATE_Y, LINEAR);
float translationX = scaleAndTranslation.translationX; float translationX = scaleAndTranslation.translationX;
if (mRecentsView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) { if (mRecentsView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
@@ -123,14 +121,14 @@ public abstract class BaseRecentsViewStateController<T extends View>
setter.setFloat(mRecentsView, VIEW_TRANSLATE_Y, scaleAndTranslation.translationY, setter.setFloat(mRecentsView, VIEW_TRANSLATE_Y, scaleAndTranslation.translationY,
translateYInterpolator); translateYInterpolator);
setter.setFloat(mRecentsView, getContentAlphaProperty(), toState.overviewUi ? 1 : 0, setter.setFloat(mRecentsView, getContentAlphaProperty(), toState.overviewUi ? 1 : 0,
builder.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT)); config.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
OverviewScrim scrim = mLauncher.getDragLayer().getOverviewScrim(); OverviewScrim scrim = mLauncher.getDragLayer().getOverviewScrim();
setter.setFloat(scrim, SCRIM_PROGRESS, toState.getOverviewScrimAlpha(mLauncher), setter.setFloat(scrim, SCRIM_PROGRESS, toState.getOverviewScrimAlpha(mLauncher),
builder.getInterpolator(ANIM_OVERVIEW_SCRIM_FADE, LINEAR)); config.getInterpolator(ANIM_OVERVIEW_SCRIM_FADE, LINEAR));
if (mActionsView != null) { if (mActionsView != null) {
setter.setFloat(mActionsView, VIEW_TRANSLATE_X, translationX, translateXInterpolator); setter.setFloat(mActionsView, VIEW_TRANSLATE_X, translationX, translateXInterpolator);
setter.setFloat(mActionsView, VIEW_ALPHA, toState.overviewUi ? 1 : 0, setter.setFloat(mActionsView, VIEW_ALPHA, toState.overviewUi ? 1 : 0,
builder.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT)); config.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
} }
} }
@@ -0,0 +1,45 @@
/*
* Copyright (C) 2020 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;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import android.content.Context;
import android.os.Bundle;
import android.util.Size;
import android.view.View;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.graphics.LauncherPreviewRenderer;
import com.android.systemui.shared.system.SurfaceViewRequestReceiver;
/** Render preview using surface view. */
public class PreviewSurfaceRenderer {
/** Handle a received surface view request. */
public static void render(Context context, Bundle bundle) {
final String gridName = bundle.getString("name");
bundle.remove("name");
final InvariantDeviceProfile idp = new InvariantDeviceProfile(context, gridName);
MAIN_EXECUTOR.execute(() -> {
View view = new LauncherPreviewRenderer(context, idp).getRenderedView();
new SurfaceViewRequestReceiver().onReceive(context, bundle, view,
new Size(view.getMeasuredWidth(), view.getMeasuredHeight()));
});
}
}
@@ -9,7 +9,7 @@ import android.view.MotionEvent;
import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Launcher; import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager.AnimationFlags; import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
import com.android.launcher3.touch.AbstractStateChangeTouchController; import com.android.launcher3.touch.AbstractStateChangeTouchController;
import com.android.launcher3.touch.SingleAxisSwipeDetector; import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -19,13 +19,13 @@ import static com.android.launcher3.AbstractFloatingView.TYPE_ACCESSIBLE;
import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS;
import static com.android.launcher3.anim.Interpolators.ACCEL; import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL; import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS; import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
import android.animation.TimeInterpolator; import android.animation.TimeInterpolator;
@@ -37,11 +37,10 @@ import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile; import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher; import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager.AnimationFlags;
import com.android.launcher3.allapps.AllAppsTransitionController; 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.anim.Interpolators;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
import com.android.launcher3.touch.AbstractStateChangeTouchController; import com.android.launcher3.touch.AbstractStateChangeTouchController;
import com.android.launcher3.touch.SingleAxisSwipeDetector; import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.uioverrides.states.OverviewState; import com.android.launcher3.uioverrides.states.OverviewState;
@@ -148,16 +147,16 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr
return isTouchOverHotseat(mLauncher, ev) ? ContainerType.HOTSEAT : ContainerType.WORKSPACE; return isTouchOverHotseat(mLauncher, ev) ? ContainerType.HOTSEAT : ContainerType.WORKSPACE;
} }
private AnimatorSetBuilder getNormalToOverviewAnimation() { private StateAnimationConfig getNormalToOverviewAnimation() {
mAllAppsInterpolatorWrapper.baseInterpolator = LINEAR; mAllAppsInterpolatorWrapper.baseInterpolator = LINEAR;
AnimatorSetBuilder builder = new AnimatorSetBuilder(); StateAnimationConfig builder = new StateAnimationConfig();
builder.setInterpolator(ANIM_VERTICAL_PROGRESS, mAllAppsInterpolatorWrapper); builder.setInterpolator(ANIM_VERTICAL_PROGRESS, mAllAppsInterpolatorWrapper);
return builder; return builder;
} }
public static AnimatorSetBuilder getOverviewToAllAppsAnimation() { private static StateAnimationConfig getOverviewToAllAppsAnimation() {
AnimatorSetBuilder builder = new AnimatorSetBuilder(); StateAnimationConfig builder = new StateAnimationConfig();
builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(ACCEL, builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(ACCEL,
0, ALL_APPS_CONTENT_FADE_THRESHOLD)); 0, ALL_APPS_CONTENT_FADE_THRESHOLD));
builder.setInterpolator(ANIM_OVERVIEW_FADE, Interpolators.clampToProgress(DEACCEL, builder.setInterpolator(ANIM_OVERVIEW_FADE, Interpolators.clampToProgress(DEACCEL,
@@ -165,8 +164,8 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr
return builder; return builder;
} }
private AnimatorSetBuilder getAllAppsToOverviewAnimation() { private StateAnimationConfig getAllAppsToOverviewAnimation() {
AnimatorSetBuilder builder = new AnimatorSetBuilder(); StateAnimationConfig builder = new StateAnimationConfig();
builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(DEACCEL, builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(DEACCEL,
1 - ALL_APPS_CONTENT_FADE_THRESHOLD, 1)); 1 - ALL_APPS_CONTENT_FADE_THRESHOLD, 1));
builder.setInterpolator(ANIM_OVERVIEW_FADE, Interpolators.clampToProgress(ACCEL, builder.setInterpolator(ANIM_OVERVIEW_FADE, Interpolators.clampToProgress(ACCEL,
@@ -174,41 +173,42 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr
return builder; return builder;
} }
private AnimatorSetBuilder getNormalToAllAppsAnimation() { private StateAnimationConfig getNormalToAllAppsAnimation() {
AnimatorSetBuilder builder = new AnimatorSetBuilder(); StateAnimationConfig builder = new StateAnimationConfig();
builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(ACCEL, builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(ACCEL,
0, ALL_APPS_CONTENT_FADE_THRESHOLD)); 0, ALL_APPS_CONTENT_FADE_THRESHOLD));
return builder; return builder;
} }
private AnimatorSetBuilder getAllAppsToNormalAnimation() { private StateAnimationConfig getAllAppsToNormalAnimation() {
AnimatorSetBuilder builder = new AnimatorSetBuilder(); StateAnimationConfig builder = new StateAnimationConfig();
builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(DEACCEL, builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(DEACCEL,
1 - ALL_APPS_CONTENT_FADE_THRESHOLD, 1)); 1 - ALL_APPS_CONTENT_FADE_THRESHOLD, 1));
return builder; return builder;
} }
@Override @Override
protected AnimatorSetBuilder getAnimatorSetBuilderForStates(LauncherState fromState, protected StateAnimationConfig getConfigForStates(
LauncherState toState) { LauncherState fromState, LauncherState toState) {
AnimatorSetBuilder builder = new AnimatorSetBuilder(); final StateAnimationConfig config;
if (fromState == NORMAL && toState == OVERVIEW) { if (fromState == NORMAL && toState == OVERVIEW) {
builder = getNormalToOverviewAnimation(); config = getNormalToOverviewAnimation();
} else if (fromState == OVERVIEW && toState == ALL_APPS) { } else if (fromState == OVERVIEW && toState == ALL_APPS) {
builder = getOverviewToAllAppsAnimation(); config = getOverviewToAllAppsAnimation();
} else if (fromState == ALL_APPS && toState == OVERVIEW) { } else if (fromState == ALL_APPS && toState == OVERVIEW) {
builder = getAllAppsToOverviewAnimation(); config = getAllAppsToOverviewAnimation();
} else if (fromState == NORMAL && toState == ALL_APPS) { } else if (fromState == NORMAL && toState == ALL_APPS) {
builder = getNormalToAllAppsAnimation(); config = getNormalToAllAppsAnimation();
} else if (fromState == ALL_APPS && toState == NORMAL) { } else if (fromState == ALL_APPS && toState == NORMAL) {
builder = getAllAppsToNormalAnimation(); config = getAllAppsToNormalAnimation();
} else {
config = new StateAnimationConfig();
} }
return builder; return config;
} }
@Override @Override
protected float initCurrentAnimation(@AnimationFlags int animComponents) { protected float initCurrentAnimation(@AnimationFlags int animFlags) {
animComponents = updateAnimComponentsOnReinit(animComponents);
float range = getShiftRange(); float range = getShiftRange();
long maxAccuracy = (long) (2 * range); long maxAccuracy = (long) (2 * range);
@@ -217,8 +217,10 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr
float totalShift = endVerticalShift - startVerticalShift; float totalShift = endVerticalShift - startVerticalShift;
final AnimatorSetBuilder builder = totalShift == 0 ? new AnimatorSetBuilder() final StateAnimationConfig config = totalShift == 0 ? new StateAnimationConfig()
: getAnimatorSetBuilderForStates(mFromState, mToState); : getConfigForStates(mFromState, mToState);
config.animFlags = updateAnimComponentsOnReinit(animFlags);
config.duration = maxAccuracy;
cancelPendingAnim(); cancelPendingAnim();
@@ -232,15 +234,15 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr
cancelPendingAnim(); cancelPendingAnim();
clearState(); clearState();
}; };
mCurrentAnimation = AnimatorPlaybackController.wrap(mPendingAnimation, maxAccuracy) mCurrentAnimation = mPendingAnimation.createPlaybackController()
.setOnCancelRunnable(onCancelRunnable); .setOnCancelRunnable(onCancelRunnable);
mLauncher.getStateManager().setCurrentUserControlledAnimation(mCurrentAnimation); mLauncher.getStateManager().setCurrentUserControlledAnimation(mCurrentAnimation);
totalShift = LayoutUtils.getShelfTrackingDistance(mLauncher, totalShift = LayoutUtils.getShelfTrackingDistance(mLauncher,
mLauncher.getDeviceProfile()); mLauncher.getDeviceProfile());
} else { } else {
mCurrentAnimation = mLauncher.getStateManager() mCurrentAnimation = mLauncher.getStateManager()
.createAnimationToNewWorkspace(mToState, builder, maxAccuracy, this::clearState, .createAnimationToNewWorkspace(mToState, config)
animComponents); .setOnCancelRunnable(this::clearState);
} }
if (totalShift == 0) { if (totalShift == 0) {
@@ -21,21 +21,22 @@ import android.content.Context;
import android.os.Handler; import android.os.Handler;
import com.android.launcher3.LauncherAnimationRunner; import com.android.launcher3.LauncherAnimationRunner;
import com.android.launcher3.WrappedLauncherAnimationRunner;
import com.android.systemui.shared.system.ActivityOptionsCompat; import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.RemoteAnimationAdapterCompat; import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat; import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.TransactionCompat; import com.android.systemui.shared.system.TransactionCompat;
@FunctionalInterface public abstract class RemoteAnimationProvider {
public interface RemoteAnimationProvider {
LauncherAnimationRunner mAnimationRunner;
static final int Z_BOOST_BASE = 800570000; static final int Z_BOOST_BASE = 800570000;
AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets, public abstract AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
RemoteAnimationTargetCompat[] wallpaperTargets); RemoteAnimationTargetCompat[] wallpaperTargets);
default ActivityOptions toActivityOptions(Handler handler, long duration, Context context) { ActivityOptions toActivityOptions(Handler handler, long duration, Context context) {
LauncherAnimationRunner runner = new LauncherAnimationRunner(handler, mAnimationRunner = new LauncherAnimationRunner(handler,
false /* startAtFrontOfQueue */) { false /* startAtFrontOfQueue */) {
@Override @Override
@@ -44,8 +45,11 @@ public interface RemoteAnimationProvider {
result.setAnimation(createWindowAnimation(appTargets, wallpaperTargets), context); result.setAnimation(createWindowAnimation(appTargets, wallpaperTargets), context);
} }
}; };
final LauncherAnimationRunner wrapper = new WrappedLauncherAnimationRunner(
mAnimationRunner, false /* startAtFrontOfQueue */);
return ActivityOptionsCompat.makeRemoteAnimation( return ActivityOptionsCompat.makeRemoteAnimation(
new RemoteAnimationAdapterCompat(runner, duration, 0)); new RemoteAnimationAdapterCompat(wrapper, duration, 0));
} }
/** /**
@@ -63,7 +67,7 @@ public interface RemoteAnimationProvider {
} }
} }
static int getLayer(RemoteAnimationTargetCompat target, int boostModeTarget) { public static int getLayer(RemoteAnimationTargetCompat target, int boostModeTarget) {
return target.mode == boostModeTarget return target.mode == boostModeTarget
? Z_BOOST_BASE + target.prefixOrderIndex ? Z_BOOST_BASE + target.prefixOrderIndex
: target.prefixOrderIndex; : target.prefixOrderIndex;
@@ -72,7 +76,7 @@ public interface RemoteAnimationProvider {
/** /**
* @return the target with the lowest opaque layer for a certain app animation, or null. * @return the target with the lowest opaque layer for a certain app animation, or null.
*/ */
static RemoteAnimationTargetCompat findLowestOpaqueLayerTarget( public static RemoteAnimationTargetCompat findLowestOpaqueLayerTarget(
RemoteAnimationTargetCompat[] appTargets, int mode) { RemoteAnimationTargetCompat[] appTargets, int mode) {
int lowestLayer = Integer.MAX_VALUE; int lowestLayer = Integer.MAX_VALUE;
int lowestLayerIndex = -1; int lowestLayerIndex = -1;
+1 -1
View File
@@ -208,7 +208,7 @@
<string name="notification_dots_service_title">Show notification dots</string> <string name="notification_dots_service_title">Show notification dots</string>
<!-- Label for the setting that allows the automatic placement of launcher shortcuts for applications and games installed on the device [CHAR LIMIT=40] --> <!-- Label for the setting that allows the automatic placement of launcher shortcuts for applications and games installed on the device [CHAR LIMIT=40] -->
<string name="auto_add_shortcuts_label">Add icon to Home screen</string> <string name="auto_add_shortcuts_label">Add app icons to Home screen</string>
<!-- Text description of the setting that allows the automatic placement of launcher shortcuts for applications and games installed on the device [CHAR LIMIT=NONE] --> <!-- Text description of the setting that allows the automatic placement of launcher shortcuts for applications and games installed on the device [CHAR LIMIT=NONE] -->
<string name="auto_add_shortcuts_description">For new apps</string> <string name="auto_add_shortcuts_description">For new apps</string>
@@ -0,0 +1,155 @@
/*
* Copyright (C) 2020 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.model;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
import static com.android.launcher3.LauncherSettings.Favorites.TMP_CONTENT_URI;
import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
import static com.android.launcher3.util.LauncherModelHelper.APP_ICON;
import static com.android.launcher3.util.LauncherModelHelper.DESKTOP;
import static com.android.launcher3.util.LauncherModelHelper.HOTSEAT;
import static com.android.launcher3.util.LauncherModelHelper.SHORTCUT;
import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Point;
import android.os.Process;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.util.LauncherModelHelper;
import com.android.launcher3.util.LauncherRoboTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RuntimeEnvironment;
import java.util.HashSet;
/** Unit tests for {@link GridSizeMigrationTaskV2} */
@RunWith(LauncherRoboTestRunner.class)
public class GridSizeMigrationTaskV2Test {
private LauncherModelHelper mModelHelper;
private Context mContext;
private SQLiteDatabase mDb;
private HashSet<String> mValidPackages;
private InvariantDeviceProfile mIdp;
@Before
public void setUp() {
mModelHelper = new LauncherModelHelper();
mContext = RuntimeEnvironment.application;
mDb = mModelHelper.provider.getDb();
mValidPackages = new HashSet<>();
mValidPackages.add(TEST_PACKAGE);
mIdp = InvariantDeviceProfile.INSTANCE.get(mContext);
long userSerial = UserCache.INSTANCE.get(mContext).getSerialNumberForUser(
Process.myUserHandle());
dropTable(mDb, LauncherSettings.Favorites.TMP_TABLE);
LauncherSettings.Favorites.addTableToDb(mDb, userSerial, false,
LauncherSettings.Favorites.TMP_TABLE);
}
@Test
public void testMigration() {
final String testPackage1 = "com.android.launcher3.validpackage1";
final String testPackage2 = "com.android.launcher3.validpackage2";
final String testPackage3 = "com.android.launcher3.validpackage3";
final String testPackage4 = "com.android.launcher3.validpackage4";
final String testPackage5 = "com.android.launcher3.validpackage5";
final String testPackage7 = "com.android.launcher3.validpackage7";
mValidPackages.add(testPackage1);
mValidPackages.add(testPackage2);
mValidPackages.add(testPackage3);
mValidPackages.add(testPackage4);
mValidPackages.add(testPackage5);
mValidPackages.add(testPackage7);
int[] srcHotseatItems = {
mModelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI),
mModelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI),
-1,
mModelHelper.addItem(SHORTCUT, 3, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI),
mModelHelper.addItem(APP_ICON, 4, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI),
};
mModelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage5, 5, TMP_CONTENT_URI);
int[] destHotseatItems = {
-1,
mModelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2),
-1,
};
mModelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage7);
mIdp.numHotseatIcons = 3;
mIdp.numColumns = 3;
mIdp.numRows = 3;
GridSizeMigrationTaskV2.DbReader srcReader = new GridSizeMigrationTaskV2.DbReader(mDb,
LauncherSettings.Favorites.TMP_TABLE, mContext, mValidPackages, 5);
GridSizeMigrationTaskV2.DbReader destReader = new GridSizeMigrationTaskV2.DbReader(mDb,
LauncherSettings.Favorites.TABLE_NAME, mContext, mValidPackages, 3);
GridSizeMigrationTaskV2 task = new GridSizeMigrationTaskV2(mContext, mDb, srcReader,
destReader, 3, new Point(mIdp.numColumns, mIdp.numRows));
task.migrate();
Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
new String[]{LauncherSettings.Favorites.SCREEN, LauncherSettings.Favorites.INTENT},
"container=" + CONTAINER_HOTSEAT, null, null, null);
assertEquals(c.getCount(), 3);
int screenIndex = c.getColumnIndex(LauncherSettings.Favorites.SCREEN);
int intentIndex = c.getColumnIndex(LauncherSettings.Favorites.INTENT);
c.moveToNext();
assertEquals(c.getInt(screenIndex), 1);
assertTrue(c.getString(intentIndex).contains(testPackage2));
c.moveToNext();
assertEquals(c.getInt(screenIndex), 0);
assertTrue(c.getString(intentIndex).contains(testPackage1));
c.moveToNext();
assertEquals(c.getInt(screenIndex), 2);
assertTrue(c.getString(intentIndex).contains(testPackage3));
c.close();
c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
new String[]{LauncherSettings.Favorites.CELLX, LauncherSettings.Favorites.CELLY,
LauncherSettings.Favorites.INTENT},
"container=" + CONTAINER_DESKTOP, null, null, null);
assertEquals(c.getCount(), 2);
intentIndex = c.getColumnIndex(LauncherSettings.Favorites.INTENT);
int cellXIndex = c.getColumnIndex(LauncherSettings.Favorites.CELLX);
int cellYIndex = c.getColumnIndex(LauncherSettings.Favorites.CELLY);
c.moveToNext();
assertTrue(c.getString(intentIndex).contains(testPackage7));
c.moveToNext();
assertTrue(c.getString(intentIndex).contains(testPackage5));
assertEquals(c.getInt(cellXIndex), 0);
assertEquals(c.getInt(cellYIndex), 2);
}
}
@@ -230,7 +230,21 @@ public class LauncherModelHelper {
} }
public int addItem(int type, int screen, int container, int x, int y) { public int addItem(int type, int screen, int container, int x, int y) {
return addItem(type, screen, container, x, y, mDefaultProfileId); return addItem(type, screen, container, x, y, mDefaultProfileId, TEST_PACKAGE);
}
public int addItem(int type, int screen, int container, int x, int y, long profileId) {
return addItem(type, screen, container, x, y, profileId, TEST_PACKAGE);
}
public int addItem(int type, int screen, int container, int x, int y, String packageName) {
return addItem(type, screen, container, x, y, mDefaultProfileId, packageName);
}
public int addItem(int type, int screen, int container, int x, int y, String packageName,
int id, Uri contentUri) {
addItem(type, screen, container, x, y, mDefaultProfileId, packageName, id, contentUri);
return id;
} }
/** /**
@@ -238,11 +252,19 @@ public class LauncherModelHelper {
* @param type {@link #APP_ICON} or {@link #SHORTCUT} or >= 2 for * @param type {@link #APP_ICON} or {@link #SHORTCUT} or >= 2 for
* folder (where the type represents the number of items in the folder). * folder (where the type represents the number of items in the folder).
*/ */
public int addItem(int type, int screen, int container, int x, int y, long profileId) { public int addItem(int type, int screen, int container, int x, int y, long profileId,
String packageName) {
Context context = RuntimeEnvironment.application; Context context = RuntimeEnvironment.application;
int id = LauncherSettings.Settings.call(context.getContentResolver(), int id = LauncherSettings.Settings.call(context.getContentResolver(),
LauncherSettings.Settings.METHOD_NEW_ITEM_ID) LauncherSettings.Settings.METHOD_NEW_ITEM_ID)
.getInt(LauncherSettings.Settings.EXTRA_VALUE); .getInt(LauncherSettings.Settings.EXTRA_VALUE);
addItem(type, screen, container, x, y, profileId, packageName, id, CONTENT_URI);
return id;
}
public void addItem(int type, int screen, int container, int x, int y, long profileId,
String packageName, int id, Uri contentUri) {
Context context = RuntimeEnvironment.application;
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(LauncherSettings.Favorites._ID, id); values.put(LauncherSettings.Favorites._ID, id);
@@ -257,7 +279,7 @@ public class LauncherModelHelper {
if (type == APP_ICON || type == SHORTCUT) { if (type == APP_ICON || type == SHORTCUT) {
values.put(LauncherSettings.Favorites.ITEM_TYPE, type); values.put(LauncherSettings.Favorites.ITEM_TYPE, type);
values.put(LauncherSettings.Favorites.INTENT, values.put(LauncherSettings.Favorites.INTENT,
new Intent(Intent.ACTION_MAIN).setPackage(TEST_PACKAGE).toUri(0)); new Intent(Intent.ACTION_MAIN).setPackage(packageName).toUri(0));
} else { } else {
values.put(LauncherSettings.Favorites.ITEM_TYPE, values.put(LauncherSettings.Favorites.ITEM_TYPE,
LauncherSettings.Favorites.ITEM_TYPE_FOLDER); LauncherSettings.Favorites.ITEM_TYPE_FOLDER);
@@ -267,8 +289,7 @@ public class LauncherModelHelper {
} }
} }
context.getContentResolver().insert(CONTENT_URI, values); context.getContentResolver().insert(contentUri, values);
return id;
} }
public int[][][] createGrid(int[][][] typeArray) { public int[][][] createGrid(int[][][] typeArray) {
@@ -22,7 +22,6 @@ import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CH
import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled; import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent; import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
import android.animation.Animator;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.util.AttributeSet; import android.util.AttributeSet;
@@ -30,11 +29,12 @@ import android.util.Pair;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.Interpolator;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action; import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.util.TouchController; import com.android.launcher3.util.TouchController;
@@ -134,9 +134,8 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch
* Creates a user-controlled animation to hint that the view will be closed if completed. * Creates a user-controlled animation to hint that the view will be closed if completed.
* @param distanceToMove The max distance that elements should move from their starting point. * @param distanceToMove The max distance that elements should move from their starting point.
*/ */
public @Nullable Animator createHintCloseAnim(float distanceToMove) { public void addHintCloseAnim(
return null; float distanceToMove, Interpolator interpolator, PendingAnimation target) { }
}
public abstract void logActionCommand(int command); public abstract void logActionCommand(int command);
@@ -17,6 +17,7 @@
package com.android.launcher3; package com.android.launcher3;
import static com.android.launcher3.config.FeatureFlags.MULTI_DB_GRID_MIRATION_ALGO; import static com.android.launcher3.config.FeatureFlags.MULTI_DB_GRID_MIRATION_ALGO;
import static com.android.launcher3.provider.LauncherDbUtils.copyTable;
import static com.android.launcher3.provider.LauncherDbUtils.dropTable; import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
import static com.android.launcher3.provider.LauncherDbUtils.tableExists; import static com.android.launcher3.provider.LauncherDbUtils.tableExists;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
@@ -164,8 +165,11 @@ public class LauncherProvider extends ContentProvider {
return false; return false;
} }
mOpenHelper.close(); DatabaseHelper oldHelper = mOpenHelper;
mOpenHelper = new DatabaseHelper(getContext()); mOpenHelper = new DatabaseHelper(getContext());
copyTable(oldHelper.getReadableDatabase(), Favorites.TABLE_NAME,
mOpenHelper.getWritableDatabase(), Favorites.TMP_TABLE, getContext());
oldHelper.close();
return true; return true;
} }
@@ -102,6 +102,11 @@ public class LauncherSettings {
*/ */
public static final String PREVIEW_TABLE_NAME = "favorites_preview"; public static final String PREVIEW_TABLE_NAME = "favorites_preview";
/**
* Temporary table used specifically for multi-db grid migrations
*/
public static final String TMP_TABLE = "favorites_tmp";
/** /**
* The content:// style URL for "favorites" table * The content:// style URL for "favorites" table
*/ */
@@ -114,6 +119,12 @@ public class LauncherSettings {
public static final Uri PREVIEW_CONTENT_URI = Uri.parse("content://" public static final Uri PREVIEW_CONTENT_URI = Uri.parse("content://"
+ LauncherProvider.AUTHORITY + "/" + PREVIEW_TABLE_NAME); + LauncherProvider.AUTHORITY + "/" + PREVIEW_TABLE_NAME);
/**
* The content:// style URL for "favorites_tmp" table
*/
public static final Uri TMP_CONTENT_URI = Uri.parse("content://"
+ LauncherProvider.AUTHORITY + "/" + TMP_TABLE);
/** /**
* The content:// style URL for a given row, identified by its id. * The content:// style URL for a given row, identified by its id.
* *
+13 -13
View File
@@ -20,11 +20,6 @@ import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS;
import static android.view.View.VISIBLE; import static android.view.View.VISIBLE;
import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED; import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_X;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE;
import static com.android.launcher3.anim.Interpolators.ACCEL; import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.ACCEL_2; import static com.android.launcher3.anim.Interpolators.ACCEL_2;
import static com.android.launcher3.anim.Interpolators.DEACCEL; import static com.android.launcher3.anim.Interpolators.DEACCEL;
@@ -32,6 +27,11 @@ import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
import static com.android.launcher3.anim.Interpolators.clampToProgress; import static com.android.launcher3.anim.Interpolators.clampToProgress;
import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS; import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
import static com.android.launcher3.states.RotationHelper.REQUEST_NONE; import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCALE;
import static com.android.launcher3.testing.TestProtocol.ALL_APPS_STATE_ORDINAL; import static com.android.launcher3.testing.TestProtocol.ALL_APPS_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.BACKGROUND_APP_STATE_ORDINAL; import static com.android.launcher3.testing.TestProtocol.BACKGROUND_APP_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.HINT_STATE_ORDINAL; import static com.android.launcher3.testing.TestProtocol.HINT_STATE_ORDINAL;
@@ -46,9 +46,9 @@ import android.view.View;
import android.view.animation.Interpolator; import android.view.animation.Interpolator;
import com.android.launcher3.allapps.AllAppsContainerView; import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.states.HintState; import com.android.launcher3.states.HintState;
import com.android.launcher3.states.SpringLoadedState; import com.android.launcher3.states.SpringLoadedState;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.uioverrides.states.AllAppsState; import com.android.launcher3.uioverrides.states.AllAppsState;
import com.android.launcher3.uioverrides.states.OverviewState; import com.android.launcher3.uioverrides.states.OverviewState;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
@@ -325,13 +325,13 @@ public abstract class LauncherState {
* - Setting some start values (e.g. scale) for views that are hidden but about to be shown. * - Setting some start values (e.g. scale) for views that are hidden but about to be shown.
*/ */
public void prepareForAtomicAnimation(Launcher launcher, LauncherState fromState, public void prepareForAtomicAnimation(Launcher launcher, LauncherState fromState,
AnimatorSetBuilder builder) { StateAnimationConfig config) {
if (this == NORMAL && fromState == OVERVIEW) { if (this == NORMAL && fromState == OVERVIEW) {
builder.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL); config.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL);
builder.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL); config.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL);
builder.setInterpolator(ANIM_OVERVIEW_SCALE, clampToProgress(ACCEL, 0, 0.9f)); config.setInterpolator(ANIM_OVERVIEW_SCALE, clampToProgress(ACCEL, 0, 0.9f));
builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCEL); config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCEL);
builder.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL_1_7); config.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL_1_7);
Workspace workspace = launcher.getWorkspace(); Workspace workspace = launcher.getWorkspace();
// Start from a higher workspace scale, but only if we're invisible so we don't jump. // Start from a higher workspace scale, but only if we're invisible so we don't jump.
@@ -363,7 +363,7 @@ public abstract class LauncherState {
} }
} else if (this == NORMAL && fromState == OVERVIEW_PEEK) { } else if (this == NORMAL && fromState == OVERVIEW_PEEK) {
// Keep fully visible until the very end (when overview is offscreen) to make invisible. // Keep fully visible until the very end (when overview is offscreen) to make invisible.
builder.setInterpolator(ANIM_OVERVIEW_FADE, t -> t < 1 ? 0 : 1); config.setInterpolator(ANIM_OVERVIEW_FADE, t -> t < 1 ? 0 : 1);
} }
} }
@@ -17,26 +17,23 @@
package com.android.launcher3; package com.android.launcher3;
import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER; import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_COMPONENTS;
import android.animation.Animator; import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet; import android.animation.AnimatorSet;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import androidx.annotation.IntDef;
import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.anim.PropertySetter.AnimatedPropertySetter;
import com.android.launcher3.compat.AccessibilityManagerCompat; import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList; import java.util.ArrayList;
/** /**
@@ -84,28 +81,7 @@ public class LauncherStateManager {
public static final String TAG = "StateManager"; public static final String TAG = "StateManager";
// We separate the state animations into "atomic" and "non-atomic" components. The atomic private final AnimationState mConfig = new AnimationState();
// components may be run atomically - that is, all at once, instead of user-controlled. However,
// atomic components are not restricted to this purpose; they can be user-controlled alongside
// non atomic components as well. Note that each gesture model has exactly one atomic component,
// PLAY_ATOMIC_OVERVIEW_SCALE *or* PLAY_ATOMIC_OVERVIEW_PEEK.
@IntDef(flag = true, value = {
PLAY_NON_ATOMIC,
PLAY_ATOMIC_OVERVIEW_SCALE,
PLAY_ATOMIC_OVERVIEW_PEEK,
SKIP_OVERVIEW,
})
@Retention(RetentionPolicy.SOURCE)
public @interface AnimationFlags {}
public static final int PLAY_NON_ATOMIC = 1 << 0;
public static final int PLAY_ATOMIC_OVERVIEW_SCALE = 1 << 1;
public static final int PLAY_ATOMIC_OVERVIEW_PEEK = 1 << 2;
public static final int SKIP_OVERVIEW = 1 << 3;
public static final int ANIM_ALL_COMPONENTS = PLAY_NON_ATOMIC | PLAY_ATOMIC_OVERVIEW_SCALE
| PLAY_ATOMIC_OVERVIEW_PEEK;
private final AnimationConfig mConfig = new AnimationConfig();
private final Handler mUiHandler; private final Handler mUiHandler;
private final Launcher mLauncher; private final Launcher mLauncher;
private final ArrayList<StateListener> mListeners = new ArrayList<>(); private final ArrayList<StateListener> mListeners = new ArrayList<>();
@@ -140,7 +116,7 @@ public class LauncherStateManager {
writer.println(prefix + "\tmCurrentStableState:" + mCurrentStableState); writer.println(prefix + "\tmCurrentStableState:" + mCurrentStableState);
writer.println(prefix + "\tmState:" + mState); writer.println(prefix + "\tmState:" + mState);
writer.println(prefix + "\tmRestState:" + mRestState); writer.println(prefix + "\tmRestState:" + mRestState);
writer.println(prefix + "\tisInTransition:" + (mConfig.mCurrentAnimation != null)); writer.println(prefix + "\tisInTransition:" + (mConfig.currentAnimation != null));
} }
public StateHandler[] getStateHandlers() { public StateHandler[] getStateHandlers() {
@@ -171,7 +147,7 @@ public class LauncherStateManager {
*/ */
public boolean isInStableState(LauncherState state) { public boolean isInStableState(LauncherState state) {
return mState == state && mCurrentStableState == state return mState == state && mCurrentStableState == state
&& (mConfig.mTargetState == null || mConfig.mTargetState == state); && (mConfig.targetState == null || mConfig.targetState == state);
} }
/** /**
@@ -218,12 +194,12 @@ public class LauncherStateManager {
} }
public void reapplyState(boolean cancelCurrentAnimation) { public void reapplyState(boolean cancelCurrentAnimation) {
boolean wasInAnimation = mConfig.mCurrentAnimation != null; boolean wasInAnimation = mConfig.currentAnimation != null;
if (cancelCurrentAnimation) { if (cancelCurrentAnimation) {
cancelAllStateElementAnimation(); cancelAllStateElementAnimation();
cancelAnimation(); cancelAnimation();
} }
if (mConfig.mCurrentAnimation == null) { if (mConfig.currentAnimation == null) {
for (StateHandler handler : getStateHandlers()) { for (StateHandler handler : getStateHandlers()) {
handler.setState(mState); handler.setState(mState);
} }
@@ -237,16 +213,16 @@ public class LauncherStateManager {
final Runnable onCompleteRunnable) { final Runnable onCompleteRunnable) {
animated &= Utilities.areAnimationsEnabled(mLauncher); animated &= Utilities.areAnimationsEnabled(mLauncher);
if (mLauncher.isInState(state)) { if (mLauncher.isInState(state)) {
if (mConfig.mCurrentAnimation == null) { if (mConfig.currentAnimation == null) {
// Run any queued runnable // Run any queued runnable
if (onCompleteRunnable != null) { if (onCompleteRunnable != null) {
onCompleteRunnable.run(); onCompleteRunnable.run();
} }
return; return;
} else if (!mConfig.userControlled && animated && mConfig.mTargetState == state) { } else if (!mConfig.userControlled && animated && mConfig.targetState == state) {
// We are running the same animation as requested // We are running the same animation as requested
if (onCompleteRunnable != null) { if (onCompleteRunnable != null) {
mConfig.mCurrentAnimation.addListener( mConfig.currentAnimation.addListener(
AnimationSuccessListener.forRunnable(onCompleteRunnable)); AnimationSuccessListener.forRunnable(onCompleteRunnable));
} }
return; return;
@@ -276,9 +252,9 @@ public class LauncherStateManager {
if (delay > 0) { if (delay > 0) {
// Create the animation after the delay as some properties can change between preparing // Create the animation after the delay as some properties can change between preparing
// the animation and running the animation. // the animation and running the animation.
int startChangeId = mConfig.mChangeId; int startChangeId = mConfig.changeId;
mUiHandler.postDelayed(() -> { mUiHandler.postDelayed(() -> {
if (mConfig.mChangeId == startChangeId) { if (mConfig.changeId == startChangeId) {
goToStateAnimated(state, fromState, onCompleteRunnable); goToStateAnimated(state, fromState, onCompleteRunnable);
} }
}, delay); }, delay);
@@ -294,11 +270,11 @@ public class LauncherStateManager {
mConfig.duration = state == NORMAL mConfig.duration = state == NORMAL
? fromState.getTransitionDuration(mLauncher) ? fromState.getTransitionDuration(mLauncher)
: state.getTransitionDuration(mLauncher); : state.getTransitionDuration(mLauncher);
prepareForAtomicAnimation(fromState, state, mConfig);
AnimatorSetBuilder builder = new AnimatorSetBuilder(); AnimatorSet animation = createAnimationToNewWorkspaceInternal(state).getAnim();
prepareForAtomicAnimation(fromState, state, builder); if (onCompleteRunnable != null) {
AnimatorSet animation = createAnimationToNewWorkspaceInternal( animation.addListener(AnimationSuccessListener.forRunnable(onCompleteRunnable));
state, builder, onCompleteRunnable); }
mUiHandler.post(new StartAnimRunnable(animation)); mUiHandler.post(new StartAnimRunnable(animation));
} }
@@ -308,44 +284,22 @@ public class LauncherStateManager {
* - Setting some start values (e.g. scale) for views that are hidden but about to be shown. * - Setting some start values (e.g. scale) for views that are hidden but about to be shown.
*/ */
public void prepareForAtomicAnimation(LauncherState fromState, LauncherState toState, public void prepareForAtomicAnimation(LauncherState fromState, LauncherState toState,
AnimatorSetBuilder builder) { StateAnimationConfig config) {
toState.prepareForAtomicAnimation(mLauncher, fromState, builder); toState.prepareForAtomicAnimation(mLauncher, fromState, config);
}
public AnimatorSet createAtomicAnimation(LauncherState fromState, LauncherState toState,
AnimatorSetBuilder builder, @AnimationFlags int animFlags, long duration) {
prepareForAtomicAnimation(fromState, toState, builder);
AnimationConfig config = new AnimationConfig();
config.mAnimFlags = animFlags;
config.duration = duration;
for (StateHandler handler : mLauncher.getStateManager().getStateHandlers()) {
handler.setStateWithAnimation(toState, builder, config);
}
return builder.build();
} }
/** /**
* Creates a {@link AnimatorPlaybackController} that can be used for a controlled * Creates an animation representing atomic transitions between the provided states
* state transition. The UI is force-set to fromState before creating the controller.
* @param fromState the initial state for the transition.
* @param state the final state for the transition.
* @param duration intended duration for normal playback. Use higher duration for better
* accuracy.
*/ */
public AnimatorPlaybackController createAnimationToNewWorkspace( public AnimatorSet createAtomicAnimation(
LauncherState fromState, LauncherState state, long duration) { LauncherState fromState, LauncherState toState, StateAnimationConfig config) {
// Since we are creating a state animation to a different state, temporarily prevent state PendingAnimation builder = new PendingAnimation(config.duration);
// change as part of config reset. prepareForAtomicAnimation(fromState, toState, config);
LauncherState originalRestState = mRestState;
mRestState = state;
mConfig.reset();
mRestState = originalRestState;
for (StateHandler handler : getStateHandlers()) { for (StateHandler handler : mLauncher.getStateManager().getStateHandlers()) {
handler.setState(fromState); handler.setStateWithAnimation(toState, config, builder);
} }
return builder.getAnim();
return createAnimationToNewWorkspace(state, duration);
} }
/** /**
@@ -362,32 +316,27 @@ public class LauncherStateManager {
public AnimatorPlaybackController createAnimationToNewWorkspace( public AnimatorPlaybackController createAnimationToNewWorkspace(
LauncherState state, long duration, @AnimationFlags int animComponents) { LauncherState state, long duration, @AnimationFlags int animComponents) {
return createAnimationToNewWorkspace(state, new AnimatorSetBuilder(), duration, null, StateAnimationConfig config = new StateAnimationConfig();
animComponents); config.duration = duration;
config.animFlags = animComponents;
return createAnimationToNewWorkspace(state, config);
} }
public AnimatorPlaybackController createAnimationToNewWorkspace(LauncherState state, public AnimatorPlaybackController createAnimationToNewWorkspace(LauncherState state,
AnimatorSetBuilder builder, long duration, Runnable onCancelRunnable, StateAnimationConfig config) {
@AnimationFlags int animComponents) {
mConfig.reset(); mConfig.reset();
mConfig.userControlled = true; config.copyTo(mConfig);
mConfig.mAnimFlags = animComponents; mConfig.playbackController = createAnimationToNewWorkspaceInternal(state)
mConfig.duration = duration; .createPlaybackController();
mConfig.playbackController = AnimatorPlaybackController.wrap(
createAnimationToNewWorkspaceInternal(state, builder, null), duration)
.setOnCancelRunnable(onCancelRunnable);
return mConfig.playbackController; return mConfig.playbackController;
} }
protected AnimatorSet createAnimationToNewWorkspaceInternal(final LauncherState state, private PendingAnimation createAnimationToNewWorkspaceInternal(final LauncherState state) {
AnimatorSetBuilder builder, final Runnable onCompleteRunnable) { PendingAnimation builder = new PendingAnimation(mConfig.duration);
for (StateHandler handler : getStateHandlers()) { for (StateHandler handler : getStateHandlers()) {
handler.setStateWithAnimation(state, builder, mConfig); handler.setStateWithAnimation(state, mConfig, builder);
} }
builder.getAnim().addListener(new AnimationSuccessListener() {
final AnimatorSet animation = builder.build();
animation.addListener(new AnimationSuccessListener() {
@Override @Override
public void onAnimationStart(Animator animation) { public void onAnimationStart(Animator animation) {
@@ -397,15 +346,11 @@ public class LauncherStateManager {
@Override @Override
public void onAnimationSuccess(Animator animator) { public void onAnimationSuccess(Animator animator) {
// Run any queued runnables
if (onCompleteRunnable != null) {
onCompleteRunnable.run();
}
onStateTransitionEnd(state); onStateTransitionEnd(state);
} }
}); });
mConfig.setAnimation(animation, state); mConfig.setAnimation(builder.getAnim(), state);
return mConfig.mCurrentAnimation; return builder;
} }
private void onStateTransitionStart(LauncherState state) { private void onStateTransitionStart(LauncherState state) {
@@ -452,7 +397,7 @@ public class LauncherStateManager {
} }
public void moveToRestState() { public void moveToRestState() {
if (mConfig.mCurrentAnimation != null && mConfig.userControlled) { if (mConfig.currentAnimation != null && mConfig.userControlled) {
// The user is doing something. Lets not mess it up // The user is doing something. Lets not mess it up
return; return;
} }
@@ -500,12 +445,12 @@ public class LauncherStateManager {
&& mConfig.playbackController.getTarget() == childAnim) { && mConfig.playbackController.getTarget() == childAnim) {
clearCurrentAnimation(); clearCurrentAnimation();
break; break;
} else if (mConfig.mCurrentAnimation == childAnim) { } else if (mConfig.currentAnimation == childAnim) {
clearCurrentAnimation(); clearCurrentAnimation();
break; break;
} }
} }
boolean reapplyNeeded = mConfig.mCurrentAnimation != null; boolean reapplyNeeded = mConfig.currentAnimation != null;
cancelAnimation(); cancelAnimation();
if (reapplyNeeded) { if (reapplyNeeded) {
reapplyState(); reapplyState();
@@ -557,9 +502,9 @@ public class LauncherStateManager {
} }
private void clearCurrentAnimation() { private void clearCurrentAnimation() {
if (mConfig.mCurrentAnimation != null) { if (mConfig.currentAnimation != null) {
mConfig.mCurrentAnimation.removeListener(mConfig); mConfig.currentAnimation.removeListener(mConfig);
mConfig.mCurrentAnimation = null; mConfig.currentAnimation = null;
} }
mConfig.playbackController = null; mConfig.playbackController = null;
} }
@@ -574,54 +519,42 @@ public class LauncherStateManager {
@Override @Override
public void run() { public void run() {
if (mConfig.mCurrentAnimation != mAnim) { if (mConfig.currentAnimation != mAnim) {
return; return;
} }
mAnim.start(); mAnim.start();
} }
} }
public static class AnimationConfig extends AnimatorListenerAdapter { private static class AnimationState extends StateAnimationConfig implements AnimatorListener {
public long duration;
public boolean userControlled; private static final StateAnimationConfig DEFAULT = new StateAnimationConfig();
public AnimatorPlaybackController playbackController;
private @AnimationFlags int mAnimFlags = ANIM_ALL_COMPONENTS; public AnimatorPlaybackController playbackController;
private PropertySetter mPropertySetter; public AnimatorSet currentAnimation;
public LauncherState targetState;
private AnimatorSet mCurrentAnimation;
private LauncherState mTargetState;
// Id to keep track of config changes, to tie an animation with the corresponding request // Id to keep track of config changes, to tie an animation with the corresponding request
private int mChangeId = 0; public int changeId = 0;
/** /**
* Cancels the current animation and resets config variables. * Cancels the current animation and resets config variables.
*/ */
public void reset() { public void reset() {
duration = 0; DEFAULT.copyTo(this);
userControlled = false; targetState = null;
mAnimFlags = ANIM_ALL_COMPONENTS;
mPropertySetter = null;
mTargetState = null;
if (playbackController != null) { if (playbackController != null) {
playbackController.getAnimationPlayer().cancel(); playbackController.getAnimationPlayer().cancel();
playbackController.dispatchOnCancel(); playbackController.dispatchOnCancel();
} else if (mCurrentAnimation != null) { } else if (currentAnimation != null) {
mCurrentAnimation.setDuration(0); currentAnimation.setDuration(0);
mCurrentAnimation.cancel(); currentAnimation.cancel();
} }
mCurrentAnimation = null; currentAnimation = null;
playbackController = null; playbackController = null;
mChangeId ++; changeId++;
}
public PropertySetter getPropertySetter(AnimatorSetBuilder builder) {
if (mPropertySetter == null) {
mPropertySetter = duration == 0 ? NO_ANIM_PROPERTY_SETTER
: new AnimatedPropertySetter(duration, builder);
}
return mPropertySetter;
} }
@Override @Override
@@ -629,51 +562,25 @@ public class LauncherStateManager {
if (playbackController != null && playbackController.getTarget() == animation) { if (playbackController != null && playbackController.getTarget() == animation) {
playbackController = null; playbackController = null;
} }
if (mCurrentAnimation == animation) { if (currentAnimation == animation) {
mCurrentAnimation = null; currentAnimation = null;
} }
} }
public void setAnimation(AnimatorSet animation, LauncherState targetState) { public void setAnimation(AnimatorSet animation, LauncherState targetState) {
mCurrentAnimation = animation; currentAnimation = animation;
mTargetState = targetState; this.targetState = targetState;
mCurrentAnimation.addListener(this); currentAnimation.addListener(this);
} }
/** @Override
* @return Whether Overview is scaling as part of this animation. If this is the only public void onAnimationStart(Animator animator) { }
* component (i.e. NON_ATOMIC_COMPONENT isn't included), then this scaling is happening
* atomically, rather than being part of a normal state animation. StateHandlers can use
* this to designate part of their animation that should scale with Overview.
*/
public boolean playAtomicOverviewScaleComponent() {
return hasAnimationFlag(PLAY_ATOMIC_OVERVIEW_SCALE);
}
/** @Override
* @return Whether this animation will play atomically at the same time as a different, public void onAnimationCancel(Animator animator) { }
* user-controlled state transition. StateHandlers, which contribute to both animations, can
* use this to avoid animating the same properties in both animations, since they'd conflict
* with one another.
*/
public boolean onlyPlayAtomicComponent() {
return getAnimComponents() == PLAY_ATOMIC_OVERVIEW_SCALE
|| getAnimComponents() == PLAY_ATOMIC_OVERVIEW_PEEK;
}
/** @Override
* Returns true if the config and any of the provided component flags public void onAnimationRepeat(Animator animator) { }
*/
public boolean hasAnimationFlag(@AnimationFlags int a) {
return (mAnimFlags & a) != 0;
}
/**
* @return Only the flags that determine which animation components to play.
*/
public @AnimationFlags int getAnimComponents() {
return mAnimFlags & ANIM_ALL_COMPONENTS;
}
} }
public interface StateHandler { public interface StateHandler {
@@ -686,8 +593,8 @@ public class LauncherStateManager {
/** /**
* Sets the UI to {@param state} by animating any changes. * Sets the UI to {@param state} by animating any changes.
*/ */
void setStateWithAnimation(LauncherState toState, void setStateWithAnimation(
AnimatorSetBuilder builder, AnimationConfig config); LauncherState toState, StateAnimationConfig config, PendingAnimation animation);
} }
public interface StateListener { public interface StateListener {
+6 -6
View File
@@ -60,11 +60,10 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.Toast; import android.widget.Toast;
import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener; import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener;
import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.accessibility.AccessibleDragListenerAdapter; import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper; import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dot.FolderDotInfo; import com.android.launcher3.dot.FolderDotInfo;
import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.dragndrop.DragController;
@@ -83,6 +82,7 @@ import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.pageindicators.WorkspacePageIndicator; import com.android.launcher3.pageindicators.WorkspacePageIndicator;
import com.android.launcher3.popup.PopupContainerWithArrow; import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider; import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.touch.WorkspaceTouchListener; import com.android.launcher3.touch.WorkspaceTouchListener;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action; import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
@@ -1383,10 +1383,10 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
* Sets the current workspace {@link LauncherState}, then animates the UI * Sets the current workspace {@link LauncherState}, then animates the UI
*/ */
@Override @Override
public void setStateWithAnimation(LauncherState toState, public void setStateWithAnimation(
AnimatorSetBuilder builder, AnimationConfig config) { LauncherState toState, StateAnimationConfig config, PendingAnimation animation) {
StateTransitionListener listener = new StateTransitionListener(toState); StateTransitionListener listener = new StateTransitionListener(toState);
mStateTransitionAnimation.setStateWithAnimation(toState, builder, config); mStateTransitionAnimation.setStateWithAnimation(toState, config, animation);
// Invalidate the pages now, so that we have the visible pages before the // Invalidate the pages now, so that we have the visible pages before the
// animation is started // animation is started
@@ -1399,7 +1399,7 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
stepAnimator.addUpdateListener(listener); stepAnimator.addUpdateListener(listener);
stepAnimator.setDuration(config.duration); stepAnimator.setDuration(config.duration);
stepAnimator.addListener(listener); stepAnimator.addListener(listener);
builder.play(stepAnimator); animation.add(stepAnimator);
} }
public WorkspaceStateTransitionAnimation getStateTransitionAnimation() { public WorkspaceStateTransitionAnimation getStateTransitionAnimation() {
@@ -22,27 +22,27 @@ import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.LauncherState.HOTSEAT_ICONS; import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_SCALE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_TRANSLATE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE;
import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.ZOOM_OUT; import static com.android.launcher3.anim.Interpolators.ZOOM_OUT;
import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER; import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
import static com.android.launcher3.graphics.WorkspaceAndHotseatScrim.SCRIM_PROGRESS; import static com.android.launcher3.graphics.WorkspaceAndHotseatScrim.SCRIM_PROGRESS;
import static com.android.launcher3.graphics.WorkspaceAndHotseatScrim.SYSUI_PROGRESS; import static com.android.launcher3.graphics.WorkspaceAndHotseatScrim.SYSUI_PROGRESS;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_SCALE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_TRANSLATE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCALE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
import android.view.View; import android.view.View;
import android.view.animation.Interpolator; import android.view.animation.Interpolator;
import com.android.launcher3.LauncherState.PageAlphaProvider; import com.android.launcher3.LauncherState.PageAlphaProvider;
import com.android.launcher3.LauncherState.ScaleAndTranslation; import com.android.launcher3.LauncherState.ScaleAndTranslation;
import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.allapps.AllAppsContainerView; import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.PropertySetter; import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.graphics.WorkspaceAndHotseatScrim; import com.android.launcher3.graphics.WorkspaceAndHotseatScrim;
import com.android.launcher3.states.StateAnimationConfig;
/** /**
* Manages the animations between each of the workspace states. * Manages the animations between each of the workspace states.
@@ -60,13 +60,15 @@ public class WorkspaceStateTransitionAnimation {
} }
public void setState(LauncherState toState) { public void setState(LauncherState toState) {
setWorkspaceProperty(toState, NO_ANIM_PROPERTY_SETTER, new AnimatorSetBuilder(), setWorkspaceProperty(toState, NO_ANIM_PROPERTY_SETTER, new StateAnimationConfig());
new AnimationConfig());
} }
public void setStateWithAnimation(LauncherState toState, AnimatorSetBuilder builder, /**
AnimationConfig config) { * @see com.android.launcher3.LauncherStateManager.StateHandler#setStateWithAnimation
setWorkspaceProperty(toState, config.getPropertySetter(builder), builder, config); */
public void setStateWithAnimation(
LauncherState toState, StateAnimationConfig config, PendingAnimation animation) {
setWorkspaceProperty(toState, animation, config);
} }
public float getFinalScale() { public float getFinalScale() {
@@ -77,7 +79,7 @@ public class WorkspaceStateTransitionAnimation {
* Starts a transition animation for the workspace. * Starts a transition animation for the workspace.
*/ */
private void setWorkspaceProperty(LauncherState state, PropertySetter propertySetter, private void setWorkspaceProperty(LauncherState state, PropertySetter propertySetter,
AnimatorSetBuilder builder, AnimationConfig config) { StateAnimationConfig config) {
ScaleAndTranslation scaleAndTranslation = state.getWorkspaceScaleAndTranslation(mLauncher); ScaleAndTranslation scaleAndTranslation = state.getWorkspaceScaleAndTranslation(mLauncher);
ScaleAndTranslation hotseatScaleAndTranslation = state.getHotseatScaleAndTranslation( ScaleAndTranslation hotseatScaleAndTranslation = state.getHotseatScaleAndTranslation(
mLauncher); mLauncher);
@@ -87,11 +89,11 @@ public class WorkspaceStateTransitionAnimation {
final int childCount = mWorkspace.getChildCount(); final int childCount = mWorkspace.getChildCount();
for (int i = 0; i < childCount; i++) { for (int i = 0; i < childCount; i++) {
applyChildState(state, (CellLayout) mWorkspace.getChildAt(i), i, pageAlphaProvider, applyChildState(state, (CellLayout) mWorkspace.getChildAt(i), i, pageAlphaProvider,
propertySetter, builder, config); propertySetter, config);
} }
int elements = state.getVisibleElements(mLauncher); int elements = state.getVisibleElements(mLauncher);
Interpolator fadeInterpolator = builder.getInterpolator(ANIM_WORKSPACE_FADE, Interpolator fadeInterpolator = config.getInterpolator(ANIM_WORKSPACE_FADE,
pageAlphaProvider.interpolator); pageAlphaProvider.interpolator);
boolean playAtomicComponent = config.playAtomicOverviewScaleComponent(); boolean playAtomicComponent = config.playAtomicOverviewScaleComponent();
Hotseat hotseat = mWorkspace.getHotseat(); Hotseat hotseat = mWorkspace.getHotseat();
@@ -99,7 +101,7 @@ public class WorkspaceStateTransitionAnimation {
AllAppsContainerView qsbScaleView = mLauncher.getAppsView(); AllAppsContainerView qsbScaleView = mLauncher.getAppsView();
View qsbView = qsbScaleView.getSearchView(); View qsbView = qsbScaleView.getSearchView();
if (playAtomicComponent) { if (playAtomicComponent) {
Interpolator scaleInterpolator = builder.getInterpolator(ANIM_WORKSPACE_SCALE, ZOOM_OUT); Interpolator scaleInterpolator = config.getInterpolator(ANIM_WORKSPACE_SCALE, ZOOM_OUT);
propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, scaleInterpolator); propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, scaleInterpolator);
if (!hotseat.getRotationMode().isTransposed) { if (!hotseat.getRotationMode().isTransposed) {
@@ -107,7 +109,7 @@ public class WorkspaceStateTransitionAnimation {
setPivotToScaleWithWorkspace(qsbScaleView); setPivotToScaleWithWorkspace(qsbScaleView);
} }
float hotseatScale = hotseatScaleAndTranslation.scale; float hotseatScale = hotseatScaleAndTranslation.scale;
Interpolator hotseatScaleInterpolator = builder.getInterpolator(ANIM_HOTSEAT_SCALE, Interpolator hotseatScaleInterpolator = config.getInterpolator(ANIM_HOTSEAT_SCALE,
scaleInterpolator); scaleInterpolator);
propertySetter.setFloat(hotseat, SCALE_PROPERTY, hotseatScale, propertySetter.setFloat(hotseat, SCALE_PROPERTY, hotseatScale,
hotseatScaleInterpolator); hotseatScaleInterpolator);
@@ -127,13 +129,13 @@ public class WorkspaceStateTransitionAnimation {
Interpolator translationInterpolator = !playAtomicComponent Interpolator translationInterpolator = !playAtomicComponent
? LINEAR ? LINEAR
: builder.getInterpolator(ANIM_WORKSPACE_TRANSLATE, ZOOM_OUT); : config.getInterpolator(ANIM_WORKSPACE_TRANSLATE, ZOOM_OUT);
propertySetter.setFloat(mWorkspace, VIEW_TRANSLATE_X, propertySetter.setFloat(mWorkspace, VIEW_TRANSLATE_X,
scaleAndTranslation.translationX, translationInterpolator); scaleAndTranslation.translationX, translationInterpolator);
propertySetter.setFloat(mWorkspace, VIEW_TRANSLATE_Y, propertySetter.setFloat(mWorkspace, VIEW_TRANSLATE_Y,
scaleAndTranslation.translationY, translationInterpolator); scaleAndTranslation.translationY, translationInterpolator);
Interpolator hotseatTranslationInterpolator = builder.getInterpolator( Interpolator hotseatTranslationInterpolator = config.getInterpolator(
ANIM_HOTSEAT_TRANSLATE, translationInterpolator); ANIM_HOTSEAT_TRANSLATE, translationInterpolator);
propertySetter.setFloat(hotseat, VIEW_TRANSLATE_Y, propertySetter.setFloat(hotseat, VIEW_TRANSLATE_Y,
hotseatScaleAndTranslation.translationY, hotseatTranslationInterpolator); hotseatScaleAndTranslation.translationY, hotseatTranslationInterpolator);
@@ -166,12 +168,12 @@ public class WorkspaceStateTransitionAnimation {
public void applyChildState(LauncherState state, CellLayout cl, int childIndex) { public void applyChildState(LauncherState state, CellLayout cl, int childIndex) {
applyChildState(state, cl, childIndex, state.getWorkspacePageAlphaProvider(mLauncher), applyChildState(state, cl, childIndex, state.getWorkspacePageAlphaProvider(mLauncher),
NO_ANIM_PROPERTY_SETTER, new AnimatorSetBuilder(), new AnimationConfig()); NO_ANIM_PROPERTY_SETTER, new StateAnimationConfig());
} }
private void applyChildState(LauncherState state, CellLayout cl, int childIndex, private void applyChildState(LauncherState state, CellLayout cl, int childIndex,
PageAlphaProvider pageAlphaProvider, PropertySetter propertySetter, PageAlphaProvider pageAlphaProvider, PropertySetter propertySetter,
AnimatorSetBuilder builder, AnimationConfig config) { StateAnimationConfig config) {
float pageAlpha = pageAlphaProvider.getPageAlpha(childIndex); float pageAlpha = pageAlphaProvider.getPageAlpha(childIndex);
int drawableAlpha = Math.round(pageAlpha * (state.hasWorkspacePageBackground ? 255 : 0)); int drawableAlpha = Math.round(pageAlpha * (state.hasWorkspacePageBackground ? 255 : 0));
@@ -181,7 +183,7 @@ public class WorkspaceStateTransitionAnimation {
DRAWABLE_ALPHA, drawableAlpha, ZOOM_OUT); DRAWABLE_ALPHA, drawableAlpha, ZOOM_OUT);
} }
if (config.playAtomicOverviewScaleComponent()) { if (config.playAtomicOverviewScaleComponent()) {
Interpolator fadeInterpolator = builder.getInterpolator(ANIM_WORKSPACE_FADE, Interpolator fadeInterpolator = config.getInterpolator(ANIM_WORKSPACE_FADE,
pageAlphaProvider.interpolator); pageAlphaProvider.interpolator);
propertySetter.setFloat(cl.getShortcutsAndWidgets(), VIEW_ALPHA, propertySetter.setFloat(cl.getShortcutsAndWidgets(), VIEW_ALPHA,
pageAlpha, fadeInterpolator); pageAlpha, fadeInterpolator);
@@ -5,13 +5,13 @@ import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA;
import static com.android.launcher3.LauncherState.APPS_VIEW_ITEM_MASK; import static com.android.launcher3.LauncherState.APPS_VIEW_ITEM_MASK;
import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherState.VERTICAL_SWIPE_INDICATOR; import static com.android.launcher3.LauncherState.VERTICAL_SWIPE_INDICATOR;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_HEADER_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER; import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_HEADER_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
import static com.android.launcher3.util.SystemUiController.UI_STATE_ALL_APPS; import static com.android.launcher3.util.SystemUiController.UI_STATE_ALL_APPS;
import android.animation.Animator; import android.animation.Animator;
@@ -24,12 +24,12 @@ import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener; import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.Launcher; import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.LauncherStateManager.StateHandler; import com.android.launcher3.LauncherStateManager.StateHandler;
import com.android.launcher3.R; import com.android.launcher3.R;
import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.PropertySetter; import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.util.Themes; import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ScrimView; import com.android.launcher3.views.ScrimView;
@@ -112,7 +112,7 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfil
* @param progress value between 0 and 1, 0 shows all apps and 1 shows workspace * @param progress value between 0 and 1, 0 shows all apps and 1 shows workspace
* *
* @see #setState(LauncherState) * @see #setState(LauncherState)
* @see #setStateWithAnimation(LauncherState, AnimatorSetBuilder, AnimationConfig) * @see #setStateWithAnimation(LauncherState, StateAnimationConfig, PendingAnimation)
*/ */
public void setProgress(float progress) { public void setProgress(float progress) {
mProgress = progress; mProgress = progress;
@@ -143,7 +143,7 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfil
@Override @Override
public void setState(LauncherState state) { public void setState(LauncherState state) {
setProgress(state.getVerticalProgress(mLauncher)); setProgress(state.getVerticalProgress(mLauncher));
setAlphas(state, null, new AnimatorSetBuilder()); setAlphas(state, new StateAnimationConfig(), NO_ANIM_PROPERTY_SETTER);
onProgressAnimationEnd(); onProgressAnimationEnd();
} }
@@ -153,7 +153,7 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfil
*/ */
@Override @Override
public void setStateWithAnimation(LauncherState toState, public void setStateWithAnimation(LauncherState toState,
AnimatorSetBuilder builder, AnimationConfig config) { StateAnimationConfig config, PendingAnimation builder) {
float targetProgress = toState.getVerticalProgress(mLauncher); float targetProgress = toState.getVerticalProgress(mLauncher);
if (Float.compare(mProgress, targetProgress) == 0) { if (Float.compare(mProgress, targetProgress) == 0) {
setAlphas(toState, config, builder); setAlphas(toState, config, builder);
@@ -168,14 +168,14 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfil
} }
Interpolator interpolator = config.userControlled ? LINEAR : toState == OVERVIEW Interpolator interpolator = config.userControlled ? LINEAR : toState == OVERVIEW
? builder.getInterpolator(ANIM_OVERVIEW_SCALE, FAST_OUT_SLOW_IN) ? config.getInterpolator(ANIM_OVERVIEW_SCALE, FAST_OUT_SLOW_IN)
: FAST_OUT_SLOW_IN; : FAST_OUT_SLOW_IN;
Animator anim = createSpringAnimation(mProgress, targetProgress); Animator anim = createSpringAnimation(mProgress, targetProgress);
anim.setDuration(config.duration); anim.setDuration(config.duration);
anim.setInterpolator(builder.getInterpolator(ANIM_VERTICAL_PROGRESS, interpolator)); anim.setInterpolator(config.getInterpolator(ANIM_VERTICAL_PROGRESS, interpolator));
anim.addListener(getProgressAnimatorListener()); anim.addListener(getProgressAnimatorListener());
builder.add(anim);
builder.play(anim);
setAlphas(toState, config, builder); setAlphas(toState, config, builder);
} }
@@ -184,21 +184,18 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfil
return ObjectAnimator.ofFloat(this, ALL_APPS_PROGRESS, progressValues); return ObjectAnimator.ofFloat(this, ALL_APPS_PROGRESS, progressValues);
} }
private void setAlphas(LauncherState toState, AnimationConfig config, /**
AnimatorSetBuilder builder) { * Updates the property for the provided state
setAlphas(toState.getVisibleElements(mLauncher), config, builder); */
} public void setAlphas(LauncherState state, StateAnimationConfig config, PropertySetter setter) {
int visibleElements = state.getVisibleElements(mLauncher);
public void setAlphas(int visibleElements, AnimationConfig config, AnimatorSetBuilder builder) {
PropertySetter setter = config == null ? NO_ANIM_PROPERTY_SETTER
: config.getPropertySetter(builder);
boolean hasHeaderExtra = (visibleElements & ALL_APPS_HEADER_EXTRA) != 0; boolean hasHeaderExtra = (visibleElements & ALL_APPS_HEADER_EXTRA) != 0;
boolean hasAllAppsContent = (visibleElements & ALL_APPS_CONTENT) != 0; boolean hasAllAppsContent = (visibleElements & ALL_APPS_CONTENT) != 0;
boolean hasAnyVisibleItem = (visibleElements & APPS_VIEW_ITEM_MASK) != 0; boolean hasAnyVisibleItem = (visibleElements & APPS_VIEW_ITEM_MASK) != 0;
Interpolator allAppsFade = builder.getInterpolator(ANIM_ALL_APPS_FADE, LINEAR); Interpolator allAppsFade = config.getInterpolator(ANIM_ALL_APPS_FADE, LINEAR);
Interpolator headerFade = builder.getInterpolator(ANIM_ALL_APPS_HEADER_FADE, allAppsFade); Interpolator headerFade = config.getInterpolator(ANIM_ALL_APPS_HEADER_FADE, allAppsFade);
setter.setViewAlpha(mAppsView.getContentView(), hasAllAppsContent ? 1 : 0, allAppsFade); setter.setViewAlpha(mAppsView.getContentView(), hasAllAppsContent ? 1 : 0, allAppsFade);
setter.setViewAlpha(mAppsView.getScrollBar(), hasAllAppsContent ? 1 : 0, allAppsFade); setter.setViewAlpha(mAppsView.getScrollBar(), hasAllAppsContent ? 1 : 0, allAppsFade);
mAppsView.getFloatingHeaderView().setContentVisibility(hasHeaderExtra, hasAllAppsContent, mAppsView.getFloatingHeaderView().setContentVisibility(hasHeaderExtra, hasAllAppsContent,
@@ -64,13 +64,6 @@ public class AnimatorPlaybackController implements ValueAnimator.AnimatorUpdateL
return new AnimatorPlaybackController(anim, duration, childAnims); return new AnimatorPlaybackController(anim, duration, childAnims);
} }
public static AnimatorPlaybackController wrap(PendingAnimation anim, long duration) {
/**
* TODO: use {@link AnimatorSet#setCurrentPlayTime(long)} once b/68382377 is fixed.
*/
return new AnimatorPlaybackController(anim.anim, duration, anim.animHolders);
}
private static final FloatProperty<ValueAnimator> CURRENT_PLAY_TIME = private static final FloatProperty<ValueAnimator> CURRENT_PLAY_TIME =
new FloatProperty<ValueAnimator>("current-play-time") { new FloatProperty<ValueAnimator>("current-play-time") {
@Override @Override
@@ -96,8 +89,8 @@ public class AnimatorPlaybackController implements ValueAnimator.AnimatorUpdateL
protected boolean mTargetCancelled = false; protected boolean mTargetCancelled = false;
protected Runnable mOnCancelRunnable; protected Runnable mOnCancelRunnable;
private AnimatorPlaybackController( /** package private */
AnimatorSet anim, long duration, ArrayList<Holder> childAnims) { AnimatorPlaybackController(AnimatorSet anim, long duration, ArrayList<Holder> childAnims) {
mAnim = anim; mAnim = anim;
mDuration = duration; mDuration = duration;
@@ -1,65 +0,0 @@
/*
* Copyright (C) 2017 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.anim;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.util.SparseArray;
import android.view.animation.Interpolator;
import java.util.ArrayList;
/**
* Utility class for building animator set
*/
public class AnimatorSetBuilder {
public static final int ANIM_VERTICAL_PROGRESS = 0;
public static final int ANIM_WORKSPACE_SCALE = 1;
public static final int ANIM_WORKSPACE_TRANSLATE = 2;
public static final int ANIM_WORKSPACE_FADE = 3;
public static final int ANIM_HOTSEAT_SCALE = 4;
public static final int ANIM_HOTSEAT_TRANSLATE = 5;
public static final int ANIM_OVERVIEW_SCALE = 6;
public static final int ANIM_OVERVIEW_TRANSLATE_X = 7;
public static final int ANIM_OVERVIEW_TRANSLATE_Y = 8;
public static final int ANIM_OVERVIEW_FADE = 9;
public static final int ANIM_ALL_APPS_FADE = 10;
public static final int ANIM_OVERVIEW_SCRIM_FADE = 11;
public static final int ANIM_ALL_APPS_HEADER_FADE = 12; // e.g. predictions
protected final ArrayList<Animator> mAnims = new ArrayList<>();
private final SparseArray<Interpolator> mInterpolators = new SparseArray<>();
public void play(Animator anim) {
mAnims.add(anim);
}
public AnimatorSet build() {
AnimatorSet anim = new AnimatorSet();
anim.playTogether(mAnims);
return anim;
}
public Interpolator getInterpolator(int animId, Interpolator fallback) {
return mInterpolators.get(animId, fallback);
}
public void setInterpolator(int animId, Interpolator interpolator) {
mInterpolators.put(animId, interpolator);
}
}
@@ -19,9 +19,12 @@ import static com.android.launcher3.anim.AnimatorPlaybackController.addAnimation
import android.animation.Animator; import android.animation.Animator;
import android.animation.AnimatorSet; import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator; import android.animation.TimeInterpolator;
import android.annotation.TargetApi; import android.animation.ValueAnimator;
import android.os.Build; import android.util.FloatProperty;
import android.util.IntProperty;
import android.view.View;
import com.android.launcher3.anim.AnimatorPlaybackController.Holder; import com.android.launcher3.anim.AnimatorPlaybackController.Holder;
@@ -37,14 +40,24 @@ import java.util.function.Consumer;
* *
* TODO: Find a better name * TODO: Find a better name
*/ */
@TargetApi(Build.VERSION_CODES.O) public class PendingAnimation implements PropertySetter {
public class PendingAnimation {
private final ArrayList<Consumer<EndState>> mEndListeners = new ArrayList<>(); private final ArrayList<Consumer<EndState>> mEndListeners = new ArrayList<>();
/** package private **/ private final ArrayList<Holder> mAnimHolders = new ArrayList<>();
final AnimatorSet anim = new AnimatorSet(); private final AnimatorSet mAnim;
final ArrayList<Holder> animHolders = new ArrayList<>(); private final long mDuration;
private ValueAnimator mProgressAnimator;
public PendingAnimation(long duration) {
this(duration, new AnimatorSet());
}
public PendingAnimation(long duration, AnimatorSet targetSet) {
mDuration = duration;
mAnim = targetSet;
}
/** /**
* Utility method to sent an interpolator on an animation and add it to the list * Utility method to sent an interpolator on an animation and add it to the list
@@ -63,8 +76,8 @@ public class PendingAnimation {
} }
public void add(Animator a, SpringProperty springProperty) { public void add(Animator a, SpringProperty springProperty) {
anim.play(a); mAnim.play(a);
addAnimationHoldersRecur(a, springProperty, animHolders); addAnimationHoldersRecur(a, springProperty, mAnimHolders);
} }
public void finish(boolean isSuccess, int logAction) { public void finish(boolean isSuccess, int logAction) {
@@ -74,6 +87,67 @@ public class PendingAnimation {
mEndListeners.clear(); mEndListeners.clear();
} }
@Override
public void setViewAlpha(View view, float alpha, TimeInterpolator interpolator) {
if (view == null || view.getAlpha() == alpha) {
return;
}
ObjectAnimator anim = ObjectAnimator.ofFloat(view, View.ALPHA, alpha);
anim.addListener(new AlphaUpdateListener(view));
anim.setDuration(mDuration).setInterpolator(interpolator);
add(anim);
}
@Override
public <T> void setFloat(T target, FloatProperty<T> property, float value,
TimeInterpolator interpolator) {
if (property.get(target) == value) {
return;
}
Animator anim = ObjectAnimator.ofFloat(target, property, value);
anim.setDuration(mDuration).setInterpolator(interpolator);
add(anim);
}
@Override
public <T> void setInt(T target, IntProperty<T> property, int value,
TimeInterpolator interpolator) {
if (property.get(target) == value) {
return;
}
Animator anim = ObjectAnimator.ofInt(target, property, value);
anim.setDuration(mDuration).setInterpolator(interpolator);
add(anim);
}
/**
* Adds a callback to be run on every frame of the animation
*/
public void addOnFrameCallback(Runnable runnable) {
if (mProgressAnimator == null) {
mProgressAnimator = ValueAnimator.ofFloat(0, 1).setDuration(mDuration);
add(mProgressAnimator);
}
mProgressAnimator.addUpdateListener(anim -> runnable.run());
}
public AnimatorSet getAnim() {
return mAnim;
}
/**
* Creates a controller for this animation
*/
public AnimatorPlaybackController createPlaybackController() {
return new AnimatorPlaybackController(mAnim, mDuration, mAnimHolders);
}
/**
* Add a listener of receiving the end state.
* Note that the listeners are called as a result of calling {@link #finish(boolean, int)}
* and not automatically
*/
public void addEndListener(Consumer<EndState> listener) { public void addEndListener(Consumer<EndState> listener) {
mEndListeners.add(listener); mEndListeners.add(listener);
} }
@@ -16,8 +16,6 @@
package com.android.launcher3.anim; package com.android.launcher3.anim;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator; import android.animation.TimeInterpolator;
import android.util.FloatProperty; import android.util.FloatProperty;
import android.util.IntProperty; import android.util.IntProperty;
@@ -26,68 +24,35 @@ import android.view.View;
/** /**
* Utility class for setting a property with or without animation * Utility class for setting a property with or without animation
*/ */
public class PropertySetter { public interface PropertySetter {
public static final PropertySetter NO_ANIM_PROPERTY_SETTER = new PropertySetter(); PropertySetter NO_ANIM_PROPERTY_SETTER = new PropertySetter() { };
public void setViewAlpha(View view, float alpha, TimeInterpolator interpolator) { /**
* Sets the view alpha using the provided interpolator.
* Unlike {@link #setFloat}, this also updates the visibility of the view as alpha changes
* between zero and non-zero.
*/
default void setViewAlpha(View view, float alpha, TimeInterpolator interpolator) {
if (view != null) { if (view != null) {
view.setAlpha(alpha); view.setAlpha(alpha);
AlphaUpdateListener.updateVisibility(view); AlphaUpdateListener.updateVisibility(view);
} }
} }
public <T> void setFloat(T target, FloatProperty<T> property, float value, /**
* Updates the float property of the target using the provided interpolator
*/
default <T> void setFloat(T target, FloatProperty<T> property, float value,
TimeInterpolator interpolator) { TimeInterpolator interpolator) {
property.setValue(target, value); property.setValue(target, value);
} }
public <T> void setInt(T target, IntProperty<T> property, int value, /**
* Updates the int property of the target using the provided interpolator
*/
default <T> void setInt(T target, IntProperty<T> property, int value,
TimeInterpolator interpolator) { TimeInterpolator interpolator) {
property.setValue(target, value); property.setValue(target, value);
} }
public static class AnimatedPropertySetter extends PropertySetter {
private final long mDuration;
private final AnimatorSetBuilder mStateAnimator;
public AnimatedPropertySetter(long duration, AnimatorSetBuilder builder) {
mDuration = duration;
mStateAnimator = builder;
}
@Override
public void setViewAlpha(View view, float alpha, TimeInterpolator interpolator) {
if (view == null || view.getAlpha() == alpha) {
return;
}
ObjectAnimator anim = ObjectAnimator.ofFloat(view, View.ALPHA, alpha);
anim.addListener(new AlphaUpdateListener(view));
anim.setDuration(mDuration).setInterpolator(interpolator);
mStateAnimator.play(anim);
}
@Override
public <T> void setFloat(T target, FloatProperty<T> property, float value,
TimeInterpolator interpolator) {
if (property.get(target) == value) {
return;
}
Animator anim = ObjectAnimator.ofFloat(target, property, value);
anim.setDuration(mDuration).setInterpolator(interpolator);
mStateAnimator.play(anim);
}
@Override
public <T> void setInt(T target, IntProperty<T> property, int value,
TimeInterpolator interpolator) {
if (property.get(target) == value) {
return;
}
Animator anim = ObjectAnimator.ofInt(target, property, value);
anim.setDuration(mDuration).setInterpolator(interpolator);
mStateAnimator.play(anim);
}
}
} }
@@ -100,11 +100,11 @@ public final class FeatureFlags {
"Suggests folder names instead of blank text."); "Suggests folder names instead of blank text.");
public static final BooleanFlag APP_SEARCH_IMPROVEMENTS = new DeviceFlag( public static final BooleanFlag APP_SEARCH_IMPROVEMENTS = new DeviceFlag(
"APP_SEARCH_IMPROVEMENTS", false, "APP_SEARCH_IMPROVEMENTS", true,
"Adds localized title and keyword search and ranking"); "Adds localized title and keyword search and ranking");
public static final BooleanFlag ENABLE_PREDICTION_DISMISS = new DeviceFlag( public static final BooleanFlag ENABLE_PREDICTION_DISMISS = getDebugFlag(
"ENABLE_PREDICTION_DISMISS", true, "Allow option to dimiss apps from predicted list"); "ENABLE_PREDICTION_DISMISS", false, "Allow option to dimiss apps from predicted list");
public static final BooleanFlag ENABLE_QUICK_CAPTURE_GESTURE = getDebugFlag( public static final BooleanFlag ENABLE_QUICK_CAPTURE_GESTURE = getDebugFlag(
"ENABLE_QUICK_CAPTURE_GESTURE", true, "Swipe from right to left to quick capture"); "ENABLE_QUICK_CAPTURE_GESTURE", true, "Swipe from right to left to quick capture");
@@ -117,11 +117,11 @@ public final class FeatureFlags {
"ASSISTANT_GIVES_LAUNCHER_FOCUS", false, "ASSISTANT_GIVES_LAUNCHER_FOCUS", false,
"Allow Launcher to handle nav bar gestures while Assistant is running over it"); "Allow Launcher to handle nav bar gestures while Assistant is running over it");
public static final BooleanFlag ENABLE_HYBRID_HOTSEAT = new DeviceFlag( public static final BooleanFlag ENABLE_HYBRID_HOTSEAT = getDebugFlag(
"ENABLE_HYBRID_HOTSEAT", false, "Fill gaps in hotseat with predicted apps"); "ENABLE_HYBRID_HOTSEAT", false, "Fill gaps in hotseat with predicted apps");
public static final BooleanFlag HOTSEAT_MIGRATE_NEW_PAGE = new DeviceFlag( public static final BooleanFlag HOTSEAT_MIGRATE_NEW_PAGE = getDebugFlag(
"HOTSEAT_MIGRATE_NEW_PAGE", true, "HOTSEAT_MIGRATE_NEW_PAGE", false,
"Migrates hotseat to a new workspace page instead of same page"); "Migrates hotseat to a new workspace page instead of same page");
public static final BooleanFlag ENABLE_DEEP_SHORTCUT_ICON_CACHE = getDebugFlag( public static final BooleanFlag ENABLE_DEEP_SHORTCUT_ICON_CACHE = getDebugFlag(
@@ -131,13 +131,18 @@ public final class FeatureFlags {
"MULTI_DB_GRID_MIRATION_ALGO", false, "Use the multi-db grid migration algorithm"); "MULTI_DB_GRID_MIRATION_ALGO", false, "Use the multi-db grid migration algorithm");
public static final BooleanFlag ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER = getDebugFlag( public static final BooleanFlag ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER = getDebugFlag(
"ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER", true, "ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER", true, "Show launcher preview in grid picker");
"Show launcher preview in grid picker");
public static final BooleanFlag USE_SURFACE_VIEW_FOR_GRID_PREVIEW = getDebugFlag(
"USE_SURFACE_VIEW_FOR_GRID_PREVIEW", false, "Use surface view for grid preview");
public static final BooleanFlag ENABLE_OVERVIEW_ACTIONS = getDebugFlag( public static final BooleanFlag ENABLE_OVERVIEW_ACTIONS = getDebugFlag(
"ENABLE_OVERVIEW_ACTIONS", true, "Show app actions instead of the shelf in Overview." "ENABLE_OVERVIEW_ACTIONS", true, "Show app actions instead of the shelf in Overview."
+ " As part of this decoupling, also distinguish swipe up from nav bar vs above it."); + " As part of this decoupling, also distinguish swipe up from nav bar vs above it.");
public static final BooleanFlag ENABLE_SELECT_MODE = getDebugFlag(
"ENABLE_SELECT_MODE", true, "Show Select Mode button in Overview Actions");
public static final BooleanFlag ENABLE_DATABASE_RESTORE = getDebugFlag( public static final BooleanFlag ENABLE_DATABASE_RESTORE = getDebugFlag(
"ENABLE_DATABASE_RESTORE", true, "ENABLE_DATABASE_RESTORE", true,
"Enable database restore when new restore session is created"); "Enable database restore when new restore session is created");
@@ -1,5 +1,6 @@
package com.android.launcher3.graphics; package com.android.launcher3.graphics;
import static com.android.launcher3.config.FeatureFlags.USE_SURFACE_VIEW_FOR_GRID_PREVIEW;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import android.content.ContentProvider; import android.content.ContentProvider;
@@ -19,6 +20,7 @@ import android.util.Xml;
import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.InvariantDeviceProfile.GridOption; import com.android.launcher3.InvariantDeviceProfile.GridOption;
import com.android.launcher3.R; import com.android.launcher3.R;
import com.android.launcher3.uioverrides.PreviewSurfaceRenderer;
import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserException;
@@ -62,6 +64,11 @@ public class GridOptionsProvider extends ContentProvider {
private static final String KEY_PREVIEW = "preview"; private static final String KEY_PREVIEW = "preview";
private static final String MIME_TYPE_PNG = "image/png"; private static final String MIME_TYPE_PNG = "image/png";
private static final String METHOD_GET_PREVIEW = "get_preview";
private static final String METADATA_KEY_PREVIEW_VERSION = "preview_version";
public static final PipeDataWriter<Future<Bitmap>> BITMAP_WRITER = public static final PipeDataWriter<Future<Bitmap>> BITMAP_WRITER =
new PipeDataWriter<Future<Bitmap>>() { new PipeDataWriter<Future<Bitmap>>() {
@Override @Override
@@ -98,6 +105,10 @@ public class GridOptionsProvider extends ContentProvider {
.add(KEY_IS_DEFAULT, idp.numColumns == gridOption.numColumns .add(KEY_IS_DEFAULT, idp.numColumns == gridOption.numColumns
&& idp.numRows == gridOption.numRows); && idp.numRows == gridOption.numRows);
} }
Bundle metadata = new Bundle();
metadata.putString(METADATA_KEY_PREVIEW_VERSION,
USE_SURFACE_VIEW_FOR_GRID_PREVIEW.get() ? "V2" : "V1");
cursor.setExtras(metadata);
return cursor; return cursor;
} }
@@ -188,4 +199,14 @@ public class GridOptionsProvider extends ContentProvider {
throw new FileNotFoundException(e.getMessage()); throw new FileNotFoundException(e.getMessage());
} }
} }
@Override
public Bundle call(String method, String arg, Bundle extras) {
if (!METHOD_GET_PREVIEW.equals(method)) {
return null;
}
PreviewSurfaceRenderer.render(getContext(), extras);
return null;
}
} }
@@ -262,6 +262,13 @@ public class LauncherPreviewRenderer implements Callable<Bitmap> {
}); });
} }
/** Populate preview and render it. */
public View getRenderedView() {
MainThreadRenderer renderer = new MainThreadRenderer(mContext);
renderer.populate();
return renderer.mRootView;
}
private class MainThreadRenderer extends ContextThemeWrapper private class MainThreadRenderer extends ContextThemeWrapper
implements ActivityContext, WorkspaceLayoutManager, LayoutInflater.Factory2 { implements ActivityContext, WorkspaceLayoutManager, LayoutInflater.Factory2 {
@@ -388,7 +395,7 @@ public class LauncherPreviewRenderer implements Callable<Bitmap> {
} }
} }
private void renderScreenShot(Canvas canvas) { private void populate() {
if (ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER.get()) { if (ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER.get()) {
boolean needsToMigrate = needsToMigrate(mContext, mIdp); boolean needsToMigrate = needsToMigrate(mContext, mIdp);
boolean success = false; boolean success = false;
@@ -499,7 +506,10 @@ public class LauncherPreviewRenderer implements Callable<Bitmap> {
measureView(mRootView, mDp.widthPx, mDp.heightPx); measureView(mRootView, mDp.widthPx, mDp.heightPx);
// Additional measure for views which use auto text size API // Additional measure for views which use auto text size API
measureView(mRootView, mDp.widthPx, mDp.heightPx); measureView(mRootView, mDp.widthPx, mDp.heightPx);
}
private void renderScreenShot(Canvas canvas) {
populate();
mRootView.draw(canvas); mRootView.draw(canvas);
dispatchVisibilityAggregated(mRootView, false); dispatchVisibilityAggregated(mRootView, false);
} }
@@ -143,7 +143,7 @@ public class UserEventDispatcher implements ResourceBasedOverride {
fillIntentInfo(itemTarget, intent, userHandle); fillIntentInfo(itemTarget, intent, userHandle);
} }
LauncherEvent event = newLauncherEvent(action, targets); LauncherEvent event = newLauncherEvent(action, targets);
ItemInfo info = (ItemInfo) v.getTag(); ItemInfo info = v == null ? null : (ItemInfo) v.getTag();
if (info != null && Utilities.IS_DEBUG_DEVICE && FeatureFlags.ENABLE_HYBRID_HOTSEAT.get()) { if (info != null && Utilities.IS_DEBUG_DEVICE && FeatureFlags.ENABLE_HYBRID_HOTSEAT.get()) {
FileLog.d(TAG, "appLaunch: packageName:" + info.getTargetComponent().getPackageName() FileLog.d(TAG, "appLaunch: packageName:" + info.getTargetComponent().getPackageName()
+ ",isWorkApp:" + (info.user != null && !Process.myUserHandle().equals( + ",isWorkApp:" + (info.user != null && !Process.myUserHandle().equals(
@@ -936,8 +936,8 @@ public class GridSizeMigrationTask {
boolean dbChanged = false; boolean dbChanged = false;
if (migrateForPreview) { if (migrateForPreview) {
copyTable(transaction.getDb(), Favorites.TABLE_NAME, Favorites.PREVIEW_TABLE_NAME, copyTable(transaction.getDb(), Favorites.TABLE_NAME, transaction.getDb(),
context); Favorites.PREVIEW_TABLE_NAME, context);
} }
GridBackupTable backupTable = new GridBackupTable(context, transaction.getDb(), GridBackupTable backupTable = new GridBackupTable(context, transaction.getDb(),
@@ -950,10 +950,11 @@ public class GridSizeMigrationTask {
HashSet<String> validPackages = getValidPackages(context); HashSet<String> validPackages = getValidPackages(context);
// Hotseat. // Hotseat.
if (srcHotseatCount != idp.numHotseatIcons) { if (srcHotseatCount != idp.numHotseatIcons
// Migrate hotseat. && new GridSizeMigrationTask(context, transaction.getDb(), validPackages,
dbChanged = new GridSizeMigrationTask(context, transaction.getDb(), validPackages, migrateForPreview, srcHotseatCount,
migrateForPreview, srcHotseatCount, idp.numHotseatIcons).migrateHotseat(); idp.numHotseatIcons).migrateHotseat()) {
dbChanged = true;
} }
// Grid size // Grid size
@@ -16,9 +16,46 @@
package com.android.launcher3.model; package com.android.launcher3.model;
import static com.android.launcher3.Utilities.getPointString;
import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Point;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.Utilities;
import com.android.launcher3.graphics.LauncherPreviewRenderer;
import com.android.launcher3.pm.InstallSessionHelper;
import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
import com.android.launcher3.util.GridOccupancy;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.widget.WidgetManagerHelper;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
/** /**
* This class takes care of shrinking the workspace (by maximum of one row and one column), as a * This class takes care of shrinking the workspace (by maximum of one row and one column), as a
@@ -26,14 +63,63 @@ import com.android.launcher3.InvariantDeviceProfile;
*/ */
public class GridSizeMigrationTaskV2 { public class GridSizeMigrationTaskV2 {
private GridSizeMigrationTaskV2(Context context) { public static final String KEY_MIGRATION_SRC_WORKSPACE_SIZE = "migration_src_workspace_size";
public static final String KEY_MIGRATION_SRC_HOTSEAT_COUNT = "migration_src_hotseat_count";
private static final String TAG = "GridSizeMigrationTaskV2";
private static final boolean DEBUG = true;
private final Context mContext;
private final SQLiteDatabase mDb;
private final DbReader mSrcReader;
private final DbReader mDestReader;
private final List<DbEntry> mHotseatItems;
private final List<DbEntry> mWorkspaceItems;
private final List<DbEntry> mHotseatDiff;
private final List<DbEntry> mWorkspaceDiff;
private final int mDestHotseatSize;
private final int mTrgX, mTrgY;
@VisibleForTesting
protected GridSizeMigrationTaskV2(Context context, SQLiteDatabase db, DbReader srcReader,
DbReader destReader, int destHotseatSize, Point targetSize) {
mContext = context;
mDb = db;
mSrcReader = srcReader;
mDestReader = destReader;
mHotseatItems = destReader.loadHotseatEntries();
mWorkspaceItems = destReader.loadAllWorkspaceEntries();
mHotseatDiff = calcDiff(mSrcReader.loadHotseatEntries(), mHotseatItems);
mWorkspaceDiff = calcDiff(mSrcReader.loadAllWorkspaceEntries(), mWorkspaceItems);
mDestHotseatSize = destHotseatSize;
mTrgX = targetSize.x;
mTrgY = targetSize.y;
}
/**
* Check given a new IDP, if migration is necessary.
*/
public static boolean needsToMigrate(Context context, InvariantDeviceProfile idp) {
SharedPreferences prefs = Utilities.getPrefs(context);
String gridSizeString = getPointString(idp.numColumns, idp.numRows);
return !gridSizeString.equals(prefs.getString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, ""))
|| idp.numHotseatIcons != prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT,
idp.numHotseatIcons);
} }
/** See {@link #migrateGridIfNeeded(Context, InvariantDeviceProfile)} */ /** See {@link #migrateGridIfNeeded(Context, InvariantDeviceProfile)} */
public static boolean migrateGridIfNeeded(Context context) { public static boolean migrateGridIfNeeded(Context context) {
// To be implemented. if (context instanceof LauncherPreviewRenderer.PreviewContext) {
return true; return true;
}
return migrateGridIfNeeded(context, null);
} }
/** /**
@@ -43,7 +129,608 @@ public class GridSizeMigrationTaskV2 {
* @return false if the migration failed. * @return false if the migration failed.
*/ */
public static boolean migrateGridIfNeeded(Context context, InvariantDeviceProfile idp) { public static boolean migrateGridIfNeeded(Context context, InvariantDeviceProfile idp) {
// To be implemented. boolean migrateForPreview = idp != null;
if (!migrateForPreview) {
idp = LauncherAppState.getIDP(context);
}
if (!needsToMigrate(context, idp)) {
return true;
}
SharedPreferences prefs = Utilities.getPrefs(context);
String gridSizeString = getPointString(idp.numColumns, idp.numRows);
if (gridSizeString.equals(prefs.getString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, ""))
&& idp.numHotseatIcons == prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT,
idp.numHotseatIcons)) {
// Skip if workspace and hotseat sizes have not changed.
return true;
}
HashSet<String> validPackages = getValidPackages(context);
int srcHotseatCount = prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numHotseatIcons);
if (!LauncherSettings.Settings.call(
context.getContentResolver(),
LauncherSettings.Settings.METHOD_UPDATE_CURRENT_OPEN_HELPER).getBoolean(
LauncherSettings.Settings.EXTRA_VALUE)) {
return false;
}
long migrationStartTime = System.currentTimeMillis();
try (SQLiteTransaction t = (SQLiteTransaction) LauncherSettings.Settings.call(
context.getContentResolver(),
LauncherSettings.Settings.METHOD_NEW_TRANSACTION).getBinder(
LauncherSettings.Settings.EXTRA_VALUE)) {
DbReader srcReader = new DbReader(t.getDb(), LauncherSettings.Favorites.TMP_TABLE,
context, validPackages, srcHotseatCount);
DbReader destReader = new DbReader(t.getDb(), LauncherSettings.Favorites.TABLE_NAME,
context, validPackages, idp.numHotseatIcons);
Point targetSize = new Point(idp.numColumns, idp.numRows);
GridSizeMigrationTaskV2 task = new GridSizeMigrationTaskV2(context, t.getDb(),
srcReader, destReader, idp.numHotseatIcons, targetSize);
task.migrate();
dropTable(t.getDb(), LauncherSettings.Favorites.TMP_TABLE);
t.commit();
return true;
} catch (Exception e) {
Log.e(TAG, "Error during grid migration", e);
return false;
} finally {
Log.v(TAG, "Workspace migration completed in "
+ (System.currentTimeMillis() - migrationStartTime));
// Save current configuration, so that the migration does not run again.
prefs.edit()
.putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString)
.putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numHotseatIcons)
.apply();
}
}
@VisibleForTesting
protected boolean migrate() {
if (mHotseatDiff.isEmpty() && mWorkspaceDiff.isEmpty()) {
return false;
}
// Migrate hotseat
HotseatPlacementSolution hotseatSolution = new HotseatPlacementSolution(mDb, mSrcReader,
mContext, mDestHotseatSize, mHotseatItems, mHotseatDiff);
hotseatSolution.find();
// Sort the items by the reading order.
Collections.sort(mWorkspaceDiff);
// Migrate workspace.
for (int screenId = 0; screenId <= mDestReader.mLastScreenId; screenId++) {
if (DEBUG) {
Log.d(TAG, "Migrating " + screenId);
}
List<DbEntry> entries = mDestReader.loadWorkspaceEntries(screenId);
GridPlacementSolution workspaceSolution = new GridPlacementSolution(mDb, mSrcReader,
mContext, entries, screenId, mTrgX, mTrgY, mWorkspaceDiff);
workspaceSolution.find();
if (mWorkspaceDiff.isEmpty()) {
break;
}
}
int screenId = mDestReader.mLastScreenId + 1;
while (!mWorkspaceDiff.isEmpty()) {
GridPlacementSolution workspaceSolution = new GridPlacementSolution(mDb, mSrcReader,
mContext, new ArrayList<>(), screenId, mTrgX, mTrgY, mWorkspaceDiff);
workspaceSolution.find();
screenId++;
}
return true; return true;
} }
/** Return what's in the src but not in the dest */
private static List<DbEntry> calcDiff(List<DbEntry> src, List<DbEntry> dest) {
Set<String> destSet = dest.parallelStream().map(DbEntry::getIntentStr).collect(
Collectors.toSet());
List<DbEntry> diff = new ArrayList<>();
for (DbEntry entry : src) {
if (!destSet.contains(entry.mIntent)) {
diff.add(entry);
}
}
return diff;
}
private static void insertEntryInDb(SQLiteDatabase db, Context context,
ArrayList<DbEntry> entriesFromSrcDb, DbEntry entry) {
int id = -1;
switch (entry.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: {
for (DbEntry e : entriesFromSrcDb) {
if (TextUtils.equals(e.mIntent, entry.mIntent)) {
id = e.id;
}
}
break;
}
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: {
for (DbEntry e : entriesFromSrcDb) {
if (e.mFolderItems.size() == entry.mFolderItems.size()
&& e.mFolderItems.containsAll(entry.mFolderItems)) {
id = e.id;
}
}
break;
}
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET: {
for (DbEntry e : entriesFromSrcDb) {
if (TextUtils.equals(e.mProvider, entry.mProvider)) {
id = e.id;
break;
}
}
break;
}
default:
return;
}
Cursor c = db.query(LauncherSettings.Favorites.TMP_TABLE, null,
LauncherSettings.Favorites._ID + " = '" + id + "'", null, null, null, null);
while (c.moveToNext()) {
ContentValues values = new ContentValues();
DatabaseUtils.cursorRowToContentValues(c, values);
entry.updateContentValues(values);
values.put(LauncherSettings.Favorites._ID,
LauncherSettings.Settings.call(context.getContentResolver(),
LauncherSettings.Settings.METHOD_NEW_ITEM_ID).getInt(
LauncherSettings.Settings.EXTRA_VALUE));
db.insert(LauncherSettings.Favorites.TABLE_NAME, null, values);
}
c.close();
}
private static void removeEntryFromDb(SQLiteDatabase db, IntArray entryId) {
db.delete(LauncherSettings.Favorites.TABLE_NAME, Utilities.createDbSelectionQuery(
LauncherSettings.Favorites._ID, entryId), null);
}
private static HashSet<String> getValidPackages(Context context) {
// Initialize list of valid packages. This contain all the packages which are already on
// the device and packages which are being installed. Any item which doesn't belong to
// this set is removed.
// Since the loader removes such items anyway, removing these items here doesn't cause
// any extra data loss and gives us more free space on the grid for better migration.
HashSet<String> validPackages = new HashSet<>();
for (PackageInfo info : context.getPackageManager()
.getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES)) {
validPackages.add(info.packageName);
}
InstallSessionHelper.INSTANCE.get(context)
.getActiveSessions().keySet()
.forEach(packageUserKey -> validPackages.add(packageUserKey.mPackageName));
return validPackages;
}
protected static class GridPlacementSolution {
private final SQLiteDatabase mDb;
private final DbReader mSrcReader;
private final Context mContext;
private final GridOccupancy mOccupied;
private final int mScreenId;
private final int mTrgX;
private final int mTrgY;
private final List<DbEntry> mItemsToPlace;
private int mNextStartX;
private int mNextStartY;
GridPlacementSolution(SQLiteDatabase db, DbReader srcReader, Context context,
List<DbEntry> placedWorkspaceItems, int screenId, int trgX,
int trgY, List<DbEntry> itemsToPlace) {
mDb = db;
mSrcReader = srcReader;
mContext = context;
mOccupied = new GridOccupancy(trgX, trgY);
mScreenId = screenId;
mTrgX = trgX;
mTrgY = trgY;
mNextStartX = 0;
mNextStartY = mTrgY - 1;
for (DbEntry entry : placedWorkspaceItems) {
mOccupied.markCells(entry, true);
}
mItemsToPlace = itemsToPlace;
}
public void find() {
Iterator<DbEntry> iterator = mItemsToPlace.iterator();
while (iterator.hasNext()) {
final DbEntry entry = iterator.next();
if (entry.minSpanX > mTrgX || entry.minSpanY > mTrgY) {
iterator.remove();
continue;
}
if (findPlacement(entry)) {
insertEntryInDb(mDb, mContext, mSrcReader.mWorkspaceEntries, entry);
iterator.remove();
}
}
}
private boolean findPlacement(DbEntry entry) {
for (int y = mNextStartY; y >= 0; y--) {
for (int x = mNextStartX; x < mTrgX; x++) {
boolean fits = mOccupied.isRegionVacant(x, y, entry.spanX, entry.spanY);
boolean minFits = mOccupied.isRegionVacant(x, y, entry.minSpanX,
entry.minSpanY);
if (minFits) {
entry.spanX = entry.minSpanX;
entry.spanY = entry.minSpanY;
}
if (fits || minFits) {
entry.screenId = mScreenId;
entry.cellX = x;
entry.cellY = y;
mOccupied.markCells(entry, true);
mNextStartX = x + entry.spanX;
mNextStartY = y;
return true;
}
}
}
return false;
}
}
protected static class HotseatPlacementSolution {
private final SQLiteDatabase mDb;
private final DbReader mSrcReader;
private final Context mContext;
private final HotseatOccupancy mOccupied;
private final List<DbEntry> mItemsToPlace;
HotseatPlacementSolution(SQLiteDatabase db, DbReader srcReader, Context context,
int hotseatSize, List<DbEntry> placedHotseatItems, List<DbEntry> itemsToPlace) {
mDb = db;
mSrcReader = srcReader;
mContext = context;
mOccupied = new HotseatOccupancy(hotseatSize);
for (DbEntry entry : placedHotseatItems) {
mOccupied.markCells(entry, true);
}
mItemsToPlace = itemsToPlace;
}
public void find() {
for (int i = 0; i < mOccupied.mCells.length; i++) {
if (!mOccupied.mCells[i] && !mItemsToPlace.isEmpty()) {
DbEntry entry = mItemsToPlace.remove(0);
entry.screenId = i;
// These values does not affect the item position, but we should set them
// to something other than -1.
entry.cellX = i;
entry.cellY = 0;
insertEntryInDb(mDb, mContext, mSrcReader.mHotseatEntries, entry);
mOccupied.markCells(entry, true);
}
}
}
private class HotseatOccupancy {
private final boolean[] mCells;
private HotseatOccupancy(int hotseatSize) {
mCells = new boolean[hotseatSize];
}
private void markCells(ItemInfo item, boolean value) {
mCells[item.screenId] = value;
}
}
}
protected static class DbReader {
private final SQLiteDatabase mDb;
private final String mTableName;
private final Context mContext;
private final HashSet<String> mValidPackages;
private final int mHotseatSize;
private int mLastScreenId = -1;
private final ArrayList<DbEntry> mHotseatEntries = new ArrayList<>();
private final ArrayList<DbEntry> mWorkspaceEntries = new ArrayList<>();
DbReader(SQLiteDatabase db, String tableName, Context context,
HashSet<String> validPackages, int hotseatSize) {
mDb = db;
mTableName = tableName;
mContext = context;
mValidPackages = validPackages;
mHotseatSize = hotseatSize;
}
protected ArrayList<DbEntry> loadHotseatEntries() {
Cursor c = queryWorkspace(
new String[]{
LauncherSettings.Favorites._ID, // 0
LauncherSettings.Favorites.ITEM_TYPE, // 1
LauncherSettings.Favorites.INTENT, // 2
LauncherSettings.Favorites.SCREEN}, // 3
LauncherSettings.Favorites.CONTAINER + " = "
+ LauncherSettings.Favorites.CONTAINER_HOTSEAT);
final int indexId = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
final int indexItemType = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
final int indexIntent = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
final int indexScreen = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
IntArray entriesToRemove = new IntArray();
while (c.moveToNext()) {
DbEntry entry = new DbEntry();
entry.id = c.getInt(indexId);
entry.itemType = c.getInt(indexItemType);
entry.screenId = c.getInt(indexScreen);
if (entry.screenId >= mHotseatSize) {
entriesToRemove.add(entry.id);
continue;
}
try {
// calculate weight
switch (entry.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: {
entry.mIntent = c.getString(indexIntent);
verifyIntent(c.getString(indexIntent));
break;
}
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: {
int total = getFolderItemsCount(entry);
if (total == 0) {
throw new Exception("Folder is empty");
}
break;
}
default:
throw new Exception("Invalid item type");
}
} catch (Exception e) {
if (DEBUG) {
Log.d(TAG, "Removing item " + entry.id, e);
}
entriesToRemove.add(entry.id);
continue;
}
mHotseatEntries.add(entry);
}
removeEntryFromDb(mDb, entriesToRemove);
c.close();
return mHotseatEntries;
}
protected ArrayList<DbEntry> loadAllWorkspaceEntries() {
Cursor c = queryWorkspace(
new String[]{
LauncherSettings.Favorites._ID, // 0
LauncherSettings.Favorites.ITEM_TYPE, // 1
LauncherSettings.Favorites.SCREEN, // 2
LauncherSettings.Favorites.CELLX, // 3
LauncherSettings.Favorites.CELLY, // 4
LauncherSettings.Favorites.SPANX, // 5
LauncherSettings.Favorites.SPANY, // 6
LauncherSettings.Favorites.INTENT, // 7
LauncherSettings.Favorites.APPWIDGET_PROVIDER, // 8
LauncherSettings.Favorites.APPWIDGET_ID}, // 9
LauncherSettings.Favorites.CONTAINER + " = "
+ LauncherSettings.Favorites.CONTAINER_DESKTOP);
return loadWorkspaceEntries(c);
}
protected ArrayList<DbEntry> loadWorkspaceEntries(int screen) {
Cursor c = queryWorkspace(
new String[]{
LauncherSettings.Favorites._ID, // 0
LauncherSettings.Favorites.ITEM_TYPE, // 1
LauncherSettings.Favorites.SCREEN, // 2
LauncherSettings.Favorites.CELLX, // 3
LauncherSettings.Favorites.CELLY, // 4
LauncherSettings.Favorites.SPANX, // 5
LauncherSettings.Favorites.SPANY, // 6
LauncherSettings.Favorites.INTENT, // 7
LauncherSettings.Favorites.APPWIDGET_PROVIDER, // 8
LauncherSettings.Favorites.APPWIDGET_ID}, // 9
LauncherSettings.Favorites.CONTAINER + " = "
+ LauncherSettings.Favorites.CONTAINER_DESKTOP
+ " AND " + LauncherSettings.Favorites.SCREEN + " = " + screen);
return loadWorkspaceEntries(c);
}
private ArrayList<DbEntry> loadWorkspaceEntries(Cursor c) {
final int indexId = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
final int indexItemType = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
final int indexScreen = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
final int indexCellX = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
final int indexCellY = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
final int indexSpanX = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX);
final int indexSpanY = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY);
final int indexIntent = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
final int indexAppWidgetProvider = c.getColumnIndexOrThrow(
LauncherSettings.Favorites.APPWIDGET_PROVIDER);
final int indexAppWidgetId = c.getColumnIndexOrThrow(
LauncherSettings.Favorites.APPWIDGET_ID);
IntArray entriesToRemove = new IntArray();
WidgetManagerHelper widgetManagerHelper = new WidgetManagerHelper(mContext);
while (c.moveToNext()) {
DbEntry entry = new DbEntry();
entry.id = c.getInt(indexId);
entry.itemType = c.getInt(indexItemType);
entry.screenId = c.getInt(indexScreen);
mLastScreenId = Math.max(mLastScreenId, entry.screenId);
entry.cellX = c.getInt(indexCellX);
entry.cellY = c.getInt(indexCellY);
entry.spanX = c.getInt(indexSpanX);
entry.spanY = c.getInt(indexSpanY);
try {
// calculate weight
switch (entry.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: {
entry.mIntent = c.getString(indexIntent);
verifyIntent(entry.mIntent);
break;
}
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: {
entry.mProvider = c.getString(indexAppWidgetProvider);
ComponentName cn = ComponentName.unflattenFromString(entry.mProvider);
verifyPackage(cn.getPackageName());
int widgetId = c.getInt(indexAppWidgetId);
LauncherAppWidgetProviderInfo pInfo =
widgetManagerHelper.getLauncherAppWidgetInfo(widgetId);
Point spans = null;
if (pInfo != null) {
spans = pInfo.getMinSpans();
}
if (spans != null) {
entry.minSpanX = spans.x > 0 ? spans.x : entry.spanX;
entry.minSpanY = spans.y > 0 ? spans.y : entry.spanY;
} else {
// Assume that the widget be resized down to 2x2
entry.minSpanX = entry.minSpanY = 2;
}
break;
}
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: {
int total = getFolderItemsCount(entry);
if (total == 0) {
throw new Exception("Folder is empty");
}
break;
}
default:
throw new Exception("Invalid item type");
}
} catch (Exception e) {
if (DEBUG) {
Log.d(TAG, "Removing item " + entry.id, e);
}
entriesToRemove.add(entry.id);
continue;
}
mWorkspaceEntries.add(entry);
}
removeEntryFromDb(mDb, entriesToRemove);
c.close();
return mWorkspaceEntries;
}
private int getFolderItemsCount(DbEntry entry) {
Cursor c = queryWorkspace(
new String[]{LauncherSettings.Favorites._ID, LauncherSettings.Favorites.INTENT},
LauncherSettings.Favorites.CONTAINER + " = " + entry.id);
int total = 0;
while (c.moveToNext()) {
try {
String intent = c.getString(1);
verifyIntent(intent);
total++;
entry.mFolderItems.add(intent);
} catch (Exception e) {
removeEntryFromDb(mDb, IntArray.wrap(c.getInt(0)));
}
}
c.close();
return total;
}
private Cursor queryWorkspace(String[] columns, String where) {
return mDb.query(mTableName, columns, where, null, null, null, null);
}
/** Verifies if the mIntent should be restored. */
private void verifyIntent(String intentStr)
throws Exception {
Intent intent = Intent.parseUri(intentStr, 0);
if (intent.getComponent() != null) {
verifyPackage(intent.getComponent().getPackageName());
} else if (intent.getPackage() != null) {
// Only verify package if the component was null.
verifyPackage(intent.getPackage());
}
}
/** Verifies if the package should be restored */
private void verifyPackage(String packageName)
throws Exception {
if (!mValidPackages.contains(packageName)) {
// TODO(b/151468819): Handle promise app icon restoration during grid migration.
throw new Exception("Package not available");
}
}
}
protected static class DbEntry extends ItemInfo implements Comparable<DbEntry> {
private String mIntent;
private String mProvider;
private Set<String> mFolderItems = new HashSet<>();
/** Comparator according to the reading order */
@Override
public int compareTo(DbEntry another) {
if (screenId != another.screenId) {
return Integer.compare(screenId, another.screenId);
}
if (cellY != another.cellY) {
return -Integer.compare(cellY, another.cellY);
}
return Integer.compare(cellX, another.cellX);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DbEntry entry = (DbEntry) o;
return Objects.equals(mIntent, entry.mIntent);
}
@Override
public int hashCode() {
return Objects.hash(mIntent);
}
public void updateContentValues(ContentValues values) {
values.put(LauncherSettings.Favorites.SCREEN, screenId);
values.put(LauncherSettings.Favorites.CELLX, cellX);
values.put(LauncherSettings.Favorites.CELLY, cellY);
values.put(LauncherSettings.Favorites.SPANX, spanX);
values.put(LauncherSettings.Favorites.SPANY, spanY);
}
public String getIntentStr() {
return mIntent;
}
}
} }
@@ -118,13 +118,20 @@ public class LauncherDbUtils {
db.execSQL("DROP TABLE IF EXISTS " + tableName); db.execSQL("DROP TABLE IF EXISTS " + tableName);
} }
/** Copy from table to the to table. */ /** Copy fromTable in fromDb to toTable in toDb. */
public static void copyTable(SQLiteDatabase db, String from, String to, Context context) { public static void copyTable(SQLiteDatabase fromDb, String fromTable, SQLiteDatabase toDb,
String toTable, Context context) {
long userSerial = UserCache.INSTANCE.get(context).getSerialNumberForUser( long userSerial = UserCache.INSTANCE.get(context).getSerialNumberForUser(
Process.myUserHandle()); Process.myUserHandle());
dropTable(db, to); dropTable(toDb, toTable);
Favorites.addTableToDb(db, userSerial, false, to); Favorites.addTableToDb(toDb, userSerial, false, toTable);
db.execSQL("INSERT INTO " + to + " SELECT * FROM " + from); if (fromDb != toDb) {
toDb.execSQL("ATTACH DATABASE '" + fromDb.getPath() + "' AS from_db");
toDb.execSQL(
"INSERT INTO " + toTable + " SELECT * FROM from_db." + fromTable);
} else {
toDb.execSQL("INSERT INTO " + toTable + " SELECT * FROM " + fromTable);
}
} }
/** /**
@@ -0,0 +1,155 @@
/*
* Copyright (C) 2020 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.states;
import android.view.animation.Interpolator;
import androidx.annotation.IntDef;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Utility class for building animator set
*/
public class StateAnimationConfig {
// We separate the state animations into "atomic" and "non-atomic" components. The atomic
// components may be run atomically - that is, all at once, instead of user-controlled. However,
// atomic components are not restricted to this purpose; they can be user-controlled alongside
// non atomic components as well. Note that each gesture model has exactly one atomic component,
// PLAY_ATOMIC_OVERVIEW_SCALE *or* PLAY_ATOMIC_OVERVIEW_PEEK.
@IntDef(flag = true, value = {
PLAY_NON_ATOMIC,
PLAY_ATOMIC_OVERVIEW_SCALE,
PLAY_ATOMIC_OVERVIEW_PEEK,
SKIP_OVERVIEW,
})
@Retention(RetentionPolicy.SOURCE)
public @interface AnimationFlags {}
public static final int PLAY_NON_ATOMIC = 1 << 0;
public static final int PLAY_ATOMIC_OVERVIEW_SCALE = 1 << 1;
public static final int PLAY_ATOMIC_OVERVIEW_PEEK = 1 << 2;
public static final int SKIP_OVERVIEW = 1 << 3;
public long duration;
public boolean userControlled;
public @AnimationFlags int animFlags = ANIM_ALL_COMPONENTS;
public static final int ANIM_ALL_COMPONENTS = PLAY_NON_ATOMIC | PLAY_ATOMIC_OVERVIEW_SCALE
| PLAY_ATOMIC_OVERVIEW_PEEK;
// Various types of animation state transition
@IntDef(value = {
ANIM_VERTICAL_PROGRESS,
ANIM_WORKSPACE_SCALE,
ANIM_WORKSPACE_TRANSLATE,
ANIM_WORKSPACE_FADE,
ANIM_HOTSEAT_SCALE,
ANIM_HOTSEAT_TRANSLATE,
ANIM_OVERVIEW_SCALE,
ANIM_OVERVIEW_TRANSLATE_X,
ANIM_OVERVIEW_TRANSLATE_Y,
ANIM_OVERVIEW_FADE,
ANIM_ALL_APPS_FADE,
ANIM_OVERVIEW_SCRIM_FADE,
ANIM_ALL_APPS_HEADER_FADE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface AnimType {}
public static final int ANIM_VERTICAL_PROGRESS = 0;
public static final int ANIM_WORKSPACE_SCALE = 1;
public static final int ANIM_WORKSPACE_TRANSLATE = 2;
public static final int ANIM_WORKSPACE_FADE = 3;
public static final int ANIM_HOTSEAT_SCALE = 4;
public static final int ANIM_HOTSEAT_TRANSLATE = 5;
public static final int ANIM_OVERVIEW_SCALE = 6;
public static final int ANIM_OVERVIEW_TRANSLATE_X = 7;
public static final int ANIM_OVERVIEW_TRANSLATE_Y = 8;
public static final int ANIM_OVERVIEW_FADE = 9;
public static final int ANIM_ALL_APPS_FADE = 10;
public static final int ANIM_OVERVIEW_SCRIM_FADE = 11;
public static final int ANIM_ALL_APPS_HEADER_FADE = 12; // e.g. predictions
private static final int ANIM_TYPES_COUNT = 13;
private final Interpolator[] mInterpolators = new Interpolator[ANIM_TYPES_COUNT];
public StateAnimationConfig() { }
/**
* Copies the config to target
*/
public void copyTo(StateAnimationConfig target) {
target.duration = duration;
target.animFlags = animFlags;
target.userControlled = userControlled;
for (int i = 0; i < ANIM_TYPES_COUNT; i++) {
target.mInterpolators[i] = mInterpolators[i];
}
}
/**
* Returns the interpolator set for animId or fallback if nothing is set
*
* @see #setInterpolator(int, Interpolator)
*/
public Interpolator getInterpolator(@AnimType int animId, Interpolator fallback) {
return mInterpolators[animId] == null ? fallback : mInterpolators[animId];
}
/**
* Sets an interpolator for a given animation type
*/
public void setInterpolator(@AnimType int animId, Interpolator interpolator) {
mInterpolators[animId] = interpolator;
}
/**
* @return Whether Overview is scaling as part of this animation. If this is the only
* component (i.e. NON_ATOMIC_COMPONENT isn't included), then this scaling is happening
* atomically, rather than being part of a normal state animation. StateHandlers can use
* this to designate part of their animation that should scale with Overview.
*/
public boolean playAtomicOverviewScaleComponent() {
return hasAnimationFlag(StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_SCALE);
}
/**
* @return Whether this animation will play atomically at the same time as a different,
* user-controlled state transition. StateHandlers, which contribute to both animations, can
* use this to avoid animating the same properties in both animations, since they'd conflict
* with one another.
*/
public boolean onlyPlayAtomicComponent() {
return getAnimComponents() == StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_SCALE
|| getAnimComponents() == StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_PEEK;
}
/**
* Returns true if the config and any of the provided component flags
*/
public boolean hasAnimationFlag(@AnimationFlags int a) {
return (animFlags & a) != 0;
}
/**
* @return Only the flags that determine which animation components to play.
*/
public @AnimationFlags int getAnimComponents() {
return animFlags & StateAnimationConfig.ANIM_ALL_COMPONENTS;
}
}
@@ -19,11 +19,11 @@ import static com.android.launcher3.LauncherAnimUtils.MIN_PROGRESS_TO_ALL_APPS;
import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherStateManager.ANIM_ALL_COMPONENTS;
import static com.android.launcher3.LauncherStateManager.PLAY_ATOMIC_OVERVIEW_SCALE;
import static com.android.launcher3.LauncherStateManager.PLAY_NON_ATOMIC;
import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity; import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS; import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_COMPONENTS;
import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_SCALE;
import static com.android.launcher3.states.StateAnimationConfig.PLAY_NON_ATOMIC;
import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs; import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
import android.animation.Animator; import android.animation.Animator;
@@ -37,12 +37,12 @@ import android.view.MotionEvent;
import com.android.launcher3.Launcher; import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAnimUtils; import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager.AnimationFlags;
import com.android.launcher3.Utilities; import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
@@ -358,14 +358,18 @@ public abstract class AbstractStateChangeTouchController
private AnimatorSet createAtomicAnimForState(LauncherState fromState, LauncherState targetState, private AnimatorSet createAtomicAnimForState(LauncherState fromState, LauncherState targetState,
long duration) { long duration) {
AnimatorSetBuilder builder = getAnimatorSetBuilderForStates(fromState, targetState); StateAnimationConfig config = getConfigForStates(fromState, targetState);
return mLauncher.getStateManager().createAtomicAnimation(fromState, targetState, builder, config.animFlags = PLAY_ATOMIC_OVERVIEW_SCALE;
PLAY_ATOMIC_OVERVIEW_SCALE, duration); config.duration = duration;
return mLauncher.getStateManager().createAtomicAnimation(fromState, targetState, config);
} }
protected AnimatorSetBuilder getAnimatorSetBuilderForStates(LauncherState fromState, /**
LauncherState toState) { * Returns animation config for state transition between provided states
return new AnimatorSetBuilder(); */
protected StateAnimationConfig getConfigForStates(
LauncherState fromState, LauncherState toState) {
return new StateAnimationConfig();
} }
@Override @Override
@@ -23,7 +23,7 @@ import android.view.MotionEvent;
import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Launcher; import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager.AnimationFlags; import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
/** /**
@@ -16,8 +16,8 @@
package com.android.launcher3.widget; package com.android.launcher3.widget;
import android.animation.Animator; import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder; import android.animation.PropertyValuesHolder;
import android.content.Context; import android.content.Context;
import android.graphics.Rect; import android.graphics.Rect;
@@ -28,16 +28,15 @@ import android.view.Gravity;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.animation.Interpolator;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.Nullable;
import com.android.launcher3.Insettable; import com.android.launcher3.Insettable;
import com.android.launcher3.ItemInfo; import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R; import com.android.launcher3.R;
import com.android.launcher3.ResourceUtils; import com.android.launcher3.ResourceUtils;
import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.model.WidgetItem; import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.util.PackageUserKey;
@@ -156,7 +155,7 @@ public class WidgetsBottomSheet extends BaseWidgetSheet implements Insettable {
setupNavBarColor(); setupNavBarColor();
mOpenCloseAnimator.setValues( mOpenCloseAnimator.setValues(
PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED)); PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
mOpenCloseAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); mOpenCloseAnimator.setInterpolator(FAST_OUT_SLOW_IN);
mOpenCloseAnimator.start(); mOpenCloseAnimator.start();
} }
@@ -191,9 +190,9 @@ public class WidgetsBottomSheet extends BaseWidgetSheet implements Insettable {
mIsOpen ? R.string.widgets_list : R.string.widgets_list_closed)); mIsOpen ? R.string.widgets_list : R.string.widgets_list_closed));
} }
@Nullable
@Override @Override
public Animator createHintCloseAnim(float distanceToMove) { public void addHintCloseAnim(
return ObjectAnimator.ofInt(this, PADDING_BOTTOM, (int) (distanceToMove + mInsets.bottom)); float distanceToMove, Interpolator interpolator, PendingAnimation target) {
target.setInt(this, PADDING_BOTTOM, (int) (distanceToMove + mInsets.bottom), interpolator);
} }
} }
@@ -15,12 +15,11 @@
*/ */
package com.android.launcher3.widget; package com.android.launcher3.widget;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL; import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL;
import android.animation.Animator; import android.animation.Animator;
import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder; import android.animation.PropertyValuesHolder;
import android.content.Context; import android.content.Context;
import android.graphics.Rect; import android.graphics.Rect;
@@ -30,8 +29,8 @@ import android.view.LayoutInflater;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.animation.AnimationUtils; import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import com.android.launcher3.Insettable; import com.android.launcher3.Insettable;
@@ -39,6 +38,7 @@ import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener; import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener;
import com.android.launcher3.R; import com.android.launcher3.R;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.compat.AccessibilityManagerCompat; import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.views.RecyclerViewFastScroller; import com.android.launcher3.views.RecyclerViewFastScroller;
import com.android.launcher3.views.TopRoundedCornerView; import com.android.launcher3.views.TopRoundedCornerView;
@@ -242,13 +242,11 @@ public class WidgetsFullSheet extends BaseWidgetSheet
return mAdapter.getItemCount(); return mAdapter.getItemCount();
} }
@Nullable
@Override @Override
public Animator createHintCloseAnim(float distanceToMove) { public void addHintCloseAnim(
AnimatorSet anim = new AnimatorSet(); float distanceToMove, Interpolator interpolator, PendingAnimation target) {
anim.play(ObjectAnimator.ofFloat(mRecyclerView, TRANSLATION_Y, -distanceToMove)); target.setFloat(mRecyclerView, VIEW_TRANSLATE_Y, -distanceToMove, interpolator);
anim.play(ObjectAnimator.ofFloat(mRecyclerView, ALPHA, 0.5f)); target.setViewAlpha(mRecyclerView, 0.5f, interpolator);
return anim;
} }
@Override @Override
@@ -19,10 +19,12 @@ package com.android.launcher3.uioverrides;
import android.util.IntProperty; import android.util.IntProperty;
import android.view.View; import android.view.View;
import com.android.launcher3.Launcher; import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager; import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.states.StateAnimationConfig;
/** /**
* Controls the blur, for the Launcher surface only. * Controls the blur, for the Launcher surface only.
@@ -52,6 +54,6 @@ public class BackgroundBlurController implements LauncherStateManager.StateHandl
public void setState(LauncherState toState) {} public void setState(LauncherState toState) {}
@Override @Override
public void setStateWithAnimation(LauncherState toState, AnimatorSetBuilder builder, public void setStateWithAnimation(LauncherState toState, StateAnimationConfig config,
LauncherStateManager.AnimationConfig config) {} PendingAnimation animation) { }
} }
@@ -0,0 +1,27 @@
/*
* Copyright (C) 2020 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;
import android.content.Context;
import android.os.Bundle;
/** Render preview using surface view. */
public class PreviewSurfaceRenderer {
/** Handle a received surface view request. */
public static void render(Context context, Bundle bundle) { }
}