diff options
8 files changed, 519 insertions, 186 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java new file mode 100644 index 000000000000..52103d3bd739 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java @@ -0,0 +1,246 @@ +/* + * 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.navigationbar; + +import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU; + +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE; + +import android.content.ContentResolver; +import android.content.Context; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.UserHandle; +import android.provider.Settings; +import android.view.accessibility.AccessibilityManager; + +import androidx.annotation.NonNull; + +import com.android.systemui.Dumpable; +import com.android.systemui.accessibility.AccessibilityButtonModeObserver; +import com.android.systemui.assist.AssistManager; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dump.DumpManager; +import com.android.systemui.recents.OverviewProxyService; +import com.android.systemui.settings.UserTracker; +import com.android.systemui.shared.system.QuickStepContract; +import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import dagger.Lazy; + +/** + * Extracts shared elements between navbar and taskbar delegate to de-dupe logic and help them + * experience the joys of friendship. + * The events are then passed through + * + * Currently consolidates + * * A11y + * * Assistant + */ +@SysUISingleton +public final class NavBarHelper implements + AccessibilityButtonModeObserver.ModeChangedListener, + OverviewProxyService.OverviewProxyListener, NavigationModeController.ModeChangedListener, + Dumpable { + private final AccessibilityManager mAccessibilityManager; + private final Lazy<AssistManager> mAssistManagerLazy; + private final UserTracker mUserTracker; + private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver; + private final List<NavbarTaskbarStateUpdater> mA11yEventListeners = new ArrayList<>(); + private Context mContext; + private ContentResolver mContentResolver; + private boolean mAssistantAvailable; + private boolean mLongPressHomeEnabled; + private boolean mAssistantTouchGestureEnabled; + private int mNavBarMode; + + private final ContentObserver mAssistContentObserver = new ContentObserver( + new Handler(Looper.getMainLooper())) { + @Override + public void onChange(boolean selfChange, Uri uri) { + updateAssitantAvailability(); + } + }; + + @Inject + public NavBarHelper(AccessibilityManager accessibilityManager, + AccessibilityManagerWrapper accessibilityManagerWrapper, + AccessibilityButtonModeObserver accessibilityButtonModeObserver, + OverviewProxyService overviewProxyService, + Lazy<AssistManager> assistManagerLazy, + NavigationModeController navigationModeController, + UserTracker userTracker, + DumpManager dumpManager) { + mAccessibilityManager = accessibilityManager; + mAssistManagerLazy = assistManagerLazy; + mUserTracker = userTracker; + accessibilityManagerWrapper.addCallback( + accessibilityManager1 -> NavBarHelper.this.dispatchA11yEventUpdate()); + mAccessibilityButtonModeObserver = accessibilityButtonModeObserver; + + mAccessibilityButtonModeObserver.addListener(this); + mNavBarMode = navigationModeController.addListener(this); + overviewProxyService.addCallback(this); + dumpManager.registerDumpable(this); + } + + public void init(Context context) { + mContext = context; + mContentResolver = mContext.getContentResolver(); + mContentResolver.registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.ASSISTANT), + false /* notifyForDescendants */, mAssistContentObserver, UserHandle.USER_ALL); + mContentResolver.registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED), + false, mAssistContentObserver, UserHandle.USER_ALL); + mContentResolver.registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED), + false, mAssistContentObserver, UserHandle.USER_ALL); + updateAssitantAvailability(); + } + + public void destroy() { + mContentResolver.unregisterContentObserver(mAssistContentObserver); + } + + /** + * @param listener Will immediately get callbacks based on current state + */ + public void registerNavTaskStateUpdater(NavbarTaskbarStateUpdater listener) { + mA11yEventListeners.add(listener); + listener.updateAccessibilityServicesState(); + listener.updateAssistantAvailable(mAssistantAvailable); + } + + public void removeNavTaskStateUpdater(NavbarTaskbarStateUpdater listener) { + mA11yEventListeners.remove(listener); + } + + private void dispatchA11yEventUpdate() { + for (NavbarTaskbarStateUpdater listener : mA11yEventListeners) { + listener.updateAccessibilityServicesState(); + } + } + + private void dispatchAssistantEventUpdate(boolean assistantAvailable) { + for (NavbarTaskbarStateUpdater listener : mA11yEventListeners) { + listener.updateAssistantAvailable(assistantAvailable); + } + } + + @Override + public void onAccessibilityButtonModeChanged(int mode) { + dispatchA11yEventUpdate(); + } + + /** + * See {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_CLICKABLE} and + * {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE} + * + * @return the a11y button clickable and long_clickable states, or 0 if there is no + * a11y button in the navbar + */ + public int getA11yButtonState() { + // AccessibilityManagerService resolves services for the current user since the local + // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission + final List<String> a11yButtonTargets = + mAccessibilityManager.getAccessibilityShortcutTargets( + AccessibilityManager.ACCESSIBILITY_BUTTON); + final int requestingServices = a11yButtonTargets.size(); + + // If accessibility button is floating menu mode, click and long click state should be + // disabled. + if (mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode() + == 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); + } + + @Override + public void onConnectionChanged(boolean isConnected) { + if (isConnected) { + updateAssitantAvailability(); + } + } + + private void updateAssitantAvailability() { + boolean assistantAvailableForUser = mAssistManagerLazy.get() + .getAssistInfoForUser(UserHandle.USER_CURRENT) != null; + boolean longPressDefault = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_assistLongPressHomeEnabledDefault); + mLongPressHomeEnabled = Settings.Secure.getIntForUser(mContentResolver, + Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, longPressDefault ? 1 : 0, + mUserTracker.getUserId()) != 0; + boolean gestureDefault = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_assistTouchGestureEnabledDefault); + mAssistantTouchGestureEnabled = Settings.Secure.getIntForUser(mContentResolver, + Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED, gestureDefault ? 1 : 0, + mUserTracker.getUserId()) != 0; + + mAssistantAvailable = assistantAvailableForUser + && mAssistantTouchGestureEnabled + && QuickStepContract.isGesturalMode(mNavBarMode); + dispatchAssistantEventUpdate(mAssistantAvailable); + } + + public boolean getLongPressHomeEnabled() { + return mLongPressHomeEnabled; + } + + @Override + public void startAssistant(Bundle bundle) { + mAssistManagerLazy.get().startAssist(bundle); + } + + @Override + public void onNavigationModeChanged(int mode) { + mNavBarMode = mode; + updateAssitantAvailability(); + } + + /** + * Callbacks will get fired once immediately after registering via + * {@link #registerNavTaskStateUpdater(NavbarTaskbarStateUpdater)} + */ + public interface NavbarTaskbarStateUpdater { + void updateAccessibilityServicesState(); + void updateAssistantAvailable(boolean available); + } + + @Override + public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { + pw.println("NavbarTaskbarFriendster"); + pw.println(" longPressHomeEnabled=" + mLongPressHomeEnabled); + pw.println(" mAssistantTouchGestureEnabled=" + mAssistantTouchGestureEnabled); + pw.println(" mAssistantAvailable=" + mAssistantAvailable); + pw.println(" mNavBarMode=" + mNavBarMode); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index bdacc4118442..e0da9a0dca2c 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -67,22 +67,18 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.res.Configuration; -import android.database.ContentObserver; import android.graphics.Insets; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.RectF; import android.inputmethodservice.InputMethodService; -import android.net.Uri; import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; -import android.os.Looper; import android.os.RemoteException; import android.os.UserHandle; import android.provider.DeviceConfig; -import android.provider.Settings; import android.telecom.TelecomManager; import android.text.TextUtils; import android.util.Log; @@ -200,8 +196,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, private final Handler mHandler; private final NavigationBarOverlayController mNavbarOverlayController; private final UiEventLogger mUiEventLogger; - private final NavigationBarA11yHelper mNavigationBarA11yHelper; - private final UserTracker mUserTracker; + private final NavBarHelper mNavBarHelper; private final NotificationShadeDepthController mNotificationShadeDepthController; private Bundle mSavedState; @@ -213,9 +208,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, private int mNavigationIconHints = 0; private @TransitionMode int mNavigationBarMode; private ContentResolver mContentResolver; - private boolean mAssistantAvailable; private boolean mLongPressHomeEnabled; - private boolean mAssistantTouchGestureEnabled; private int mDisabledFlags1; private int mDisabledFlags2; @@ -309,16 +302,31 @@ public class NavigationBar implements View.OnAttachStateChangeListener, } }; + private final NavBarHelper.NavbarTaskbarStateUpdater mNavbarTaskbarStateUpdater = + new NavBarHelper.NavbarTaskbarStateUpdater() { + @Override + public void updateAccessibilityServicesState() { + updateAcessibilityStateFlags(); + } + + @Override + public void updateAssistantAvailable(boolean available) { + // TODO(b/198002034): Content observers currently can still be called back after + // being unregistered, and in this case we can ignore the change if the nav bar + // has been destroyed already + if (mNavigationBarView == null) { + return; + } + mLongPressHomeEnabled = mNavBarHelper.getLongPressHomeEnabled(); + updateAssistantEntrypoints(available); + } + }; + private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() { @Override public void onConnectionChanged(boolean isConnected) { mNavigationBarView.updateStates(); updateScreenPinningGestures(); - - // Send the assistant availability upon connection - if (isConnected) { - updateAssistantEntrypoints(); - } } @Override @@ -421,20 +429,6 @@ public class NavigationBar implements View.OnAttachStateChangeListener, } }; - private final ContentObserver mAssistContentObserver = new ContentObserver( - new Handler(Looper.getMainLooper())) { - @Override - public void onChange(boolean selfChange, Uri uri) { - // TODO(b/198002034): Content observers currently can still be called back after being - // unregistered, and in this case we can ignore the change if the nav bar has been - // destroyed already - if (mNavigationBarView == null) { - return; - } - updateAssistantEntrypoints(); - } - }; - private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener = new DeviceConfig.OnPropertiesChangedListener() { @Override @@ -504,7 +498,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, @Main Handler mainHandler, NavigationBarOverlayController navbarOverlayController, UiEventLogger uiEventLogger, - NavigationBarA11yHelper navigationBarA11yHelper, + NavBarHelper navBarHelper, UserTracker userTracker, LightBarController mainLightBarController, LightBarController.Factory lightBarControllerFactory, @@ -535,8 +529,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, mHandler = mainHandler; mNavbarOverlayController = navbarOverlayController; mUiEventLogger = uiEventLogger; - mNavigationBarA11yHelper = navigationBarA11yHelper; - mUserTracker = userTracker; + mNavBarHelper = navBarHelper; mNotificationShadeDepthController = notificationShadeDepthController; mMainLightBarController = mainLightBarController; mLightBarControllerFactory = lightBarControllerFactory; @@ -568,18 +561,9 @@ public class NavigationBar implements View.OnAttachStateChangeListener, mIsOnDefaultDisplay = mDisplayId == DEFAULT_DISPLAY; mCommandQueue.addCallback(this); - mAssistantAvailable = mAssistManagerLazy.get() - .getAssistInfoForUser(UserHandle.USER_CURRENT) != null; + mLongPressHomeEnabled = mNavBarHelper.getLongPressHomeEnabled(); mContentResolver = mContext.getContentResolver(); - mContentResolver.registerContentObserver( - Settings.Secure.getUriFor(Settings.Secure.ASSISTANT), - false /* notifyForDescendants */, mAssistContentObserver, UserHandle.USER_ALL); - mContentResolver.registerContentObserver( - Settings.Secure.getUriFor(Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED), - false, mAssistContentObserver, UserHandle.USER_ALL); - mContentResolver.registerContentObserver( - Settings.Secure.getUriFor(Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED), - false, mAssistContentObserver, UserHandle.USER_ALL); + mNavBarHelper.init(mContext); mAllowForceNavBarHandleOpaque = mContext.getResources().getBoolean( R.bool.allow_force_nav_bar_handle_opaque); mForceNavBarHandleOpaque = DeviceConfig.getBoolean( @@ -593,7 +577,6 @@ public class NavigationBar implements View.OnAttachStateChangeListener, )).filter(duration -> duration != 0); DeviceConfig.addOnPropertiesChangedListener( DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post, mOnPropertiesChangedListener); - updateAssistantEntrypoints(); if (savedState != null) { mDisabledFlags1 = savedState.getInt(EXTRA_DISABLE_STATE, 0); @@ -620,8 +603,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener, mWindowManager.removeViewImmediate(mNavigationBarView.getRootView()); mNavigationModeController.removeListener(this); - mNavigationBarA11yHelper.removeA11yEventListener(mAccessibilityListener); - mContentResolver.unregisterContentObserver(mAssistContentObserver); + mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater); + mNavBarHelper.destroy(); mDeviceProvisionedController.removeCallback(mUserSetupListener); mNotificationShadeDepthController.removeListener(mDepthListener); @@ -643,7 +626,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, mNavigationBarView.setWindowVisible(isNavBarWindowVisible()); mNavigationBarView.setBehavior(mBehavior); - mNavigationBarA11yHelper.registerA11yEventListener(mAccessibilityListener); + mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater); mSplitScreenOptional.ifPresent(mNavigationBarView::registerDockedListener); mPipOptional.ifPresent(mNavigationBarView::registerPipExclusionBoundsChangeListener); @@ -716,7 +699,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, mHandler.removeCallbacks(mAutoDim); mHandler.removeCallbacks(mOnVariableDurationHomeLongClick); mHandler.removeCallbacks(mEnableLayoutTransitions); - mNavigationBarA11yHelper.removeA11yEventListener(mAccessibilityListener); + mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater); mFrame = null; mNavigationBarView = null; mOrientationHandle = null; @@ -885,7 +868,6 @@ public class NavigationBar implements View.OnAttachStateChangeListener, pw.println(" mCurrentRotation=" + mCurrentRotation); pw.println(" mHomeButtonLongPressDurationMs=" + mHomeButtonLongPressDurationMs); pw.println(" mLongPressHomeEnabled=" + mLongPressHomeEnabled); - pw.println(" mAssistantTouchGestureEnabled=" + mAssistantTouchGestureEnabled); pw.println(" mNavigationBarWindowState=" + windowStateToString(mNavigationBarWindowState)); pw.println(" mNavigationBarMode=" @@ -1175,7 +1157,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton(); accessibilityButton.setOnClickListener(this::onAccessibilityClick); accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick); - updateAccessibilityServicesState(); + updateAcessibilityStateFlags(); ButtonDispatcher imeSwitcherButton = mNavigationBarView.getImeSwitchButton(); imeSwitcherButton.setOnClickListener(this::onImeSwitcherClick); @@ -1400,8 +1382,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener, return true; } - void updateAccessibilityServicesState() { - int a11yFlags = mNavigationBarA11yHelper.getA11yButtonState(); + void updateAcessibilityStateFlags() { + int a11yFlags = mNavBarHelper.getA11yButtonState(); if (mNavigationBarView != null) { boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0; @@ -1413,7 +1395,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, public void updateSystemUiStateFlags(int a11yFlags) { if (a11yFlags < 0) { - a11yFlags = mNavigationBarA11yHelper.getA11yButtonState(); + a11yFlags = mNavBarHelper.getA11yButtonState(); } boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0; boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0; @@ -1440,24 +1422,10 @@ public class NavigationBar implements View.OnAttachStateChangeListener, } } - private void updateAssistantEntrypoints() { - mAssistantAvailable = mAssistManagerLazy.get() - .getAssistInfoForUser(UserHandle.USER_CURRENT) != null; - boolean longPressDefault = mContext.getResources().getBoolean( - com.android.internal.R.bool.config_assistLongPressHomeEnabledDefault); - mLongPressHomeEnabled = Settings.Secure.getIntForUser(mContentResolver, - Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, longPressDefault ? 1 : 0, - mUserTracker.getUserId()) != 0; - boolean gestureDefault = mContext.getResources().getBoolean( - com.android.internal.R.bool.config_assistTouchGestureEnabledDefault); - mAssistantTouchGestureEnabled = Settings.Secure.getIntForUser(mContentResolver, - Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED, gestureDefault ? 1 : 0, - mUserTracker.getUserId()) != 0; + private void updateAssistantEntrypoints(boolean assistantAvailable) { if (mOverviewProxyService.getProxy() != null) { try { - mOverviewProxyService.getProxy().onAssistantAvailable(mAssistantAvailable - && mAssistantTouchGestureEnabled - && QuickStepContract.isGesturalMode(mNavBarMode)); + mOverviewProxyService.getProxy().onAssistantAvailable(assistantAvailable); } catch (RemoteException e) { Log.w(TAG, "Unable to send assistant availability data to launcher"); } @@ -1528,8 +1496,6 @@ public class NavigationBar implements View.OnAttachStateChangeListener, @Override public void onNavigationModeChanged(int mode) { mNavBarMode = mode; - // update assistant entry points on system navigation radio button click - updateAssistantEntrypoints(); if (!QuickStepContract.isGesturalMode(mode)) { // Reset the override alpha @@ -1568,9 +1534,6 @@ public class NavigationBar implements View.OnAttachStateChangeListener, mNavigationBarView.getBarTransitions().finishAnimations(); } - private final NavigationBarA11yHelper.NavA11yEventListener mAccessibilityListener = - this::updateAccessibilityServicesState; - private WindowManager.LayoutParams getBarLayoutParams(int rotation) { WindowManager.LayoutParams lp = getBarLayoutParamsForRotation(rotation); lp.paramsForRotation = new WindowManager.LayoutParams[4]; @@ -1674,7 +1637,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, } if (Intent.ACTION_USER_SWITCHED.equals(action)) { // The accessibility settings may be different for the new user - updateAccessibilityServicesState(); + updateAcessibilityStateFlags(); } } }; @@ -1710,7 +1673,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, private final Handler mMainHandler; private final NavigationBarOverlayController mNavbarOverlayController; private final UiEventLogger mUiEventLogger; - private final NavigationBarA11yHelper mNavigationBarA11yHelper; + private final NavBarHelper mNavBarHelper; private final UserTracker mUserTracker; private final LightBarController mMainLightBarController; private final LightBarController.Factory mLightBarControllerFactory; @@ -1743,7 +1706,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, @Main Handler mainHandler, NavigationBarOverlayController navbarOverlayController, UiEventLogger uiEventLogger, - NavigationBarA11yHelper navigationBarA11yHelper, + NavBarHelper navBarHelper, UserTracker userTracker, LightBarController mainLightBarController, LightBarController.Factory lightBarControllerFactory, @@ -1773,7 +1736,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, mMainHandler = mainHandler; mNavbarOverlayController = navbarOverlayController; mUiEventLogger = uiEventLogger; - mNavigationBarA11yHelper = navigationBarA11yHelper; + mNavBarHelper = navBarHelper; mUserTracker = userTracker; mMainLightBarController = mainLightBarController; mLightBarControllerFactory = lightBarControllerFactory; @@ -1794,7 +1757,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, mSplitScreenOptional, mRecentsOptional, mStatusBarOptionalLazy, mShadeController, mNotificationRemoteInputManager, mNotificationShadeDepthController, mSystemActions, mMainHandler, - mNavbarOverlayController, mUiEventLogger, mNavigationBarA11yHelper, + mNavbarOverlayController, mUiEventLogger, mNavBarHelper, mUserTracker, mMainLightBarController, mLightBarControllerFactory, mMainAutoHideController, mAutoHideControllerFactory, mTelecomManagerOptional, mInputMethodManager); diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java deleted file mode 100644 index 13e6d8b410d6..000000000000 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java +++ /dev/null @@ -1,90 +0,0 @@ -package com.android.systemui.navigationbar; - -import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU; - -import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE; -import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE; - -import android.view.accessibility.AccessibilityManager; - -import com.android.systemui.accessibility.AccessibilityButtonModeObserver; -import com.android.systemui.dagger.SysUISingleton; -import com.android.systemui.shared.system.QuickStepContract; -import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; - -import java.util.ArrayList; -import java.util.List; - -import javax.inject.Inject; - -/** - * Extracts shared elements of a11y necessary between navbar and taskbar delegate - */ -@SysUISingleton -public final class NavigationBarA11yHelper implements - AccessibilityButtonModeObserver.ModeChangedListener { - private final AccessibilityManager mAccessibilityManager; - private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver; - private final List<NavA11yEventListener> mA11yEventListeners = new ArrayList<>(); - - @Inject - public NavigationBarA11yHelper(AccessibilityManager accessibilityManager, - AccessibilityManagerWrapper accessibilityManagerWrapper, - AccessibilityButtonModeObserver accessibilityButtonModeObserver) { - mAccessibilityManager = accessibilityManager; - accessibilityManagerWrapper.addCallback( - accessibilityManager1 -> NavigationBarA11yHelper.this.dispatchEventUpdate()); - mAccessibilityButtonModeObserver = accessibilityButtonModeObserver; - - mAccessibilityButtonModeObserver.addListener(this); - } - - public void registerA11yEventListener(NavA11yEventListener listener) { - mA11yEventListeners.add(listener); - } - - public void removeA11yEventListener(NavA11yEventListener listener) { - mA11yEventListeners.remove(listener); - } - - private void dispatchEventUpdate() { - for (NavA11yEventListener listener : mA11yEventListeners) { - listener.updateAccessibilityServicesState(); - } - } - - @Override - public void onAccessibilityButtonModeChanged(int mode) { - dispatchEventUpdate(); - } - - /** - * See {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_CLICKABLE} and - * {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE} - * - * @return the a11y button clickable and long_clickable states, or 0 if there is no - * a11y button in the navbar - */ - public int getA11yButtonState() { - // AccessibilityManagerService resolves services for the current user since the local - // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission - final List<String> a11yButtonTargets = - mAccessibilityManager.getAccessibilityShortcutTargets( - AccessibilityManager.ACCESSIBILITY_BUTTON); - final int requestingServices = a11yButtonTargets.size(); - - // If accessibility button is floating menu mode, click and long click state should be - // disabled. - if (mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode() - == 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); - } - - public interface NavA11yEventListener { - void updateAccessibilityServicesState(); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java index 3dc79c43d894..339877824b16 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java @@ -100,7 +100,7 @@ public class NavigationBarController implements CommandQueue commandQueue, @Main Handler mainHandler, ConfigurationController configurationController, - NavigationBarA11yHelper navigationBarA11yHelper, + NavBarHelper navBarHelper, TaskbarDelegate taskbarDelegate, NavigationBar.Factory navigationBarFactory, DumpManager dumpManager, @@ -115,7 +115,7 @@ public class NavigationBarController implements mNavMode = navigationModeController.addListener(this); mTaskbarDelegate = taskbarDelegate; mTaskbarDelegate.setDependencies(commandQueue, overviewProxyService, - navigationBarA11yHelper, navigationModeController, sysUiFlagsContainer, + navBarHelper, navigationModeController, sysUiFlagsContainer, dumpManager, autoHideController); mIsTablet = isTablet(mContext); dumpManager.registerDumpable(this); diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java index d707dbdf5cba..428d9d6993b7 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java @@ -43,6 +43,8 @@ import android.content.res.Configuration; import android.hardware.display.DisplayManager; import android.inputmethodservice.InputMethodService; import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; import android.view.Display; import android.view.InsetsVisibilities; import android.view.View; @@ -73,19 +75,30 @@ import javax.inject.Singleton; public class TaskbarDelegate implements CommandQueue.Callbacks, OverviewProxyService.OverviewProxyListener, NavigationModeController.ModeChangedListener, ComponentCallbacks, Dumpable { + private static final String TAG = TaskbarDelegate.class.getSimpleName(); private final EdgeBackGestureHandler mEdgeBackGestureHandler; - + private boolean mInitialized; private CommandQueue mCommandQueue; private OverviewProxyService mOverviewProxyService; - private NavigationBarA11yHelper mNavigationBarA11yHelper; + private NavBarHelper mNavBarHelper; private NavigationModeController mNavigationModeController; private SysUiState mSysUiState; private AutoHideController mAutoHideController; private int mDisplayId; private int mNavigationIconHints; - private final NavigationBarA11yHelper.NavA11yEventListener mNavA11yEventListener = - this::updateSysuiFlags; + private final NavBarHelper.NavbarTaskbarStateUpdater mNavbarTaskbarStateUpdater = + new NavBarHelper.NavbarTaskbarStateUpdater() { + @Override + public void updateAccessibilityServicesState() { + updateSysuiFlags(); + } + + @Override + public void updateAssistantAvailable(boolean available) { + updateAssistantAvailability(available); + } + }; private int mDisabledFlags; private @WindowVisibleState int mTaskBarWindowState = WINDOW_STATE_SHOWING; private @Behavior int mBehavior; @@ -125,14 +138,14 @@ public class TaskbarDelegate implements CommandQueue.Callbacks, public void setDependencies(CommandQueue commandQueue, OverviewProxyService overviewProxyService, - NavigationBarA11yHelper navigationBarA11yHelper, + NavBarHelper navBarHelper, NavigationModeController navigationModeController, SysUiState sysUiState, DumpManager dumpManager, AutoHideController autoHideController) { // TODO: adding this in the ctor results in a dagger dependency cycle :( mCommandQueue = commandQueue; mOverviewProxyService = overviewProxyService; - mNavigationBarA11yHelper = navigationBarA11yHelper; + mNavBarHelper = navBarHelper; mNavigationModeController = navigationModeController; mSysUiState = sysUiState; dumpManager.registerDumpable(this); @@ -140,12 +153,16 @@ public class TaskbarDelegate implements CommandQueue.Callbacks, } public void init(int displayId) { + if (mInitialized) { + return; + } mDisplayId = displayId; mCommandQueue.addCallback(this); mOverviewProxyService.addCallback(this); mEdgeBackGestureHandler.onNavigationModeChanged( mNavigationModeController.addListener(this)); - mNavigationBarA11yHelper.registerA11yEventListener(mNavA11yEventListener); + mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater); + mNavBarHelper.init(mContext); mEdgeBackGestureHandler.onNavBarAttached(); // Initialize component callback Display display = mDisplayManager.getDisplay(displayId); @@ -154,23 +171,29 @@ public class TaskbarDelegate implements CommandQueue.Callbacks, // Set initial state for any listeners updateSysuiFlags(); mAutoHideController.setNavigationBar(mAutoHideUiElement); + mInitialized = true; } public void destroy() { + if (!mInitialized) { + return; + } mCommandQueue.removeCallback(this); mOverviewProxyService.removeCallback(this); mNavigationModeController.removeListener(this); - mNavigationBarA11yHelper.removeA11yEventListener(mNavA11yEventListener); + mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater); + mNavBarHelper.destroy(); mEdgeBackGestureHandler.onNavBarDetached(); if (mWindowContext != null) { mWindowContext.unregisterComponentCallbacks(this); mWindowContext = null; } mAutoHideController.setNavigationBar(null); + mInitialized = false; } private void updateSysuiFlags() { - int a11yFlags = mNavigationBarA11yHelper.getA11yButtonState(); + int a11yFlags = mNavBarHelper.getA11yButtonState(); boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0; boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0; @@ -194,6 +217,18 @@ public class TaskbarDelegate implements CommandQueue.Callbacks, .commitUpdate(mDisplayId); } + private void updateAssistantAvailability(boolean assistantAvailable) { + if (mOverviewProxyService.getProxy() == null) { + return; + } + + try { + mOverviewProxyService.getProxy().onAssistantAvailable(assistantAvailable); + } catch (RemoteException e) { + Log.e(TAG, "onAssistantAvailable() failed, available: " + assistantAvailable, e); + } + } + @Override public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, boolean showImeSwitcher) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java new file mode 100644 index 000000000000..734faec4ec74 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java @@ -0,0 +1,178 @@ +/* + * 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.navigationbar; + +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.ComponentName; +import android.view.accessibility.AccessibilityManager; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.accessibility.AccessibilityButtonModeObserver; +import com.android.systemui.assist.AssistManager; +import com.android.systemui.dump.DumpManager; +import com.android.systemui.recents.OverviewProxyService; +import com.android.systemui.settings.UserTracker; +import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import dagger.Lazy; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class NavBarHelperTest extends SysuiTestCase { + + @Mock + AccessibilityManager mAccessibilityManager; + @Mock + AccessibilityManagerWrapper mAccessibilityManagerWrapper; + @Mock + AccessibilityButtonModeObserver mAccessibilityButtonModeObserver; + @Mock + OverviewProxyService mOverviewProxyService; + @Mock + Lazy<AssistManager> mAssistManagerLazy; + @Mock + AssistManager mAssistManager; + @Mock + NavigationModeController mNavigationModeController; + @Mock + UserTracker mUserTracker; + @Mock + ComponentName mAssistantComponent; + @Mock + DumpManager mDumpManager; + @Mock + NavBarHelper.NavbarTaskbarStateUpdater mNavbarTaskbarStateUpdater; + + private NavBarHelper mNavBarHelper; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + when(mAssistManagerLazy.get()).thenReturn(mAssistManager); + when(mAssistManager.getAssistInfoForUser(anyInt())).thenReturn(mAssistantComponent); + when(mUserTracker.getUserId()).thenReturn(1); + + mNavBarHelper = new NavBarHelper(mAccessibilityManager, + mAccessibilityManagerWrapper, mAccessibilityButtonModeObserver, + mOverviewProxyService, mAssistManagerLazy, mNavigationModeController, + mUserTracker, mDumpManager); + + } + + @Test + public void registerListenersInCtor() { + verify(mAccessibilityButtonModeObserver, times(1)).addListener(mNavBarHelper); + verify(mNavigationModeController, times(1)).addListener(mNavBarHelper); + verify(mOverviewProxyService, times(1)).addCallback(mNavBarHelper); + } + + @Test + public void registerAssistantContentObserver() { + mNavBarHelper.init(mContext); + verify(mAssistManager, times(1)).getAssistInfoForUser(anyInt()); + } + + @Test + public void callbacksFiredWhenRegistering() { + mNavBarHelper.init(mContext); + mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater); + verify(mNavbarTaskbarStateUpdater, times(1)) + .updateAccessibilityServicesState(); + verify(mNavbarTaskbarStateUpdater, times(1)) + .updateAssistantAvailable(anyBoolean()); + } + + @Test + public void assistantCallbacksFiredAfterConnecting() { + mNavBarHelper.init(mContext); + // 1st set of callbacks get called when registering + mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater); + + mNavBarHelper.onConnectionChanged(false); + // assert no more callbacks fired + verify(mNavbarTaskbarStateUpdater, times(1)) + .updateAccessibilityServicesState(); + verify(mNavbarTaskbarStateUpdater, times(1)) + .updateAssistantAvailable(anyBoolean()); + + mNavBarHelper.onConnectionChanged(true); + // assert no more callbacks fired + verify(mNavbarTaskbarStateUpdater, times(1)) + .updateAccessibilityServicesState(); + verify(mNavbarTaskbarStateUpdater, times(2)) + .updateAssistantAvailable(anyBoolean()); + } + + @Test + public void a11yCallbacksFiredAfterModeChange() { + mNavBarHelper.init(mContext); + // 1st set of callbacks get called when registering + mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater); + + mNavBarHelper.onAccessibilityButtonModeChanged(0); + verify(mNavbarTaskbarStateUpdater, times(2)) + .updateAccessibilityServicesState(); + verify(mNavbarTaskbarStateUpdater, times(1)) + .updateAssistantAvailable(anyBoolean()); + } + + @Test + public void assistantCallbacksFiredAfterNavModeChange() { + mNavBarHelper.init(mContext); + // 1st set of callbacks get called when registering + mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater); + + mNavBarHelper.onNavigationModeChanged(0); + verify(mNavbarTaskbarStateUpdater, times(1)) + .updateAccessibilityServicesState(); + verify(mNavbarTaskbarStateUpdater, times(2)) + .updateAssistantAvailable(anyBoolean()); + } + + @Test + public void removeListenerNoCallbacksFired() { + mNavBarHelper.init(mContext); + // 1st set of callbacks get called when registering + mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater); + + // Remove listener + mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater); + + // Would have fired 2nd callback if not removed + mNavBarHelper.onAccessibilityButtonModeChanged(0); + + // assert no more callbacks fired + verify(mNavbarTaskbarStateUpdater, times(1)) + .updateAccessibilityServicesState(); + verify(mNavbarTaskbarStateUpdater, times(1)) + .updateAssistantAvailable(anyBoolean()); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java index 4fc329ffc7af..31d88303485c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java @@ -82,7 +82,7 @@ public class NavigationBarControllerTest extends SysuiTestCase { mCommandQueue, Dependency.get(Dependency.MAIN_HANDLER), mock(ConfigurationController.class), - mock(NavigationBarA11yHelper.class), + mock(NavBarHelper.class), mock(TaskbarDelegate.class), mNavigationBarFactory, mock(DumpManager.class), diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java index 50b717181dc2..e038b6e6dfb3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java @@ -136,7 +136,7 @@ public class NavigationBarTest extends SysuiTestCase { @Mock EdgeBackGestureHandler mEdgeBackGestureHandler; @Mock - NavigationBarA11yHelper mNavigationBarA11yHelper; + NavBarHelper mNavBarHelper; @Mock private LightBarController mLightBarController; @Mock @@ -227,6 +227,7 @@ public class NavigationBarTest extends SysuiTestCase { new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_SYSTEMUI) .setLong(HOME_BUTTON_LONG_PRESS_DURATION_MS, 100) .build()); + when(mNavBarHelper.getLongPressHomeEnabled()).thenReturn(true); mNavigationBar.onViewAttachedToWindow(mNavigationBar.createView(null)); mNavigationBar.onHomeTouch(mNavigationBar.getView(), MotionEvent.obtain( @@ -330,14 +331,14 @@ public class NavigationBarTest extends SysuiTestCase { public void testA11yEventAfterDetach() { View v = mNavigationBar.createView(null); mNavigationBar.onViewAttachedToWindow(v); - verify(mNavigationBarA11yHelper).registerA11yEventListener(any( - NavigationBarA11yHelper.NavA11yEventListener.class)); + verify(mNavBarHelper).registerNavTaskStateUpdater(any( + NavBarHelper.NavbarTaskbarStateUpdater.class)); mNavigationBar.onViewDetachedFromWindow(v); - verify(mNavigationBarA11yHelper).removeA11yEventListener(any( - NavigationBarA11yHelper.NavA11yEventListener.class)); + verify(mNavBarHelper).removeNavTaskStateUpdater(any( + NavBarHelper.NavbarTaskbarStateUpdater.class)); // Should be safe even though the internal view is now null. - mNavigationBar.updateAccessibilityServicesState(); + mNavigationBar.updateAcessibilityStateFlags(); } private NavigationBar createNavBar(Context context) { @@ -367,7 +368,7 @@ public class NavigationBarTest extends SysuiTestCase { mHandler, mock(NavigationBarOverlayController.class), mUiEventLogger, - mNavigationBarA11yHelper, + mNavBarHelper, mock(UserTracker.class), mLightBarController, mLightBarcontrollerFactory, |