Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ee9aff9b authored by Samuel Fufa's avatar Samuel Fufa
Browse files

[Search][Motion] Normal<->AllApps transition revamp

Preview video attached to BR

Key changes:

- SearchUiManager#getScrollRangeDelta is removed. AllApps initial vertical offset is now controlled by deviceProfile.allAppsOpenVerticalTranslate.
- ShelfScrimView is renamed to AllAppsScrimView. Scrim no longer does vertical translate with AllApps open.  Shelf/overview related code removed.
- Scrim and AllApps content transition are animated with different interpolators. Scrim color is a blend of accentColor and allAppsScrim with proportion determined by progress*TINT_DECAY_MULTIPLIER

Bug: 183001675
Test: Manual
Change-Id: Id6bde7ee65c55c83c2973fe41f458b9364d39ecd
parent 9efbf6f3
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -13,7 +13,7 @@
     See the License for the specific language governing permissions and
     limitations under the License.
-->
<com.android.quickstep.views.ShelfScrimView
<com.android.quickstep.views.AllAppsScrimView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
+15 −19
Original line number Diff line number Diff line
@@ -275,17 +275,14 @@ public class NoButtonNavbarToOverviewTouchController extends PortraitStatesTouch
            LauncherState fromState, LauncherState toState) {
        if (fromState == NORMAL && toState == ALL_APPS) {
            StateAnimationConfig builder = new StateAnimationConfig();
            // Fade in prediction icons quickly, then rest of all apps after reaching overview.
            float progressToReachOverview = NORMAL.getVerticalProgress(mLauncher)
                    - OVERVIEW.getVerticalProgress(mLauncher);
            builder.setInterpolator(ANIM_ALL_APPS_HEADER_FADE, Interpolators.clampToProgress(
                    ACCEL,
                    0,
                    ALL_APPS_CONTENT_FADE_THRESHOLD));
                    ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD));
            builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(
                    ACCEL,
                    progressToReachOverview,
                    progressToReachOverview + ALL_APPS_CONTENT_FADE_THRESHOLD));
                    ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD,
                    ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD));

            // Get workspace out of the way quickly, to prepare for potential pause.
            builder.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL_3);
@@ -294,15 +291,14 @@ public class NoButtonNavbarToOverviewTouchController extends PortraitStatesTouch
            return builder;
        } else if (fromState == ALL_APPS && toState == NORMAL) {
            StateAnimationConfig builder = new StateAnimationConfig();
            // Keep all apps/predictions opaque until the very end of the transition.
            float progressToReachOverview = OVERVIEW.getVerticalProgress(mLauncher);

            builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(
                    DEACCEL,
                    progressToReachOverview - ALL_APPS_CONTENT_FADE_THRESHOLD,
                    progressToReachOverview));
                    1 - ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD,
                    1 - ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD));
            builder.setInterpolator(ANIM_ALL_APPS_HEADER_FADE, Interpolators.clampToProgress(
                    DEACCEL,
                    1 - ALL_APPS_CONTENT_FADE_THRESHOLD,
                    1 - ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD,
                    1));
            return builder;
        }
+12 −6
Original line number Diff line number Diff line
@@ -49,9 +49,15 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr
    private static final String TAG = "PortraitStatesTouchCtrl";

    /**
     * The progress at which all apps content will be fully visible when swiping up from overview.
     * The progress at which all apps content will be fully visible.
     */
    protected static final float ALL_APPS_CONTENT_FADE_THRESHOLD = 0.08f;
    protected static final float ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD = 0.8f;

    /**
     * Minimum clamping progress for fading in all apps content
     */
    protected static final float ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD = 0.4f;


    /**
     * The progress at which recents will begin fading out when swiping up from overview.
@@ -116,14 +122,14 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr
    private StateAnimationConfig getNormalToAllAppsAnimation() {
        StateAnimationConfig builder = new StateAnimationConfig();
        builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(ACCEL,
                0, ALL_APPS_CONTENT_FADE_THRESHOLD));
                0, ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD));
        return builder;
    }

    private StateAnimationConfig getAllAppsToNormalAnimation() {
        StateAnimationConfig builder = new StateAnimationConfig();
        builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(DEACCEL,
                1 - ALL_APPS_CONTENT_FADE_THRESHOLD, 1));
                1 - ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD, 1));
        return builder;
    }

+125 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.views;

import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.animation.Interpolator;

import androidx.core.graphics.ColorUtils;

import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ScrimView;

/**
 * Scrim used for all-apps background. uses interpolator to coordinate fade in with
 * all-apps contents
 *
 * Note: ranges are inverted because progress goes from 1 to 0 for NORMAL->AllAPPS
 */
public class AllAppsScrimView extends ScrimView<BaseQuickstepLauncher> {

    private static final float TINT_DECAY_MULTIPLIER = .5f;

    //min progress for scrim to become visible
    private static final float SCRIM_VISIBLE_THRESHOLD = .9f;
    //max progress where scrim alpha animates.
    private static final float SCRIM_SOLID_THRESHOLD = .5f;
    private final Interpolator mScrimInterpolator = Interpolators.clampToProgress(ACCEL,
            SCRIM_SOLID_THRESHOLD,
            SCRIM_VISIBLE_THRESHOLD);

    // In transposed layout, we simply draw a flat color.
    private boolean mDrawingFlatColor;

    private final int mEndAlpha;
    private final Paint mPaint;

    private int mCurrentScrimColor;
    private final int mTintColor;

    public AllAppsScrimView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mMaxScrimAlpha = Math.round(OVERVIEW.getOverviewScrimAlpha(mLauncher) * 255);
        mTintColor = Themes.getColorAccent(mContext);


        mEndAlpha = Color.alpha(mEndScrim);
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

        // Just assume the easiest UI for now, until we have the proper layout information.
        mDrawingFlatColor = true;
    }

    @Override
    public void reInitUi() {
        DeviceProfile dp = mLauncher.getDeviceProfile();
        mDrawingFlatColor = dp.isVerticalBarLayout();
        updateColors();
        updateSysUiColors();
        invalidate();
    }

    @Override
    public void updateColors() {
        super.updateColors();
        if (mDrawingFlatColor) {
            return;
        }

        if (mProgress >= 1) {
            mCurrentScrimColor = 0;
        } else {
            float interpolationProgress = mScrimInterpolator.getInterpolation(mProgress);
            // Note that these ranges and interpolators are inverted because progress goes 1 to 0.
            int alpha = Math.round(Utilities.mapRange(interpolationProgress, mEndAlpha, 0));
            int color = ColorUtils.blendARGB(mEndScrim, mTintColor,
                    mProgress * TINT_DECAY_MULTIPLIER);
            mCurrentScrimColor = setColorAlphaBound(color, alpha);
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (mDrawingFlatColor) {
            if (mCurrentFlatColor != 0) {
                canvas.drawColor(mCurrentFlatColor);
            }
            return;
        }

        if (Color.alpha(mCurrentScrimColor) == 0) {
            return;
        } else if (mProgress <= 0) {
            canvas.drawColor(mCurrentScrimColor);
            return;
        }

        mPaint.setColor(mCurrentScrimColor);
        canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
    }
}
+0 −256
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.views;

import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
import static com.android.launcher3.util.SystemUiController.UI_STATE_SCRIM_VIEW;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Path.Direction;
import android.graphics.Path.Op;
import android.util.AttributeSet;
import android.view.animation.Interpolator;

import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.uioverrides.states.OverviewState;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ScrimView;
import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;

/**
 * Scrim used for all-apps and shelf in Overview
 * In transposed layout, it behaves as a simple color scrim.
 * In portrait layout, it draws a rounded rect such that
 *    From normal state to overview state, the shelf just fades in and does not move
 *    From overview state to all-apps state the shelf moves up and fades in to cover the screen
 */
public class ShelfScrimView extends ScrimView<BaseQuickstepLauncher>
        implements NavigationModeChangeListener {

    // If the progress is more than this, shelf follows the finger, otherwise it moves faster to
    // cover the whole screen
    private static final float SCRIM_CATCHUP_THRESHOLD = 0.2f;

    // Temporarily needed until android.R.attr.bottomDialogCornerRadius becomes public
    private static final float BOTTOM_CORNER_RADIUS_RATIO = 2f;

    // In transposed layout, we simply draw a flat color.
    private boolean mDrawingFlatColor;

    // For shelf mode
    private final int mEndAlpha;
    private final float mRadius;
    private final int mMaxScrimAlpha;
    private final Paint mPaint;

    // Mid point where the alpha changes
    private int mMidAlpha;
    private float mMidProgress;

    private Interpolator mBeforeMidProgressColorInterpolator = ACCEL;
    private Interpolator mAfterMidProgressColorInterpolator = ACCEL;

    private float mShiftRange;

    private float mTopOffset;
    private float mShelfTop;
    private float mShelfTopAtThreshold;

    private int mShelfColor;
    private int mRemainingScreenColor;

    private final Path mTempPath = new Path();
    private final Path mRemainingScreenPath = new Path();
    private boolean mRemainingScreenPathValid = false;

    private Mode mSysUINavigationMode;

    public ShelfScrimView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mMaxScrimAlpha = Math.round(OVERVIEW.getOverviewScrimAlpha(mLauncher) * 255);

        mEndAlpha = Color.alpha(mEndScrim);
        mRadius = BOTTOM_CORNER_RADIUS_RATIO * Themes.getDialogCornerRadius(context);
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

        // Just assume the easiest UI for now, until we have the proper layout information.
        mDrawingFlatColor = true;
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mRemainingScreenPathValid = false;
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        onNavigationModeChanged(SysUINavigationMode.INSTANCE.get(getContext())
                .addModeChangeListener(this));
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        SysUINavigationMode.INSTANCE.get(getContext()).removeModeChangeListener(this);
    }

    @Override
    public void onNavigationModeChanged(Mode newMode) {
        mSysUINavigationMode = newMode;
        // Note that these interpolators are inverted because progress goes 1 to 0.
        if (mSysUINavigationMode == Mode.NO_BUTTON) {
            // Show the shelf more quickly before reaching overview progress.
            mBeforeMidProgressColorInterpolator = ACCEL_2;
            mAfterMidProgressColorInterpolator = ACCEL;
        } else {
            mBeforeMidProgressColorInterpolator = ACCEL;
            mAfterMidProgressColorInterpolator = Interpolators.clampToProgress(ACCEL, 0.5f, 1f);
        }
    }

    @Override
    public void reInitUi() {
        DeviceProfile dp = mLauncher.getDeviceProfile();
        mDrawingFlatColor = dp.isVerticalBarLayout();

        if (!mDrawingFlatColor) {
            mRemainingScreenPathValid = false;
            mShiftRange = mLauncher.getAllAppsController().getShiftRange();

            // Fade in all apps background quickly to distinguish from swiping from nav bar.
            mMidAlpha = Themes.getAttrInteger(getContext(), R.attr.allAppsInterimScrimAlpha);
            mMidProgress = 1 - (OverviewState.getDefaultSwipeHeight(mLauncher)
                    / mLauncher.getAllAppsController().getShiftRange());

            mTopOffset = dp.getInsets().top;
            mShelfTopAtThreshold = mShiftRange * SCRIM_CATCHUP_THRESHOLD + mTopOffset;
        }
        updateColors();
        updateSysUiColors();
        invalidate();
    }

    @Override
    public void updateColors() {
        super.updateColors();
        if (mDrawingFlatColor) {
            return;
        }

        if (mProgress >= SCRIM_CATCHUP_THRESHOLD) {
            mShelfTop = mShiftRange * mProgress + mTopOffset;
        } else {
            mShelfTop = Utilities.mapRange(mProgress / SCRIM_CATCHUP_THRESHOLD, -mRadius,
                    mShelfTopAtThreshold);
        }

        if (mProgress >= 1) {
            mRemainingScreenColor = 0;
            mShelfColor = 0;
        } else if (mProgress >= mMidProgress) {
            mRemainingScreenColor = 0;

            int alpha = Math.round(Utilities.mapToRange(
                    mProgress, mMidProgress, 1, mMidAlpha, 0, mBeforeMidProgressColorInterpolator));
            mShelfColor = setColorAlphaBound(mEndScrim, alpha);
        } else {
            // Note that these ranges and interpolators are inverted because progress goes 1 to 0.
            int alpha = Math.round(
                    Utilities.mapToRange(mProgress, (float) 0, mMidProgress, (float) mEndAlpha,
                            (float) mMidAlpha, mAfterMidProgressColorInterpolator));
            mShelfColor = setColorAlphaBound(mEndScrim, alpha);

            int remainingScrimAlpha = Math.round(
                    Utilities.mapToRange(mProgress, (float) 0, mMidProgress, mMaxScrimAlpha,
                            (float) 0, LINEAR));
            mRemainingScreenColor = setColorAlphaBound(mScrimColor, remainingScrimAlpha);
        }
    }

    @Override
    protected void updateSysUiColors() {
        if (mDrawingFlatColor) {
            super.updateSysUiColors();
        } else {
            // Use a light system UI (dark icons) if all apps is behind at least half of the
            // status bar.
            boolean forceChange = mShelfTop <= mLauncher.getDeviceProfile().getInsets().top / 2f;
            if (forceChange) {
                mLauncher.getSystemUiController().updateUiState(UI_STATE_SCRIM_VIEW, !mIsScrimDark);
            } else {
                mLauncher.getSystemUiController().updateUiState(UI_STATE_SCRIM_VIEW, 0);
            }
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (mDrawingFlatColor) {
            if (mCurrentFlatColor != 0) {
                canvas.drawColor(mCurrentFlatColor);
            }
            return;
        }

        if (Color.alpha(mShelfColor) == 0) {
            return;
        } else if (mProgress <= 0) {
            canvas.drawColor(mShelfColor);
            return;
        }

        int height = getHeight();
        int width = getWidth();
        // Draw the scrim over the remaining screen if needed.
        if (mRemainingScreenColor != 0) {
            if (!mRemainingScreenPathValid) {
                mTempPath.reset();
                // Using a arbitrary '+10' in the bottom to avoid any left-overs at the
                // corners due to rounding issues.
                mTempPath.addRoundRect(0, height - mRadius, width, height + mRadius + 10,
                        mRadius, mRadius, Direction.CW);
                mRemainingScreenPath.reset();
                mRemainingScreenPath.addRect(0, 0, width, height, Direction.CW);
                mRemainingScreenPath.op(mTempPath, Op.DIFFERENCE);
            }

            float offset = height - mRadius - mShelfTop;
            canvas.translate(0, -offset);
            mPaint.setColor(mRemainingScreenColor);
            canvas.drawPath(mRemainingScreenPath, mPaint);
            canvas.translate(0, offset);
        }

        mPaint.setColor(mShelfColor);
        canvas.drawRoundRect(0, mShelfTop, width, height + mRadius, mRadius, mRadius, mPaint);
    }
}
Loading