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

Commit c1e28cc2 authored by jasonwshsu's avatar jasonwshsu
Browse files

Observer for the accessibility button mode and notify its listeners for the changes

* Update accessibility button state (visibility) in navigationbar when
accessibility button mode listener receive the changes.

Cherry picked from commit 2c60295b

Bug: 173938894
Bug: 173940864
Test: atest AccessibilityButtonModeObserverTest
Change-Id: I03b961c95a20e4698491443012beb3ab73333199
Merged-In: I03b961c95a20e4698491443012beb3ab73333199
parent eae3a9e4
Loading
Loading
Loading
Loading
+142 −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.systemui.accessibility;

import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;

import android.annotation.IntDef;
import android.annotation.MainThread;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings;

import androidx.annotation.NonNull;

import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.dagger.SysUISingleton;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import javax.inject.Inject;

/**
 * Observes changes of the accessibility button mode
 * {@link Settings.Secure#ACCESSIBILITY_BUTTON_MODE} and notify its listeners.
 */
@MainThread
@SysUISingleton
public class AccessibilityButtonModeObserver {

    private static final int ACCESSIBILITY_BUTTON_MODE_DEFAULT =
            ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;

    private final ContentResolver mContentResolver;
    @VisibleForTesting
    final ContentObserver mContentObserver;

    private final List<ModeChangedListener> mListeners = new ArrayList<>();

    @Retention(RetentionPolicy.SOURCE)
    @IntDef({
            ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR,
            ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU
    })
    private @interface AccessibilityButtonMode {}

    /** Listener for accessibility button mode changes. */
    public interface ModeChangedListener {

        /**
         * Called when accessibility button mode changes.
         *
         * @param mode Current accessibility button mode.
         */
        void onAccessibilityButtonModeChanged(@AccessibilityButtonMode int mode);
    }

    @Inject
    public AccessibilityButtonModeObserver(Context context) {
        mContentResolver = context.getContentResolver();
        mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
            @Override
            public void onChange(boolean selfChange) {
                updateAccessibilityButtonModeChanged();
            }
        };
    }

    /**
     * Registers a listener to receive updates from settings key {@link
     * Settings.Secure#ACCESSIBILITY_BUTTON_MODE}.
     *
     * @param listener A {@link ModeChangedListener} object.
     */
    public void addListener(@NonNull ModeChangedListener listener) {
        Objects.requireNonNull(listener, "listener must be non-null");

        mListeners.add(listener);

        if (mListeners.size() == 1) {
            mContentResolver.registerContentObserver(
                    Settings.Secure.getUriFor(
                            Settings.Secure.ACCESSIBILITY_BUTTON_MODE), /* notifyForDescendants= */
                    false, mContentObserver);
        }
    }

    /**
     * Unregisters a listener previously registered with {@link #addListener(ModeChangedListener)}.
     *
     * @param listener A {@link ModeChangedListener} object.
     */
    public void removeListener(@NonNull ModeChangedListener listener) {
        Objects.requireNonNull(listener, "listener must be non-null");

        mListeners.remove(listener);

        if (mListeners.isEmpty()) {
            mContentResolver.unregisterContentObserver(mContentObserver);
        }
    }

    /**
     * Gets the current accessibility button mode from the current user's settings.
     *
     * See {@link Settings.Secure#ACCESSIBILITY_BUTTON_MODE}.
     */
    @AccessibilityButtonMode
    public int getCurrentAccessibilityButtonMode() {
        return Settings.Secure.getInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
                ACCESSIBILITY_BUTTON_MODE_DEFAULT);
    }

    private void updateAccessibilityButtonModeChanged() {
        final int mode = getCurrentAccessibilityButtonMode();
        final int listenerSize = mListeners.size();
        for (int i = 0; i < listenerSize; i++) {
            mListeners.get(i).onAccessibilityButtonModeChanged(mode);
        }
    }
}
+3 −0
Original line number Diff line number Diff line
@@ -46,6 +46,7 @@ import com.android.keyguard.ViewMediatorCallback;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
import com.android.systemui.accessibility.ModeSwitchesController;
import com.android.systemui.accessibility.SystemActions;
import com.android.systemui.assist.AssistManager;
@@ -213,6 +214,7 @@ public class DependencyProvider {
            MetricsLogger metricsLogger,
            OverviewProxyService overviewProxyService,
            NavigationModeController navigationModeController,
            AccessibilityButtonModeObserver accessibilityButtonModeObserver,
            StatusBarStateController statusBarStateController,
            SysUiState sysUiFlagsContainer,
            BroadcastDispatcher broadcastDispatcher,
@@ -237,6 +239,7 @@ public class DependencyProvider {
                metricsLogger,
                overviewProxyService,
                navigationModeController,
                accessibilityButtonModeObserver,
                statusBarStateController,
                sysUiFlagsContainer,
                broadcastDispatcher,
+24 −2
Original line number Diff line number Diff line
@@ -113,6 +113,7 @@ import com.android.internal.util.LatencyTracker;
import com.android.internal.view.AppearanceRegion;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
import com.android.systemui.accessibility.SystemActions;
import com.android.systemui.assist.AssistHandleViewController;
import com.android.systemui.assist.AssistManager;
@@ -158,7 +159,8 @@ import dagger.Lazy;
 * Contains logic for a navigation bar view.
 */
public class NavigationBar implements View.OnAttachStateChangeListener,
        Callbacks, NavigationModeController.ModeChangedListener, DisplayManager.DisplayListener {
        Callbacks, NavigationModeController.ModeChangedListener,
        AccessibilityButtonModeObserver.ModeChangedListener, DisplayManager.DisplayListener {

    public static final String TAG = "NavigationBar";
    private static final boolean DEBUG = false;
@@ -186,6 +188,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
    private final NotificationRemoteInputManager mNotificationRemoteInputManager;
    private final OverviewProxyService mOverviewProxyService;
    private final NavigationModeController mNavigationModeController;
    private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
    private final BroadcastDispatcher mBroadcastDispatcher;
    private final CommandQueue mCommandQueue;
    private final Optional<Pip> mPipOptional;
@@ -226,6 +229,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,

    private boolean mTransientShown;
    private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
    private int mA11yBtnMode;
    private LightBarController mLightBarController;
    private AutoHideController mAutoHideController;

@@ -443,6 +447,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
            MetricsLogger metricsLogger,
            OverviewProxyService overviewProxyService,
            NavigationModeController navigationModeController,
            AccessibilityButtonModeObserver accessibilityButtonModeObserver,
            StatusBarStateController statusBarStateController,
            SysUiState sysUiFlagsContainer,
            BroadcastDispatcher broadcastDispatcher,
@@ -470,7 +475,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
        mNotificationRemoteInputManager = notificationRemoteInputManager;
        mOverviewProxyService = overviewProxyService;
        mNavigationModeController = navigationModeController;
        mNavBarMode = navigationModeController.addListener(this);
        mAccessibilityButtonModeObserver = accessibilityButtonModeObserver;
        mBroadcastDispatcher = broadcastDispatcher;
        mCommandQueue = commandQueue;
        mPipOptional = pipOptional;
@@ -480,6 +485,10 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
        mHandler = mainHandler;
        mNavbarOverlayController = navbarOverlayController;
        mUiEventLogger = uiEventLogger;

        mNavBarMode = mNavigationModeController.addListener(this);
        mAccessibilityButtonModeObserver.addListener(this);
        mA11yBtnMode = mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode();
    }

    public View getView() {
@@ -560,6 +569,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
        mContext.getSystemService(WindowManager.class).removeViewImmediate(
                mNavigationBarView.getRootView());
        mNavigationModeController.removeListener(this);
        mAccessibilityButtonModeObserver.removeListener(this);

        mAccessibilityManagerWrapper.removeCallback(mAccessibilityListener);
        mContentResolver.unregisterContentObserver(mAssistContentObserver);
@@ -1450,6 +1460,12 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
            outFeedbackEnabled[0] = feedbackEnabled;
        }

        // If accessibility button is floating menu mode, click and long click state should be
        // disabled.
        if (mA11yBtnMode == Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
            return 0;
        }

        return (requestingServices >= 1 ? SYSUI_STATE_A11Y_BUTTON_CLICKABLE : 0)
                | (requestingServices >= 2 ? SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE : 0);
    }
@@ -1540,6 +1556,12 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
        }
    }

    @Override
    public void onAccessibilityButtonModeChanged(int mode) {
        mA11yBtnMode = mode;
        updateAccessibilityServicesState(mAccessibilityManager);
    }

    public void disableAnimationsDuringHide(long delay) {
        mNavigationBarView.setLayoutTransitionsEnabled(false);
        mNavigationBarView.postDelayed(() -> mNavigationBarView.setLayoutTransitionsEnabled(true),
+5 −0
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.RegisterStatusBarResult;
import com.android.settingslib.applications.InterestingConfigChanges;
import com.android.systemui.Dumpable;
import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
import com.android.systemui.accessibility.SystemActions;
import com.android.systemui.assist.AssistHandleViewController;
import com.android.systemui.assist.AssistManager;
@@ -91,6 +92,7 @@ public class NavigationBarController implements Callbacks,
    private final MetricsLogger mMetricsLogger;
    private final OverviewProxyService mOverviewProxyService;
    private final NavigationModeController mNavigationModeController;
    private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
    private final StatusBarStateController mStatusBarStateController;
    private final SysUiState mSysUiFlagsContainer;
    private final BroadcastDispatcher mBroadcastDispatcher;
@@ -127,6 +129,7 @@ public class NavigationBarController implements Callbacks,
            MetricsLogger metricsLogger,
            OverviewProxyService overviewProxyService,
            NavigationModeController navigationModeController,
            AccessibilityButtonModeObserver accessibilityButtonModeObserver,
            StatusBarStateController statusBarStateController,
            SysUiState sysUiFlagsContainer,
            BroadcastDispatcher broadcastDispatcher,
@@ -151,6 +154,7 @@ public class NavigationBarController implements Callbacks,
        mMetricsLogger = metricsLogger;
        mOverviewProxyService = overviewProxyService;
        mNavigationModeController = navigationModeController;
        mAccessibilityButtonModeObserver = accessibilityButtonModeObserver;
        mStatusBarStateController = statusBarStateController;
        mSysUiFlagsContainer = sysUiFlagsContainer;
        mBroadcastDispatcher = broadcastDispatcher;
@@ -289,6 +293,7 @@ public class NavigationBarController implements Callbacks,
                mMetricsLogger,
                mOverviewProxyService,
                mNavigationModeController,
                mAccessibilityButtonModeObserver,
                mStatusBarStateController,
                mSysUiFlagsContainer,
                mBroadcastDispatcher,
+84 −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.systemui.accessibility;


import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;

import android.provider.Settings;
import android.testing.AndroidTestingRunner;

import androidx.test.filters.SmallTest;

import com.android.systemui.SysuiTestCase;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;

@RunWith(AndroidTestingRunner.class)
@SmallTest
public class AccessibilityButtonModeObserverTest extends SysuiTestCase {

    @Rule
    public MockitoRule mockito = MockitoJUnit.rule();

    @Mock
    private AccessibilityButtonModeObserver.ModeChangedListener mListener;

    private AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;

    private static final int TEST_A11Y_BTN_MODE_VALUE =
            Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;

    @Before
    public void setUpController() {
        Settings.Secure.putInt(mContext.getContentResolver(),
                Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
                Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
        mAccessibilityButtonModeObserver = new AccessibilityButtonModeObserver(mContext);
    }

    @Test
    public void onChange_haveListener_invokeCallback() {
        mAccessibilityButtonModeObserver.addListener(mListener);
        Settings.Secure.putInt(mContext.getContentResolver(),
                Settings.Secure.ACCESSIBILITY_BUTTON_MODE, TEST_A11Y_BTN_MODE_VALUE);

        mAccessibilityButtonModeObserver.mContentObserver.onChange(false);

        verify(mListener).onAccessibilityButtonModeChanged(TEST_A11Y_BTN_MODE_VALUE);
    }

    @Test
    public void onChange_noListener_noInvokeCallback() {
        mAccessibilityButtonModeObserver.addListener(mListener);
        mAccessibilityButtonModeObserver.removeListener(mListener);
        Settings.Secure.putInt(mContext.getContentResolver(),
                Settings.Secure.ACCESSIBILITY_BUTTON_MODE, TEST_A11Y_BTN_MODE_VALUE);

        mAccessibilityButtonModeObserver.mContentObserver.onChange(false);

        verify(mListener, never()).onAccessibilityButtonModeChanged(anyInt());
    }
}
Loading