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

Commit 0724113b authored by Sunny Goyal's avatar Sunny Goyal
Browse files

Removing insets controller animation as part of all-apps opening

Adding support for easier extension of StateHandlers

Bug: 180143210
Test: Verified on device
Change-Id: If6a088d1482c9b268b21786c4694f6e0927be0c3
parent ae6b3481
Loading
Loading
Loading
Loading
+7 −9
Original line number Diff line number Diff line
@@ -63,6 +63,7 @@ import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;

import java.util.List;
import java.util.stream.Stream;

/**
@@ -244,15 +245,12 @@ public abstract class BaseQuickstepLauncher extends Launcher
    }

    @Override
    protected StateHandler<LauncherState>[] createStateHandlers() {
        return new StateHandler[] {
                getAllAppsController(),
                getWorkspace(),
                getDepthController(),
                new RecentsViewStateController(this),
                new BackButtonAlphaHandler(this),
                getTaskbarStateHandler(),
        };
    protected void collectStateHandlers(List<StateHandler> out) {
        super.collectStateHandlers(out);
        out.add(getDepthController());
        out.add(new RecentsViewStateController(this));
        out.add(new BackButtonAlphaHandler(this));
        out.add(getTaskbarStateHandler());
    }

    public DepthController getDepthController() {
+3 −2
Original line number Diff line number Diff line
@@ -71,6 +71,7 @@ import com.android.systemui.shared.system.RemoteAnimationTargetCompat;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.List;

/**
 * A recents activity that shows the recently launched tasks as swipable task cards.
@@ -317,8 +318,8 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> {
    }

    @Override
    protected StateHandler<RecentsState>[] createStateHandlers() {
        return new StateHandler[] { new FallbackRecentsStateController(this) };
    protected void collectStateHandlers(List<StateHandler> out) {
        out.add(new FallbackRecentsStateController(this));
    }

    @Override
+4 −2
Original line number Diff line number Diff line
@@ -2761,8 +2761,10 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
        return super.onKeyUp(keyCode, event);
    }

    protected StateHandler<LauncherState>[] createStateHandlers() {
        return new StateHandler[] { getAllAppsController(), getWorkspace() };
    @Override
    protected void collectStateHandlers(List<StateHandler> out) {
        out.add(getAllAppsController());
        out.add(getWorkspace());
    }

    public TouchController[] createTouchControllers() {
+0 −315
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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 android.annotation.TargetApi;
import android.graphics.Insets;
import android.os.Build;
import android.util.Log;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowInsetsAnimationControlListener;
import android.view.WindowInsetsAnimationController;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;

import androidx.annotation.Nullable;

import com.android.launcher3.Utilities;
import com.android.launcher3.util.UiThreadHelper;

/**
 * Handles IME over all apps to be synchronously transitioning along with the passed in
 * root inset.
 */
public class AllAppsInsetTransitionController {

    private static final boolean DEBUG = true;
    private static final String TAG = "AllAppsInsetTransitionController";
    private static final Interpolator LINEAR = new LinearInterpolator();

    private WindowInsetsAnimationController mAnimationController;
    private WindowInsetsAnimationControlListener mCurrentRequest;

    private Runnable mSearchEduRunnable;

    private float mAllAppsHeight;

    private int mDownInsetBottom;
    private boolean mShownAtDown;

    private int mHiddenInsetBottom;
    private int mShownInsetBottom;

    private float mDown, mCurrent;
    private View mApps;

    /**
     *
     */
    public boolean showSearchEduIfNecessary() {
        if (mSearchEduRunnable == null) {
            return false;
        }
        mSearchEduRunnable.run();
        return true;
    }

    public void setSearchEduRunnable(Runnable eduRunnable) {
        mSearchEduRunnable = eduRunnable;
    }

    // Only purpose of these states is to keep track of fast fling transition
    enum State {
        RESET, DRAG_START_BOTTOM, DRAG_START_BOTTOM_IME_CANCELLED,
        FLING_END_TOP, FLING_END_TOP_IME_CANCELLED,
        DRAG_START_TOP, FLING_END_BOTTOM
    }

    private State mState;

    public AllAppsInsetTransitionController(float allAppsHeight, View appsView) {
        mAllAppsHeight = allAppsHeight;
        mApps = appsView;
    }

    public void show() {
        mApps.getWindowInsetsController().show(WindowInsets.Type.ime());
    }

    public void hide() {
        if (!Utilities.ATLEAST_R) return;

        WindowInsets insets = mApps.getRootWindowInsets();
        if (insets == null) return;

        boolean imeVisible = insets.isVisible(WindowInsets.Type.ime());

        if (DEBUG) {
            Log.d(TAG, "\nhide imeVisible=" + imeVisible);
        }
        if (insets.isVisible(WindowInsets.Type.ime())) {
            mApps.getWindowInsetsController().hide(WindowInsets.Type.ime());
        }
    }

    /**
     * Initializes member variables and requests for the {@link WindowInsetsAnimationController}
     * object.
     *
     * @param progress value between 0..1
     */
    @TargetApi(Build.VERSION_CODES.R)
    public void onDragStart(float progress) {
        if (!Utilities.ATLEAST_R) return;

        // Until getRootWindowInsets().isVisible(...) method returns correct value,
        // only support InsetController based IME transition during swipe up and
        // NOT swipe down
        if (Float.compare(progress, 0f) == 0) return;

        setState(true, false, progress);
        mDown = progress * mAllAppsHeight;

        // Below two values are sometimes incorrect. Possibly a platform bug
        // mDownInsetBottom = mApps.getRootWindowInsets().getInsets(WindowInsets.Type.ime()).bottom;
        // mShownAtDown = mApps.getRootWindowInsets().isVisible(WindowInsets.Type.ime());

        if (DEBUG) {
            Log.d(TAG, "\nonDragStart progress=" + progress
                    + " mDownInsets=" + mDownInsetBottom
                    + " mShownAtDown=" + mShownAtDown);
        }

        mApps.getWindowInsetsController().controlWindowInsetsAnimation(
                WindowInsets.Type.ime(), -1 /* no predetermined duration */, LINEAR, null,
                mCurrentRequest = new WindowInsetsAnimationControlListener() {

                    @Override
                    public void onReady(WindowInsetsAnimationController controller, int types) {
                        if (DEBUG) {
                            Log.d(TAG, "Listener.onReady " + (mCurrentRequest == this));
                        }
                        if (controller != null) {
                            if (mCurrentRequest == this && !handleFinishOnFling(controller)) {
                                mAnimationController = controller;
                            } else {
                                controller.finish(false /* just don't show */);
                            }
                        }
                    }

                    @Override
                    public void onFinished(WindowInsetsAnimationController controller) {
                        // when screen lock happens, then this method get called
                        if (DEBUG) {
                            Log.d(TAG, "Listener.onFinished ctrl=" + controller
                                    + " mAnimationController=" + mAnimationController);
                        }
                        if (mAnimationController != null) {
                            mAnimationController.finish(true);
                            mAnimationController = null;
                        }
                    }

                    @Override
                    public void onCancelled(@Nullable WindowInsetsAnimationController controller) {
                        if (DEBUG) {
                            // Keep the verbose logging to chase down IME not showing up issue.
                            // b/178904132
                            Log.e(TAG, "Listener.onCancelled ctrl=" + controller
                                    + " mAnimationController=" + mAnimationController,
                                    new Exception());
                        }
                        if (mState == State.DRAG_START_BOTTOM) {
                            mState = State.DRAG_START_BOTTOM_IME_CANCELLED;
                        }
                        mAnimationController = null;
                        if (controller != null) {
                            controller.finish(true);
                        }
                    }
                });
    }

    /**
     * If IME bounds after touch sequence finishes, call finish.
     */
    private boolean handleFinishOnFling(WindowInsetsAnimationController controller) {
        if (!Utilities.ATLEAST_R) return false;

        if (mState == State.FLING_END_TOP) {
            controller.finish(true);
            return true;
        } else if (mState == State.FLING_END_BOTTOM) {
            controller.finish(false);
            return true;
        }
        return false;
    }

    /**
     * Handles the translation using the progress.
     *
     * @param progress value between 0..1
     */
    @TargetApi(Build.VERSION_CODES.R)
    public void setProgress(float progress) {
        if (!Utilities.ATLEAST_R) return;
        // progress that equals to 0 or 1 is error prone. Do not use them.
        // Instead use onDragStart and onAnimationEnd
        if (mAnimationController == null || progress <= 0f || progress >= 1f) return;

        mCurrent = progress * mAllAppsHeight;
        mHiddenInsetBottom = mAnimationController.getHiddenStateInsets().bottom; // 0
        mShownInsetBottom = mAnimationController.getShownStateInsets().bottom; // 1155

        int shift = mShownAtDown ? 0 : (int) (mAllAppsHeight - mShownInsetBottom);

        int inset = (int) (mDownInsetBottom + (mDown - mCurrent) - shift);

        final int start = mShownAtDown ? mShownInsetBottom : mHiddenInsetBottom;
        final int end = mShownAtDown ? mHiddenInsetBottom : mShownInsetBottom;
        inset = Math.max(inset, mHiddenInsetBottom);
        inset = Math.min(inset, mShownInsetBottom);
        if (DEBUG && false) {
            Log.d(TAG, "updateInset mCurrent=" + mCurrent + " mDown="
                    + mDown + " hidden=" + mHiddenInsetBottom
                    + " shown=" + mShownInsetBottom
                    + " mDownInsets.bottom=" + mDownInsetBottom + " inset=" + inset
                    + " shift= " + shift);
        }

        mAnimationController.setInsetsAndAlpha(
                Insets.of(0, 0, 0, inset),
                1f, (inset - start) / (float) (end - start));
    }

    /**
     * Report to the animation controller that we no longer plan to translate anymore.
     *
     * @param progress value between 0..1
     */
    @TargetApi(Build.VERSION_CODES.R)
    public void onAnimationEnd(float progress) {
        if (DEBUG) {
            Log.d(TAG, "onAnimationEnd progress=" + progress
                    + " mAnimationController=" + mAnimationController);
        }
        if (mState == null) {
            // only called when launcher restarting.
            UiThreadHelper.hideKeyboardAsync(mApps.getContext(), mApps.getWindowToken());
        }

        setState(false, true, progress);


        if (mAnimationController == null) {
            if (mState == State.FLING_END_TOP_IME_CANCELLED) {
                mApps.getWindowInsetsController().show(WindowInsets.Type.ime());
            }
            return;
        }

        /* handle finish */
        if (mState == State.FLING_END_TOP) {
            mAnimationController.finish(true /* show */);
        } else {
            if (Float.compare(progress, 1f) == 0 /* bottom */) {
                mAnimationController.finish(false /* gone */);
            } else {
                mAnimationController.finish(mShownAtDown);
            }
        }
        /* handle finish */

        if (DEBUG) {
            Log.d(TAG, "endTranslation progress=" + progress
                    + " mAnimationController=" + mAnimationController);
        }
        mAnimationController = null;
        mCurrentRequest = null;
        setState(false, false, progress);
    }

    private void setState(boolean start, boolean end, float progress) {
        State state = State.RESET;
        if (start && end) {
            throw new IllegalStateException("drag start and end cannot happen in same call");
        }
        if (start) {
            if (Float.compare(progress, 1f) == 0) {
                state = State.DRAG_START_BOTTOM;
            } else if (Float.compare(progress, 0f) == 0) {
                state = State.DRAG_START_TOP;
            }
        } else if (end) {
            if (Float.compare(progress, 1f) == 0 && mState == State.DRAG_START_TOP) {
                state = State.FLING_END_BOTTOM;
            } else if (Float.compare(progress, 0f) == 0) {
                if (mState == State.DRAG_START_BOTTOM) {
                    state = State.FLING_END_TOP;
                } else if (mState == State.DRAG_START_BOTTOM_IME_CANCELLED) {
                    state = State.FLING_END_TOP_IME_CANCELLED;
                }
            }
        }
        if (DEBUG) {
            Log.d(TAG, "setState " + mState + " -> " + state);
        }
        mState = state;
    }
}
+8 −51
Original line number Diff line number Diff line
@@ -33,18 +33,15 @@ import static com.android.launcher3.util.SystemUiController.UI_STATE_ALLAPPS;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.content.SharedPreferences;
import android.util.FloatProperty;
import android.view.View;
import android.view.animation.Interpolator;
import android.widget.EditText;

import androidx.core.os.BuildCompat;

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;
@@ -63,8 +60,8 @@ import com.android.launcher3.views.ScrimView;
 * 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, SharedPreferences.OnSharedPreferenceChangeListener {
public class AllAppsTransitionController
        implements StateHandler<LauncherState>, OnDeviceProfileChangeListener {

    public static final FloatProperty<AllAppsTransitionController> ALL_APPS_PROGRESS =
            new FloatProperty<AllAppsTransitionController>("allAppsProgress") {
@@ -81,7 +78,6 @@ public class AllAppsTransitionController implements StateHandler<LauncherState>,
            };

    private static final int APPS_VIEW_ALPHA_CHANNEL_INDEX = 0;
    private static final String PREF_KEY_SHOW_SEARCH_IME = "pref_search_show_ime";

    private AllAppsContainerView mAppsView;
    private ScrimView mScrimView;
@@ -99,8 +95,6 @@ public class AllAppsTransitionController implements StateHandler<LauncherState>,
    private float mProgress;        // [0, 1], mShiftRange * mProgress = shiftCurrent

    private float mScrollRangeDelta = 0;
    private AllAppsInsetTransitionController mInsetController;
    private boolean mSearchImeEnabled;

    public AllAppsTransitionController(Launcher l) {
        mLauncher = l;
@@ -109,19 +103,12 @@ public class AllAppsTransitionController implements StateHandler<LauncherState>,

        mIsVerticalLayout = mLauncher.getDeviceProfile().isVerticalBarLayout();
        mLauncher.addOnDeviceProfileChangeListener(this);

        onSharedPreferenceChanged(mLauncher.getSharedPrefs(), PREF_KEY_SHOW_SEARCH_IME);
        mLauncher.getSharedPrefs().registerOnSharedPreferenceChangeListener(this);
    }

    public float getShiftRange() {
        return mShiftRange;
    }

    public AllAppsInsetTransitionController getInsetController() {
        return mInsetController;
    }

    @Override
    public void onDeviceProfileChanged(DeviceProfile dp) {
        mIsVerticalLayout = dp.isVerticalBarLayout();
@@ -146,14 +133,7 @@ public class AllAppsTransitionController implements StateHandler<LauncherState>,
        mProgress = progress;

        mScrimView.setProgress(progress);
        float shiftCurrent = progress * mShiftRange;
        mAppsView.setTranslationY(shiftCurrent);
        if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && mSearchImeEnabled) {
            if (mInsetController == null) {
                setupInsetTransitionController();
            }
            mInsetController.setProgress(progress);
        }
        mAppsView.setTranslationY(progress * mShiftRange);
    }

    public float getProgress() {
@@ -242,17 +222,12 @@ public class AllAppsTransitionController implements StateHandler<LauncherState>,
    public void setupViews(AllAppsContainerView appsView, ScrimView scrimView) {
        mAppsView = appsView;
        mScrimView = scrimView;
        if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && BuildCompat.isAtLeastR()) {
            setupInsetTransitionController();
        }
    }

    private void setupInsetTransitionController() {
        mInsetController = new AllAppsInsetTransitionController(mShiftRange, mAppsView);
        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);
        }
    }

    /**
     * Updates the total scroll range but does not update the UI.
@@ -274,23 +249,5 @@ public class AllAppsTransitionController implements StateHandler<LauncherState>,
        if (Float.compare(mProgress, 1f) == 0) {
            mAppsView.reset(false /* animate */);
        }
        if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && mSearchImeEnabled
                && BuildCompat.isAtLeastR()) {
            mInsetController.onAnimationEnd(mProgress);
            if (Float.compare(mProgress, 0f) == 0) {
                EditText editText = mAppsView.getSearchUiManager().getEditText();
                if (editText != null && !mInsetController.showSearchEduIfNecessary()) {
                    editText.requestFocus();
                }
            }
            // TODO: should make the controller hide synchronously
        }
    }

    @Override
    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
        if (s.equals(PREF_KEY_SHOW_SEARCH_IME)) {
            mSearchImeEnabled = sharedPreferences.getBoolean(s, true);
        }
    }
}
Loading