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
229 lines
8.8 KiB
Java
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 */);
|
|
}
|
|
}
|
|
}
|