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

Commit 1cf6eac2 authored by Matthew Ng's avatar Matthew Ng
Browse files

Prototype settings can remap the gestures and actions

Introduced a way for prototype settings to be able to remap the gestures
to actions via a controller. Also the back action is not restricted to a
button drag anymore. Back button visibility from prototype settings
will also signal when to hide the back button immediately.

Bug: 112934365
Test: atest QuickStepControllerTest
Change-Id: Ib5215162edb50162d6f492c5e8a07786c1e2223b
parent 9d7964b9
Loading
Loading
Loading
Loading
+2 −21
Original line number Diff line number Diff line
@@ -16,8 +16,6 @@

package com.android.systemui.statusbar.phone;

import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_HOME;

import android.annotation.NonNull;
import android.hardware.input.InputManager;
import android.os.Handler;
@@ -35,10 +33,8 @@ import com.android.systemui.recents.OverviewProxyService;
 */
public class NavigationBackAction extends NavigationGestureAction {

    private static final String PULL_HOME_GO_BACK_PROP = "quickstepcontroller_homegoesback";
    private static final String BACK_AFTER_END_PROP =
            "quickstepcontroller_homegoesbackwhenend";
    private static final String NAVBAR_EXPERIMENTS_DISABLED = "navbarexperiments_disabled";
    private static final long BACK_BUTTON_FADE_OUT_ALPHA = 60;
    private static final long BACK_GESTURE_POLL_TIMEOUT = 1000;

@@ -59,16 +55,6 @@ public class NavigationBackAction extends NavigationGestureAction {
        super(navigationBarView, service);
    }

    @Override
    public int requiresTouchDownHitTarget() {
        return HIT_TARGET_HOME;
    }

    @Override
    public boolean requiresDragWithHitTarget() {
        return true;
    }

    @Override
    public boolean canPerformAction() {
        return mProxySender.getBackButtonAlpha() > 0;
@@ -76,7 +62,7 @@ public class NavigationBackAction extends NavigationGestureAction {

    @Override
    public boolean isEnabled() {
        return swipeHomeGoBackGestureEnabled();
        return !getGlobalBoolean(NavigationPrototypeController.NAVBAR_EXPERIMENTS_DISABLED);
    }

    @Override
@@ -110,13 +96,8 @@ public class NavigationBackAction extends NavigationGestureAction {
        mNavigationBarView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
    }

    private boolean swipeHomeGoBackGestureEnabled() {
        return !getGlobalBoolean(NAVBAR_EXPERIMENTS_DISABLED)
                && getGlobalBoolean(PULL_HOME_GO_BACK_PROP);
    }

    private boolean shouldExecuteBackOnUp() {
        return !getGlobalBoolean(NAVBAR_EXPERIMENTS_DISABLED)
        return !getGlobalBoolean(NavigationPrototypeController.NAVBAR_EXPERIMENTS_DISABLED)
                && getGlobalBoolean(BACK_AFTER_END_PROP);
    }

+51 −2
Original line number Diff line number Diff line
@@ -77,6 +77,8 @@ import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.NavigationBarCompat;
import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.systemui.statusbar.phone.NavigationPrototypeController.GestureAction;
import com.android.systemui.statusbar.phone.NavigationPrototypeController.OnPrototypeChangedListener;
import com.android.systemui.statusbar.policy.DeadZone;
import com.android.systemui.statusbar.policy.KeyButtonDrawable;

@@ -146,6 +148,8 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
    private RecentsOnboarding mRecentsOnboarding;
    private NotificationPanelView mPanelView;

    private NavigationPrototypeController mPrototypeController;
    private NavigationGestureAction[] mDefaultGestureMap;
    private QuickScrubAction mQuickScrubAction;
    private QuickStepAction mQuickStepAction;
    private NavigationBackAction mBackAction;
@@ -261,6 +265,18 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
        }
    };

    private OnPrototypeChangedListener mPrototypeListener = new OnPrototypeChangedListener() {
        @Override
        public void onGestureRemap(int[] actions) {
            updateNavigationGestures();
        }

        @Override
        public void onBackButtonVisibilityChanged(boolean visible) {
            getBackButton().setVisibility(visible ? VISIBLE : GONE);
        }
    };

    public NavigationBarView(Context context, AttributeSet attrs) {
        super(context, attrs);

@@ -309,6 +325,14 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
        mQuickScrubAction = new QuickScrubAction(this, mOverviewProxyService);
        mQuickStepAction = new QuickStepAction(this, mOverviewProxyService);
        mBackAction = new NavigationBackAction(this, mOverviewProxyService);
        mDefaultGestureMap = new NavigationGestureAction[] {
                mQuickStepAction, null /* swipeDownAction*/, null /* swipeLeftAction */,
                mQuickScrubAction
        };

        mPrototypeController = new NavigationPrototypeController(mHandler, mContext);
        mPrototypeController.register();
        mPrototypeController.setOnPrototypeChangedListener(mPrototypeListener);
    }

    public BarTransitions getBarTransitions() {
@@ -323,8 +347,32 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
        mPanelView = panel;
        if (mGestureHelper instanceof QuickStepController) {
            ((QuickStepController) mGestureHelper).setComponents(this);
            ((QuickStepController) mGestureHelper).setGestureActions(mQuickStepAction,
                    null /* swipeDownAction*/, mBackAction, mQuickScrubAction);
            updateNavigationGestures();
        }
    }

    private void updateNavigationGestures() {
        if (mGestureHelper instanceof QuickStepController) {
            final int[] assignedMap = mPrototypeController.getGestureActionMap();
            ((QuickStepController) mGestureHelper).setGestureActions(
                    getNavigationActionFromType(assignedMap[0], mDefaultGestureMap[0]),
                    getNavigationActionFromType(assignedMap[1], mDefaultGestureMap[1]),
                    getNavigationActionFromType(assignedMap[2], mDefaultGestureMap[2]),
                    getNavigationActionFromType(assignedMap[3], mDefaultGestureMap[3]));
        }
    }

    private NavigationGestureAction getNavigationActionFromType(@GestureAction int actionType,
            NavigationGestureAction defaultAction) {
        switch(actionType) {
            case NavigationPrototypeController.ACTION_QUICKSTEP:
                return mQuickStepAction;
            case NavigationPrototypeController.ACTION_QUICKSCRUB:
                return mQuickScrubAction;
            case NavigationPrototypeController.ACTION_BACK:
                return mBackAction;
            default:
                return defaultAction;
        }
    }

@@ -1043,6 +1091,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
        if (mGestureHelper != null) {
            mGestureHelper.destroy();
        }
        mPrototypeController.unregister();
        setUpSwipeUpOnboarding(false);
        for (int i = 0; i < mButtonDispatchers.size(); ++i) {
            mButtonDispatchers.valueAt(i).onDestroy();
+142 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2008 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.systemui.statusbar.phone;

import android.annotation.IntDef;
import android.content.Context;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;

import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * Coordinates with the prototype settings plugin app that uses Settings.Global to allow different
 * prototypes to run in the system. The class will handle communication changes from the settings
 * app and call back to listeners.
 */
public class NavigationPrototypeController extends ContentObserver {
    private static final String HIDE_BACK_BUTTON_SETTING = "quickstepcontroller_hideback";

    static final String NAVBAR_EXPERIMENTS_DISABLED = "navbarexperiments_disabled";
    private final String GESTURE_MATCH_SETTING = "quickstepcontroller_gesture_match_map";

    @Retention(RetentionPolicy.SOURCE)
    @IntDef({ACTION_DEFAULT, ACTION_QUICKSTEP, ACTION_QUICKSCRUB, ACTION_BACK})
    @interface GestureAction {}
    static final int ACTION_DEFAULT = 0;
    static final int ACTION_QUICKSTEP = 1;
    static final int ACTION_QUICKSCRUB = 2;
    static final int ACTION_BACK = 3;

    private OnPrototypeChangedListener mListener;

    /**
     * Each index corresponds to a different action set in QuickStepController
     * {@see updateSwipeLTRBackSetting}
     */
    private int[] mActionMap = new int[4];

    private final Context mContext;

    public NavigationPrototypeController(Handler handler, Context context) {
        super(handler);
        mContext = context;
        updateSwipeLTRBackSetting();
    }

    public void setOnPrototypeChangedListener(OnPrototypeChangedListener listener) {
        mListener = listener;
    }

    /**
     * Observe all the settings to react to from prototype settings
     */
    public void register() {
        registerObserver(HIDE_BACK_BUTTON_SETTING);
        registerObserver(GESTURE_MATCH_SETTING);
    }

    /**
     * Disable observing settings to react to from prototype settings
     */
    public void unregister() {
        mContext.getContentResolver().unregisterContentObserver(this);
    }

    @Override
    public void onChange(boolean selfChange, Uri uri) {
        super.onChange(selfChange, uri);
        if (!selfChange && mListener != null) {
            try {
                final String path = uri.getPath();
                if (path.endsWith(GESTURE_MATCH_SETTING)) {
                    // Get the settings gesture map corresponding to each action
                    // {@see updateSwipeLTRBackSetting}
                    updateSwipeLTRBackSetting();
                    mListener.onGestureRemap(mActionMap);
                } else if (path.endsWith(HIDE_BACK_BUTTON_SETTING)) {
                    mListener.onBackButtonVisibilityChanged(
                            !getGlobalBool(HIDE_BACK_BUTTON_SETTING));
                }
            } catch (SettingNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Retrieve the action map to apply to the quick step controller
     * @return an action map
     */
    int[] getGestureActionMap() {
        return mActionMap;
    }

    /**
     * Since Settings.Global cannot pass arrays, use a string to represent each character as a
     * gesture map to actions corresponding to {@see GestureAction}. The number is represented as:
     * Number: [up] [down] [left] [right]
     */
    private void updateSwipeLTRBackSetting() {
        String value = Settings.Global.getString(mContext.getContentResolver(),
                GESTURE_MATCH_SETTING);
        if (value != null) {
            for (int i = 0; i < mActionMap.length; ++i) {
                mActionMap[i] = Character.getNumericValue(value.charAt(i));
            }
        }
    }

    private boolean getGlobalBool(String name) throws SettingNotFoundException {
        return Settings.Global.getInt(mContext.getContentResolver(), name) == 1;
    }

    private void registerObserver(String name) {
        mContext.getContentResolver()
                .registerContentObserver(Settings.Global.getUriFor(name), false, this);
    }

    public interface OnPrototypeChangedListener {
        void onGestureRemap(@GestureAction int[] actions);
        void onBackButtonVisibilityChanged(boolean visible);
    }
}
+5 −2
Original line number Diff line number Diff line
@@ -30,9 +30,9 @@ import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_

import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.RemoteException;
import android.provider.Settings;
import android.util.Log;
@@ -100,6 +100,7 @@ public class QuickStepController implements GestureHelper {
    private NavigationGestureAction mCurrentAction;
    private NavigationGestureAction[] mGestureActions = new NavigationGestureAction[MAX_GESTURES];

    private final Rect mLastLayoutRect = new Rect();
    private final OverviewProxyService mOverviewEventSender;
    private final Context mContext;
    private final StatusBar mStatusBar;
@@ -107,7 +108,6 @@ public class QuickStepController implements GestureHelper {
    private final Matrix mTransformLocalMatrix = new Matrix();

    public QuickStepController(Context context) {
        final Resources res = context.getResources();
        mContext = context;
        mStatusBar = SysUiServiceProvider.getComponent(context, StatusBar.class);
        mOverviewEventSender = Dependency.get(OverviewProxyService.class);
@@ -142,6 +142,8 @@ public class QuickStepController implements GestureHelper {
            if (action != null) {
                action.setBarState(true, mNavBarPosition, mDragHPositive, mDragVPositive);
                action.onDarkIntensityChange(mDarkIntensity);
                action.onLayout(true /* changed */, mLastLayoutRect.left, mLastLayoutRect.top,
                        mLastLayoutRect.right, mLastLayoutRect.bottom);
            }
        }
    }
@@ -382,6 +384,7 @@ public class QuickStepController implements GestureHelper {
                action.onLayout(changed, left, top, right, bottom);
            }
        }
        mLastLayoutRect.set(left, top, right, bottom);
    }

    @Override