summaryrefslogtreecommitdiff
path: root/src/com/android/launcher3/allapps/SearchTransitionController.java
blob: d5c3b577883253c9beeb0905f9d9852915a49245 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/*
 * 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.allapps;

import static android.view.View.VISIBLE;

import static com.android.app.animation.Interpolators.DECELERATE_1_7;
import static com.android.app.animation.Interpolators.INSTANT;
import static com.android.app.animation.Interpolators.clampToProgress;
import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback;

import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.view.View;
import android.view.animation.Interpolator;

import com.android.launcher3.R;

/** Coordinates the transition between Search and A-Z in All Apps. */
public class SearchTransitionController extends RecyclerViewAnimationController {

    // Interpolator when the user taps the QSB while already in All Apps.
    private static final Interpolator INTERPOLATOR_WITHIN_ALL_APPS = DECELERATE_1_7;
    // Interpolator when the user taps the QSB from home screen, so transition to all apps is
    // happening simultaneously.
    private static final Interpolator INTERPOLATOR_TRANSITIONING_TO_ALL_APPS = INSTANT;

    private boolean mSkipNextAnimationWithinAllApps;

    public SearchTransitionController(ActivityAllAppsContainerView<?> allAppsContainerView) {
        super(allAppsContainerView);
    }

    /**
     * Starts the transition to or from search state. If a transition is already in progress, the
     * animation will start from that point with the new duration, and the previous onEndRunnable
     * will not be called.
     *
     * @param goingToSearch true if will be showing search results, otherwise will be showing a-z
     * @param duration time in ms for the animation to run
     * @param onEndRunnable will be called when the animation finishes, unless another animation is
     *                      scheduled in the meantime
     */
    @Override
    protected void animateToState(boolean goingToSearch, long duration, Runnable onEndRunnable) {
        super.animateToState(goingToSearch, duration, onEndRunnable);
        if (!goingToSearch) {
            mAnimator.addListener(forSuccessCallback(() -> {
                mAllAppsContainerView.getFloatingHeaderView().setFloatingRowsCollapsed(false);
                mAllAppsContainerView.getFloatingHeaderView().reset(false /* animate */);
                mAllAppsContainerView.getAppsRecyclerViewContainer().setTranslationY(0);
            }));
        }
        mAllAppsContainerView.getFloatingHeaderView().setFloatingRowsCollapsed(true);
        mAllAppsContainerView.getFloatingHeaderView().setVisibility(VISIBLE);
        mAllAppsContainerView.getFloatingHeaderView().maybeSetTabVisibility(VISIBLE);
        mAllAppsContainerView.getAppsRecyclerViewContainer().setVisibility(VISIBLE);
        getRecyclerView().setVisibility(VISIBLE);
    }

    @Override
    protected SearchRecyclerView getRecyclerView() {
        return mAllAppsContainerView.getSearchRecyclerView();
    }

    @Override
    protected int onProgressUpdated(float searchToAzProgress) {
        int searchHeight = super.onProgressUpdated(searchToAzProgress);

        FloatingHeaderView headerView = mAllAppsContainerView.getFloatingHeaderView();

        // Add predictions + app divider height to account for predicted apps which will now be in
        // the Search RV instead of the floating header view. Note `getFloatingRowsHeight` returns 0
        // when predictions are not shown.
        int appsTranslationY = searchHeight + headerView.getFloatingRowsHeight();

        if (headerView.usingTabs()) {
            // Move tabs below the search results, and fade them out in 20% of the animation.
            headerView.setTranslationY(searchHeight);
            headerView.setAlpha(clampToProgress(searchToAzProgress, 0.8f, 1f));

            // Account for the additional padding added for the tabs.
            appsTranslationY +=
                    headerView.getTabsAdditionalPaddingBottom()
                            + mAllAppsContainerView.getResources().getDimensionPixelOffset(
                                    R.dimen.all_apps_tabs_margin_top)
                            - headerView.getPaddingTop();
        }

        View appsContainer = mAllAppsContainerView.getAppsRecyclerViewContainer();
        appsContainer.setTranslationY(appsTranslationY);
        // Fade apps out with tabs (in 20% of the total animation).
        appsContainer.setAlpha(clampToProgress(searchToAzProgress, 0.8f, 1f));
        return searchHeight;
    }

    /**
     * Should only animate if the view is not an app icon or if the app row is complete.
     */
    @Override
    protected boolean shouldAnimate(View view, boolean hasDecorationInfo, boolean appRowComplete) {
        return !isAppIcon(view) || appRowComplete;
    }

    @Override
    protected TimeInterpolator getInterpolator() {
        TimeInterpolator timeInterpolator =
                mAllAppsContainerView.isInAllApps()
                        ? INTERPOLATOR_WITHIN_ALL_APPS : INTERPOLATOR_TRANSITIONING_TO_ALL_APPS;
        if (mSkipNextAnimationWithinAllApps) {
            timeInterpolator = INSTANT;
            mSkipNextAnimationWithinAllApps = false;
        }
        return timeInterpolator;
    }

    /**
     * This should only be called from {@code LauncherSearchSessionManager} when app restarts due to
     * theme changes.
     */
    public void setSkipAnimationWithinAllApps(boolean skip) {
        mSkipNextAnimationWithinAllApps = skip;
    }
}