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

Commit 103c5261 authored by Alex Chau's avatar Alex Chau Committed by Automerger Merge Worker
Browse files

Update pull back animation for tablet AllApps am: e3b80636

parents 97c39ea7 e3b80636
Loading
Loading
Loading
Loading
+6 −13
Original line number Diff line number Diff line
@@ -21,7 +21,8 @@ import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRES
import static com.android.launcher3.LauncherAnimUtils.newCancelListener;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PULL_BACK_PROGRESS;
import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PULL_BACK_ALPHA;
import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PULL_BACK_TRANSLATION;
import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback;
import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
import static com.android.launcher3.config.FeatureFlags.ENABLE_ALL_APPS_EDU;
@@ -40,11 +41,9 @@ import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.util.TouchController;
import com.android.quickstep.TaskUtils;
@@ -148,16 +147,10 @@ public class NavBarToHomeTouchController implements TouchController,
            AbstractFloatingView.closeOpenContainer(mLauncher, AbstractFloatingView.TYPE_TASK_MENU);
        } else if (mStartState == ALL_APPS) {
            AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
            builder.setFloat(allAppsController, ALL_APPS_PULL_BACK_PROGRESS,
                    -mPullbackDistance / allAppsController.getShiftRange(), PULLBACK_INTERPOLATOR);

            // Slightly fade out all apps content to further distinguish from scrolling.
            StateAnimationConfig config = new StateAnimationConfig();
            config.duration = accuracy;
            config.setInterpolator(StateAnimationConfig.ANIM_ALL_APPS_FADE, Interpolators
                    .mapToProgress(PULLBACK_INTERPOLATOR, 0, 0.5f));

            allAppsController.setAlphas(mEndState, config, builder);
            builder.setFloat(allAppsController, ALL_APPS_PULL_BACK_TRANSLATION,
                    -mPullbackDistance, PULLBACK_INTERPOLATOR);
            builder.setFloat(allAppsController, ALL_APPS_PULL_BACK_ALPHA,
                    0.5f, PULLBACK_INTERPOLATOR);
        }
        AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mLauncher);
        if (topView != null) {
+76 −20
Original line number Diff line number Diff line
@@ -44,6 +44,8 @@ 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.util.MultiAdditivePropertyFactory;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.UiThreadHelper;
import com.android.launcher3.views.ScrimView;

@@ -76,20 +78,56 @@ public class AllAppsTransitionController
                }
            };

    public static final FloatProperty<AllAppsTransitionController> ALL_APPS_PULL_BACK_PROGRESS =
            new FloatProperty<AllAppsTransitionController>("allAppsPullBackProgress") {
    public static final FloatProperty<AllAppsTransitionController> ALL_APPS_PULL_BACK_TRANSLATION =
            new FloatProperty<AllAppsTransitionController>("allAppsPullBackTranslation") {

                @Override
                public Float get(AllAppsTransitionController controller) {
                    return controller.mPullBackProgress;
                    if (controller.mIsTablet) {
                        return controller.mAppsView.getRecyclerViewContainer().getTranslationY();
                    } else {
                        return controller.getAppsViewPullbackTranslationY().get(
                                controller.mAppsView);
                    }
                }

                @Override
                public void setValue(AllAppsTransitionController controller, float progress) {
                    controller.setPullBackProgress(progress);
                public void setValue(AllAppsTransitionController controller, float translation) {
                    if (controller.mIsTablet) {
                        controller.mAppsView.getRecyclerViewContainer().setTranslationY(
                                translation);
                    } else {
                        controller.getAppsViewPullbackTranslationY().set(controller.mAppsView,
                                translation);
                    }
                }
            };

    public static final FloatProperty<AllAppsTransitionController> ALL_APPS_PULL_BACK_ALPHA =
            new FloatProperty<AllAppsTransitionController>("allAppsPullBackAlpha") {

                @Override
                public Float get(AllAppsTransitionController controller) {
                    if (controller.mIsTablet) {
                        return controller.mAppsView.getRecyclerViewContainer().getAlpha();
                    } else {
                        return controller.getAppsViewPullbackAlpha().getValue();
                    }
                }

                @Override
                public void setValue(AllAppsTransitionController controller, float alpha) {
                    if (controller.mIsTablet) {
                        controller.mAppsView.getRecyclerViewContainer().setAlpha(alpha);
                    } else {
                        controller.getAppsViewPullbackAlpha().setValue(alpha);
                    }
                }
            };

    private static final int INDEX_APPS_VIEW_PROGRESS = 0;
    private static final int INDEX_APPS_VIEW_PULLBACK = 1;
    private static final int APPS_VIEW_INDEX_COUNT = 2;

    private ActivityAllAppsContainerView<Launcher> mAppsView;

@@ -104,18 +142,23 @@ public class AllAppsTransitionController
    // 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 mPullBackProgress;  // [0, 1], mShiftRange * mPullBackProgress = shiftCurrent

    private ScrimView mScrimView;
    private View mPullBackView;

    private final MultiAdditivePropertyFactory<View>
            mAppsViewTranslationYPropertyFactory = new MultiAdditivePropertyFactory<>(
            "appsViewTranslationY", View.TRANSLATION_Y);
    private MultiValueAlpha mAppsViewAlpha;

    private boolean mIsTablet;

    public AllAppsTransitionController(Launcher l) {
        mLauncher = l;
        DeviceProfile dp = mLauncher.getDeviceProfile();
        setShiftRange(dp.allAppsShiftRange);
        mProgress = 1f;
        mPullBackProgress = 1f;
        mIsVerticalLayout = dp.isVerticalBarLayout();
        mIsTablet = dp.isTablet;
        mLauncher.addOnDeviceProfileChangeListener(this);
    }

@@ -133,7 +176,7 @@ public class AllAppsTransitionController
            mLauncher.getWorkspace().getPageIndicator().setTranslationY(0);
        }

        mPullBackView = dp.isTablet ? mAppsView.getRecyclerViewContainer() : mAppsView;
        mIsTablet = dp.isTablet;
    }

    /**
@@ -146,16 +189,27 @@ public class AllAppsTransitionController
     */
    public void setProgress(float progress) {
        mProgress = progress;
        mAppsView.setTranslationY(mProgress * mShiftRange);
        getAppsViewProgressTranslationY().set(mAppsView, mProgress * mShiftRange);
    }

    public float getProgress() {
        return mProgress;
    }

    private void setPullBackProgress(float progress) {
        mPullBackProgress = progress;
        mPullBackView.setTranslationY(mPullBackProgress * mShiftRange);
    private FloatProperty<View> getAppsViewProgressTranslationY() {
        return mAppsViewTranslationYPropertyFactory.get(INDEX_APPS_VIEW_PROGRESS);
    }

    private FloatProperty<View> getAppsViewPullbackTranslationY() {
        return mAppsViewTranslationYPropertyFactory.get(INDEX_APPS_VIEW_PULLBACK);
    }

    private MultiValueAlpha.AlphaProperty getAppsViewProgressAlpha() {
        return mAppsViewAlpha.getProperty(INDEX_APPS_VIEW_PROGRESS);
    }

    private MultiValueAlpha.AlphaProperty getAppsViewPullbackAlpha() {
        return mAppsViewAlpha.getProperty(INDEX_APPS_VIEW_PULLBACK);
    }

    /**
@@ -164,8 +218,6 @@ public class AllAppsTransitionController
     */
    @Override
    public void setState(LauncherState state) {
        // Always reset pull back progress when switching states.
        setPullBackProgress(0f);
        setProgress(state.getVerticalProgress(mLauncher));
        setAlphas(state, new StateAnimationConfig(), NO_ANIM_PROPERTY_SETTER);
        onProgressAnimationEnd();
@@ -180,10 +232,13 @@ public class AllAppsTransitionController
            StateAnimationConfig config, PendingAnimation builder) {
        if (NORMAL.equals(toState) && mLauncher.isInState(ALL_APPS)) {
            UiThreadHelper.hideKeyboardAsync(mLauncher, mLauncher.getAppsView().getWindowToken());
            builder.addEndListener(success -> {
                // Reset pull back progress and alpha after switching states.
                ALL_APPS_PULL_BACK_TRANSLATION.set(this, 0f);
                ALL_APPS_PULL_BACK_ALPHA.set(this, 1f);
            });
        }

        // Always reset pull back progress when switching states.
        setPullBackProgress(0f);
        float targetProgress = toState.getVerticalProgress(mLauncher);
        if (Float.compare(mProgress, targetProgress) == 0) {
            setAlphas(toState, config, builder);
@@ -222,7 +277,8 @@ public class AllAppsTransitionController
        boolean hasAllAppsContent = (visibleElements & ALL_APPS_CONTENT) != 0;

        Interpolator allAppsFade = config.getInterpolator(ANIM_ALL_APPS_FADE, LINEAR);
        setter.setViewAlpha(mAppsView, hasAllAppsContent ? 1 : 0, allAppsFade);
        setter.setFloat(getAppsViewProgressAlpha(), MultiValueAlpha.VALUE,
                hasAllAppsContent ? 1 : 0, allAppsFade);

        boolean shouldProtectHeader =
                ALL_APPS == state || mLauncher.getStateManager().getState() == ALL_APPS;
@@ -245,8 +301,8 @@ public class AllAppsTransitionController
                            | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
        }
        mAppsView.setScrimView(scrimView);
        mPullBackView = mLauncher.getDeviceProfile().isTablet
                ? mAppsView.getRecyclerViewContainer() : mAppsView;
        mAppsViewAlpha = new MultiValueAlpha(mAppsView, APPS_VIEW_INDEX_COUNT);
        mAppsViewAlpha.setUpdateVisibility(true);
    }

    /**
+115 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.util;

import android.util.ArrayMap;
import android.util.FloatProperty;
import android.util.Log;
import android.util.Property;
import android.view.View;

/**
 * Allows to combine multiple values set by several sources.
 *
 * The various sources are meant to use [set], providing different `setterIndex` params. When it is
 * not set, 0 is used. This is meant to cover the case multiple animations are going on at the same
 * time.
 *
 * This class behaves similarly to [MultiValueAlpha], but is meant to be more abstract and reusable.
 * It sets the addition of all values.
 *
 * @param <T> Type where to apply the property.
 */
public class MultiAdditivePropertyFactory<T extends View> {

    private static final boolean DEBUG = false;
    private static final String TAG = "MultiAdditivePropertyFactory";
    private final String mName;
    private final ArrayMap<Integer, MultiAdditiveProperty> mProperties =
            new ArrayMap<>();

    // This is an optimization for cases when set is called repeatedly with the same setterIndex.
    private float mAggregationOfOthers = 0f;
    private Integer mLastIndexSet = -1;
    private final Property<View, Float> mProperty;

    public MultiAdditivePropertyFactory(String name, Property<View, Float> property) {
        mName = name;
        mProperty = property;
    }

    /** Returns the [MultiFloatProperty] associated with [inx], creating it if not present. */
    public MultiAdditiveProperty get(Integer index) {
        return mProperties.computeIfAbsent(index,
                (k) -> new MultiAdditiveProperty(index, mName + "_" + index));
    }

    /**
     * Each [setValue] will be aggregated with the other properties values created by the
     * corresponding factory.
     */
    class MultiAdditiveProperty extends FloatProperty<T> {
        private final int mInx;
        private float mValue = 0f;

        MultiAdditiveProperty(int inx, String name) {
            super(name);
            mInx = inx;
        }

        @Override
        public void setValue(T obj, float newValue) {
            if (mLastIndexSet != mInx) {
                mAggregationOfOthers = 0f;
                mProperties.forEach((key, property) -> {
                    if (key != mInx) {
                        mAggregationOfOthers += property.mValue;
                    }
                });
                mLastIndexSet = mInx;
            }
            float lastAggregatedValue = mAggregationOfOthers + newValue;
            mValue = newValue;
            apply(obj, lastAggregatedValue);

            if (DEBUG) {
                Log.d(TAG, "name=" + mName
                        + " newValue=" + newValue + " mInx=" + mInx
                        + " aggregated=" + lastAggregatedValue + " others= " + mProperties);
            }
        }

        @Override
        public Float get(T view) {
            // The scale of the view should match mLastAggregatedValue. Still, if it has been
            // changed without using this property, it can differ. As this get method is usually
            // used to set the starting point on an animation, this would result in some jumps
            // when the view scale is different than the last aggregated value. To stay on the
            // safe side, let's return the real view scale.
            return mProperty.get(view);
        }

        @Override
        public String toString() {
            return String.valueOf(mValue);
        }
    }

    protected void apply(View view, float value) {
        mProperty.set(view, value);
    }
}
+66 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.util

import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith

/** Unit tests for [MultiAdditivePropertyFactory] */
@SmallTest
@RunWith(AndroidJUnit4::class)
class MultiAdditivePropertyTest {

    private val received = mutableListOf<Float>()

    private val factory =
        object : MultiAdditivePropertyFactory<View?>("Test", View.TRANSLATION_X) {
            override fun apply(obj: View?, value: Float) {
                received.add(value)
            }
        }

    private val p1 = factory.get(1)
    private val p2 = factory.get(2)
    private val p3 = factory.get(3)

    @Test
    fun set_sameIndexes_allApplied() {
        val v1 = 50f
        val v2 = 100f
        p1.set(null, v1)
        p1.set(null, v1)
        p1.set(null, v2)

        assertThat(received).containsExactly(v1, v1, v2)
    }

    @Test
    fun set_differentIndexes_aggregationApplied() {
        val v1 = 50f
        val v2 = 100f
        val v3 = 150f
        p1.set(null, v1)
        p2.set(null, v2)
        p3.set(null, v3)

        assertThat(received).containsExactly(v1, v1 + v2, v1 + v2 + v3)
    }
}
+16 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.util

import android.view.View