blob: 9eda2f9d4d88019a952825b1e4de536473f294a2 [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.quickstep.util;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.Nullable;
import androidx.dynamicanimation.animation.SpringForce;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAnimUtils.ViewProgressProperty;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.anim.SpringObjectAnimator;
import java.util.ArrayList;
import java.util.List;
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.anim.Interpolators.LINEAR;
/**
* Creates an animation where all the workspace items are moved into their final location,
* staggered row by row from the bottom up.
* This is used in conjunction with the swipe up to home animation.
*/
public class StaggeredWorkspaceAnim {
private static final int APP_CLOSE_ROW_START_DELAY_MS = 10;
private static final int ALPHA_DURATION_MS = 250;
private static final float MAX_VELOCITY_PX_PER_S = 22f;
private static final float DAMPING_RATIO = 0.7f;
private static final float STIFFNESS = 150f;
private final float mVelocity;
private final float mSpringTransY;
private final View mViewToIgnore;
private final List<Animator> mAnimators = new ArrayList<>();
/**
* @param floatingViewOriginalView The FloatingIconView's original view.
*/
public StaggeredWorkspaceAnim(Launcher launcher, @Nullable View floatingViewOriginalView,
float velocity) {
mVelocity = velocity;
// We ignore this view since it's visibility and position is controlled by
// the FloatingIconView.
mViewToIgnore = floatingViewOriginalView;
// Scale the translationY based on the initial velocity to better sync the workspace items
// with the floating view.
float transFactor = 0.2f + 0.9f * Math.abs(velocity) / MAX_VELOCITY_PX_PER_S;
mSpringTransY = transFactor * launcher.getResources()
.getDimensionPixelSize(R.dimen.swipe_up_max_workspace_trans_y);
DeviceProfile grid = launcher.getDeviceProfile();
ShortcutAndWidgetContainer currentPage = ((CellLayout) launcher.getWorkspace()
.getChildAt(launcher.getWorkspace().getCurrentPage()))
.getShortcutsAndWidgets();
// Hotseat and QSB takes up two additional rows.
int totalRows = grid.inv.numRows + (grid.isVerticalBarLayout() ? 0 : 2);
// Set up springs on workspace items.
for (int i = currentPage.getChildCount() - 1; i >= 0; i--) {
View child = currentPage.getChildAt(i);
CellLayout.LayoutParams lp = ((CellLayout.LayoutParams) child.getLayoutParams());
addStaggeredAnimationForView(child, lp.cellY + lp.cellVSpan, totalRows);
}
// Set up springs for the hotseat and qsb.
if (grid.isVerticalBarLayout()) {
ViewGroup hotseat = (ViewGroup) launcher.getHotseat().getChildAt(0);
for (int i = hotseat.getChildCount() - 1; i >= 0; i--) {
View child = hotseat.getChildAt(i);
CellLayout.LayoutParams lp = ((CellLayout.LayoutParams) child.getLayoutParams());
addStaggeredAnimationForView(child, lp.cellY + 1, totalRows);
}
} else {
View hotseat = launcher.getHotseat().getChildAt(0);
addStaggeredAnimationForView(hotseat, grid.inv.numRows + 1, totalRows);
View qsb = launcher.findViewById(R.id.search_container_all_apps);
addStaggeredAnimationForView(qsb, grid.inv.numRows + 2, totalRows);
}
addWorkspaceScrimAnimationForState(launcher, BACKGROUND_APP, 0);
addWorkspaceScrimAnimationForState(launcher, NORMAL, ALPHA_DURATION_MS);
}
/**
* Starts the animation.
*/
public void start() {
for (Animator a : mAnimators) {
if (a instanceof SpringObjectAnimator) {
((SpringObjectAnimator) a).startSpring(1f, mVelocity, null);
} else {
a.start();
}
}
}
/**
* Adds an alpha/trans animator for {@param v}, with a start delay based on the view's row.
*
* @param v A view on the workspace.
* @param row The bottom-most row that contains the view.
* @param totalRows Total number of rows.
*/
private void addStaggeredAnimationForView(View v, int row, int totalRows) {
if (v == mViewToIgnore) {
return;
}
// Invert the rows, because we stagger starting from the bottom of the screen.
int invertedRow = totalRows - row;
// Add 1 to the inverted row so that the bottom most row has a start delay.
long startDelay = (long) ((invertedRow + 1) * APP_CLOSE_ROW_START_DELAY_MS);
v.setTranslationY(mSpringTransY);
SpringObjectAnimator springTransY = new SpringObjectAnimator<>(
new ViewProgressProperty(v, View.TRANSLATION_Y), "staggeredSpringTransY", 1f,
DAMPING_RATIO, STIFFNESS, mSpringTransY, 0);
springTransY.setStartDelay(startDelay);
mAnimators.add(springTransY);
v.setAlpha(0);
ObjectAnimator alpha = ObjectAnimator.ofFloat(v, View.ALPHA, 0f, 1f);
alpha.setInterpolator(LINEAR);
alpha.setDuration(ALPHA_DURATION_MS);
alpha.setStartDelay(startDelay);
mAnimators.add(alpha);
}
private void addWorkspaceScrimAnimationForState(Launcher launcher, LauncherState state,
long duration) {
AnimatorSetBuilder scrimAnimBuilder = new AnimatorSetBuilder();
AnimationConfig scrimAnimConfig = new AnimationConfig();
scrimAnimConfig.duration = duration;
PropertySetter scrimPropertySetter = scrimAnimConfig.getPropertySetter(scrimAnimBuilder);
launcher.getWorkspace().getStateTransitionAnimation().setScrim(scrimPropertySetter, state);
mAnimators.add(scrimAnimBuilder.build());
}
}