Files
Flexera-Launcher/src/com/android/launcher3/allapps/AllAppsTransitionController.java
T
Samuel Fufa acebd1f69a [Search][Motion] Introduce header protection in AllApps
ScrimView exposes ScrimDrawingController that allow FloatingHeaderView to observe AllApps scroll progress and draw header protection on Scrim. In addition, search box independently adopts header protection background to ensure proper clipping behavior.

Test: local
preview: https://drive.google.com/file/d/1_E577AAJ0LBg0zrKJQSEJK9GQnrtx7uv/view?usp=sharing&resourcekey=0-MTxjlB3xWyJ3qPvr4qMdig
Bug: 184946772
Change-Id: I64c0f4f50d26c475d31542148a15c7c145588d3f
2021-05-07 11:15:05 -05:00

229 lines
8.8 KiB
Java

/*
* Copyright (C) 2015 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.allapps;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.ALL_APPS_CONTENT;
import static com.android.launcher3.anim.Interpolators.ACCEL_0_75;
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_2;
import static com.android.launcher3.anim.Interpolators.LINEAR;
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.util.SystemUiController.UI_STATE_ALLAPPS;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.util.FloatProperty;
import android.view.View;
import android.view.animation.Interpolator;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.statemanager.StateManager.StateHandler;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.views.ScrimView;
/**
* Handles AllApps view transition.
* 1) Slides all apps view using direct manipulation
* 2) When finger is released, animate to either top or bottom accordingly.
* <p/>
* Algorithm:
* If release velocity > THRES1, snap according to the direction of movement.
* If release velocity < THRES1, snap according to either top or bottom depending on whether it's
* closer to top or closer to the page indicator.
*/
public class AllAppsTransitionController
implements StateHandler<LauncherState>, OnDeviceProfileChangeListener {
public static final FloatProperty<AllAppsTransitionController> ALL_APPS_PROGRESS =
new FloatProperty<AllAppsTransitionController>("allAppsProgress") {
@Override
public Float get(AllAppsTransitionController controller) {
return controller.mProgress;
}
@Override
public void setValue(AllAppsTransitionController controller, float progress) {
controller.setProgress(progress);
}
};
private AllAppsContainerView mAppsView;
private final Launcher mLauncher;
private boolean mIsVerticalLayout;
// Animation in this class is controlled by a single variable {@link mProgress}.
// Visually, it represents top y coordinate of the all apps container if multiplied with
// {@link mShiftRange}.
// When {@link mProgress} is 0, all apps container is pulled up.
// When {@link mProgress} is 1, all apps container is pulled down.
private float mShiftRange; // changes depending on the orientation
private float mProgress; // [0, 1], mShiftRange * mProgress = shiftCurrent
private float mScrollRangeDelta = 0;
private ScrimView mScrimView;
public AllAppsTransitionController(Launcher l) {
mLauncher = l;
mShiftRange = mLauncher.getDeviceProfile().heightPx;
mProgress = 1f;
mIsVerticalLayout = mLauncher.getDeviceProfile().isVerticalBarLayout();
mLauncher.addOnDeviceProfileChangeListener(this);
}
public float getShiftRange() {
return mShiftRange;
}
@Override
public void onDeviceProfileChanged(DeviceProfile dp) {
mIsVerticalLayout = dp.isVerticalBarLayout();
setScrollRangeDelta(mScrollRangeDelta);
if (mIsVerticalLayout) {
mLauncher.getHotseat().setTranslationY(0);
mLauncher.getWorkspace().getPageIndicator().setTranslationY(0);
}
}
/**
* Note this method should not be called outside this class. This is public because it is used
* in xml-based animations which also handle updating the appropriate UI.
*
* @param progress value between 0 and 1, 0 shows all apps and 1 shows workspace
* @see #setState(LauncherState)
* @see #setStateWithAnimation(LauncherState, StateAnimationConfig, PendingAnimation)
*/
public void setProgress(float progress) {
mProgress = progress;
mAppsView.setTranslationY(mProgress * mShiftRange);
}
public float getProgress() {
return mProgress;
}
/**
* Sets the vertical transition progress to {@param state} and updates all the dependent UI
* accordingly.
*/
@Override
public void setState(LauncherState state) {
setProgress(state.getVerticalProgress(mLauncher));
setAlphas(state, new StateAnimationConfig(), NO_ANIM_PROPERTY_SETTER);
onProgressAnimationEnd();
}
/**
* Creates an animation which updates the vertical transition progress and updates all the
* dependent UI using various animation events
*/
@Override
public void setStateWithAnimation(LauncherState toState,
StateAnimationConfig config, PendingAnimation builder) {
float targetProgress = toState.getVerticalProgress(mLauncher);
if (Float.compare(mProgress, targetProgress) == 0) {
setAlphas(toState, config, builder);
// Fail fast
onProgressAnimationEnd();
return;
}
Interpolator interpolator = toState.equals(ALL_APPS)
? (config.userControlled ? ACCEL_2 : ACCEL_0_75) :
(config.userControlled ? DEACCEL_2 : DEACCEL);
Animator anim = createSpringAnimation(mProgress, targetProgress);
anim.setInterpolator(config.getInterpolator(ANIM_VERTICAL_PROGRESS, interpolator));
anim.addListener(getProgressAnimatorListener());
builder.add(anim);
setAlphas(toState, config, builder);
}
public Animator createSpringAnimation(float... progressValues) {
return ObjectAnimator.ofFloat(this, ALL_APPS_PROGRESS, progressValues);
}
/**
* Updates the property for the provided state
*/
public void setAlphas(LauncherState state, StateAnimationConfig config, PropertySetter setter) {
int visibleElements = state.getVisibleElements(mLauncher);
boolean hasAllAppsContent = (visibleElements & ALL_APPS_CONTENT) != 0;
Interpolator allAppsFade = config.getInterpolator(ANIM_ALL_APPS_FADE, LINEAR);
setter.setViewAlpha(mAppsView, hasAllAppsContent ? 1 : 0, allAppsFade);
boolean shouldProtectHeader =
ALL_APPS == state || mLauncher.getStateManager().getState() == ALL_APPS;
mScrimView.setDrawingController(shouldProtectHeader ? mAppsView : null);
}
public AnimatorListenerAdapter getProgressAnimatorListener() {
return AnimationSuccessListener.forRunnable(this::onProgressAnimationEnd);
}
/**
* see Launcher#setupViews
*/
public void setupViews(ScrimView scrimView, AllAppsContainerView appsView) {
mScrimView = scrimView;
mAppsView = appsView;
if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && Utilities.ATLEAST_R) {
mLauncher.getSystemUiController().updateUiState(UI_STATE_ALLAPPS,
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
}
mAppsView.setScrimView(scrimView);
}
/**
* Updates the total scroll range but does not update the UI.
*/
public void setScrollRangeDelta(float delta) {
mScrollRangeDelta = delta;
mShiftRange = mLauncher.getDeviceProfile().heightPx - mScrollRangeDelta;
}
/**
* Set the final view states based on the progress.
* TODO: This logic should go in {@link LauncherState}
*/
private void onProgressAnimationEnd() {
if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) return;
if (Float.compare(mProgress, 1f) == 0) {
mAppsView.reset(false /* animate */);
}
}
}