diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java index e1f9fc35a8e84..02cbccc77c3fe 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java @@ -488,6 +488,12 @@ public final class SystemUiDeviceConfigFlags { public static final String APPLY_SHARING_APP_LIMITS_IN_SYSUI = "apply_sharing_app_limits_in_sysui"; + /* + * (long) The duration that the home button must be pressed before triggering Assist + */ + public static final String HOME_BUTTON_LONG_PRESS_DURATION_MS = + "home_button_long_press_duration_ms"; + private SystemUiDeviceConfigFlags() { } } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index 553623702aedb..272c5d9e406aa 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -35,6 +35,7 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL; import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME; +import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NAV_BAR_HANDLE_FORCE_OPAQUE; import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE; @@ -85,6 +86,7 @@ import android.text.TextUtils; import android.util.Log; import android.view.Display; import android.view.Gravity; +import android.view.HapticFeedbackConstants; import android.view.IWindowManager; import android.view.InsetsState.InternalInsetsType; import android.view.KeyEvent; @@ -212,6 +214,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, private int mLayoutDirection; private boolean mForceNavBarHandleOpaque; + private Optional mHomeButtonLongPressDurationMs; private boolean mIsCurrentUserSetup; /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int, int) */ @@ -373,6 +376,14 @@ public class NavigationBar implements View.OnAttachStateChangeListener, private final Runnable mAutoDim = () -> getBarTransitions().setAutoDim(true); + private final Runnable mOnVariableDurationHomeLongClick = () -> { + if (onHomeLongClick(mNavigationBarView.getHomeButton().getCurrentView())) { + mNavigationBarView.getHomeButton().getCurrentView().performHapticFeedback( + HapticFeedbackConstants.LONG_PRESS, + HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); + } + }; + private final ContentObserver mAssistContentObserver = new ContentObserver( new Handler(Looper.getMainLooper())) { @Override @@ -394,6 +405,13 @@ public class NavigationBar implements View.OnAttachStateChangeListener, mForceNavBarHandleOpaque = properties.getBoolean( NAV_BAR_HANDLE_FORCE_OPAQUE, /* defaultValue = */ true); } + + if (properties.getKeyset().contains(HOME_BUTTON_LONG_PRESS_DURATION_MS)) { + mHomeButtonLongPressDurationMs = Optional.of( + properties.getLong(HOME_BUTTON_LONG_PRESS_DURATION_MS, 0) + ).filter(duration -> duration != 0); + reconfigureHomeLongClick(); + } } }; @@ -510,6 +528,11 @@ public class NavigationBar implements View.OnAttachStateChangeListener, DeviceConfig.NAMESPACE_SYSTEMUI, NAV_BAR_HANDLE_FORCE_OPAQUE, /* defaultValue = */ true); + mHomeButtonLongPressDurationMs = Optional.of(DeviceConfig.getLong( + DeviceConfig.NAMESPACE_SYSTEMUI, + HOME_BUTTON_LONG_PRESS_DURATION_MS, + /* defaultValue = */ 0 + )).filter(duration -> duration != 0); DeviceConfig.addOnPropertiesChangedListener( DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post, mOnPropertiesChangedListener); @@ -769,6 +792,22 @@ public class NavigationBar implements View.OnAttachStateChangeListener, } } + private void reconfigureHomeLongClick() { + if (mNavigationBarView == null + || mNavigationBarView.getHomeButton().getCurrentView() == null) { + return; + } + if (mHomeButtonLongPressDurationMs.isPresent()) { + mNavigationBarView.getHomeButton().getCurrentView().setLongClickable(false); + mNavigationBarView.getHomeButton().getCurrentView().setHapticFeedbackEnabled(false); + mNavigationBarView.getHomeButton().setOnLongClickListener(null); + } else { + mNavigationBarView.getHomeButton().getCurrentView().setLongClickable(true); + mNavigationBarView.getHomeButton().getCurrentView().setHapticFeedbackEnabled(true); + mNavigationBarView.getHomeButton().setOnLongClickListener(this::onHomeLongClick); + } + } + private int deltaRotation(int oldRotation, int newRotation) { int delta = newRotation - oldRotation; if (delta < 0) delta += 4; @@ -779,6 +818,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, pw.println("NavigationBar (displayId=" + mDisplayId + "):"); pw.println(" mStartingQuickSwitchRotation=" + mStartingQuickSwitchRotation); pw.println(" mCurrentRotation=" + mCurrentRotation); + pw.println(" mHomeButtonLongPressDurationMs=" + mHomeButtonLongPressDurationMs); if (mNavigationBarView != null) { pw.println(" mNavigationBarWindowState=" @@ -1108,7 +1148,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener, ButtonDispatcher homeButton = mNavigationBarView.getHomeButton(); homeButton.setOnTouchListener(this::onHomeTouch); - homeButton.setOnLongClickListener(this::onHomeLongClick); + + reconfigureHomeLongClick(); ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton(); accessibilityButton.setOnClickListener(this::onAccessibilityClick); @@ -1118,7 +1159,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener, updateScreenPinningGestures(); } - private boolean onHomeTouch(View v, MotionEvent event) { + @VisibleForTesting + boolean onHomeTouch(View v, MotionEvent event) { if (mHomeBlockedThisTouch && event.getActionMasked() != MotionEvent.ACTION_DOWN) { return true; } @@ -1138,9 +1180,13 @@ public class NavigationBar implements View.OnAttachStateChangeListener, return true; } } + mHomeButtonLongPressDurationMs.ifPresent(longPressDuration -> { + mHandler.postDelayed(mOnVariableDurationHomeLongClick, longPressDuration); + }); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: + mHandler.removeCallbacks(mOnVariableDurationHomeLongClick); mStatusBarLazy.get().awakenDreams(); break; } 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 2b76f1c2d76d8..22c553b764b24 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java @@ -23,6 +23,7 @@ import static android.inputmethodservice.InputMethodService.IME_VISIBLE; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; +import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS; import static com.android.systemui.navigationbar.NavigationBar.NavBarActionEvent.NAVBAR_ASSIST_LONGPRESS; import static org.junit.Assert.assertEquals; @@ -31,6 +32,7 @@ import static org.junit.Assert.assertNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; @@ -44,12 +46,16 @@ import android.content.IntentFilter; import android.hardware.display.DisplayManagerGlobal; import android.os.Handler; import android.os.Looper; +import android.os.SystemClock; import android.os.UserHandle; +import android.provider.DeviceConfig; +import android.provider.Settings; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import android.view.Display; import android.view.DisplayInfo; +import android.view.MotionEvent; import android.view.WindowManager; import android.view.WindowMetrics; import android.view.accessibility.AccessibilityManager; @@ -79,6 +85,7 @@ import com.android.systemui.utils.leaks.LeakCheckedTest; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.pip.Pip; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -101,6 +108,7 @@ public class NavigationBarTest extends SysuiTestCase { private OverviewProxyService mOverviewProxyService; private CommandQueue mCommandQueue; private SysUiState mMockSysUiState; + @Mock private Handler mHandler; @Mock private BroadcastDispatcher mBroadcastDispatcher; @@ -124,12 +132,17 @@ public class NavigationBarTest extends SysuiTestCase { mDependency.injectMockDependency(NavigationBarController.class); mOverviewProxyService = mDependency.injectMockDependency(OverviewProxyService.class); TestableLooper.get(this).runWithLooper(() -> { - mHandler = new Handler(); mNavigationBar = createNavBar(mContext); mExternalDisplayNavigationBar = createNavBar(mSysuiTestableContextExternal); }); } + @After + public void tearDown() throws Exception { + DeviceConfig.resetToDefaults( + Settings.RESET_MODE_PACKAGE_DEFAULTS, DeviceConfig.NAMESPACE_SYSTEMUI); + } + private void setupSysuiDependency() { Display display = new Display(DisplayManagerGlobal.getInstance(), EXTERNAL_DISPLAY_ID, new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS); @@ -162,6 +175,32 @@ public class NavigationBarTest extends SysuiTestCase { verify(mUiEventLogger, times(1)).log(NAVBAR_ASSIST_LONGPRESS); } + @Test + public void testHomeLongPressWithCustomDuration() throws Exception { + DeviceConfig.setProperties( + new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_SYSTEMUI) + .setLong(HOME_BUTTON_LONG_PRESS_DURATION_MS, 100) + .build()); + mNavigationBar.onViewAttachedToWindow(mNavigationBar.createView(null)); + + mNavigationBar.onHomeTouch(mNavigationBar.getView(), MotionEvent.obtain( + /*downTime=*/SystemClock.uptimeMillis(), + /*eventTime=*/SystemClock.uptimeMillis(), + /*action=*/MotionEvent.ACTION_DOWN, + 0, 0, 0 + )); + verify(mHandler, times(1)).postDelayed(any(), eq(100L)); + + mNavigationBar.onHomeTouch(mNavigationBar.getView(), MotionEvent.obtain( + /*downTime=*/SystemClock.uptimeMillis(), + /*eventTime=*/SystemClock.uptimeMillis(), + /*action=*/MotionEvent.ACTION_UP, + 0, 0, 0 + )); + + verify(mHandler, times(1)).removeCallbacks(any()); + } + @Test public void testRegisteredWithDispatcher() { mNavigationBar.onViewAttachedToWindow(mNavigationBar.createView(null));