diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java index 7d8e93ce00..fbd1b6e3da 100644 --- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java @@ -49,6 +49,7 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_Q import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SHORTCUT_HELPER_SHOWING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING; +import static com.android.window.flags.Flags.predictiveBackThreeButtonNav; import static com.android.wm.shell.Flags.enableBubbleBarInPersistentTaskBar; import android.animation.Animator; @@ -71,8 +72,10 @@ import android.graphics.drawable.PaintDrawable; import android.graphics.drawable.RotateDrawable; import android.inputmethodservice.InputMethodService; import android.os.Handler; +import android.os.SystemClock; import android.util.Property; import android.view.Gravity; +import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.View.OnAttachStateChangeListener; @@ -843,12 +846,43 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT buttonView.setImageResource(drawableId); buttonView.setContentDescription(parent.getContext().getString( navButtonController.getButtonContentDescription(buttonType))); - buttonView.setOnClickListener(view -> navButtonController.onButtonClick(buttonType, view)); - buttonView.setOnLongClickListener(view -> - navButtonController.onButtonLongClick(buttonType, view)); + if (predictiveBackThreeButtonNav() && buttonType == BUTTON_BACK) { + // set up special touch listener for back button to support predictive back + setBackButtonTouchListener(buttonView, navButtonController); + } else { + buttonView.setOnClickListener(view -> + navButtonController.onButtonClick(buttonType, view)); + buttonView.setOnLongClickListener(view -> + navButtonController.onButtonLongClick(buttonType, view)); + } return buttonView; } + private void setBackButtonTouchListener(View buttonView, + TaskbarNavButtonController navButtonController) { + buttonView.setOnTouchListener((v, event) -> { + if (event.getAction() == MotionEvent.ACTION_MOVE) return false; + long time = SystemClock.uptimeMillis(); + int action = event.getAction(); + KeyEvent keyEvent = new KeyEvent(time, time, + action == MotionEvent.ACTION_DOWN ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP, + KeyEvent.KEYCODE_BACK, 0); + if (event.getAction() == MotionEvent.ACTION_CANCEL) { + keyEvent.cancel(); + } + navButtonController.executeBack(keyEvent); + + if (action == MotionEvent.ACTION_UP) { + buttonView.performClick(); + } + return false; + }); + buttonView.setOnLongClickListener((view) -> { + navButtonController.onButtonLongClick(BUTTON_BACK, view); + return false; + }); + } + private ImageView addButton(ViewGroup parent, @IdRes int id, @LayoutRes int layoutId) { ImageView buttonView = (ImageView) mContext.getLayoutInflater() .inflate(layoutId, parent, false); diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java index 894791479e..0f9ede9f56 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java @@ -16,6 +16,8 @@ package com.android.launcher3.taskbar; +import static android.view.MotionEvent.ACTION_UP; + import static com.android.internal.app.AssistUtils.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS; import static com.android.internal.app.AssistUtils.INVOCATION_TYPE_KEY; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_A11Y_BUTTON_LONGPRESS; @@ -31,12 +33,14 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY; import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING; +import static com.android.window.flags.Flags.predictiveBackThreeButtonNav; import android.content.Context; import android.os.Bundle; import android.os.Handler; import android.util.Log; import android.view.HapticFeedbackConstants; +import android.view.KeyEvent; import android.view.View; import android.view.inputmethod.Flags; @@ -141,10 +145,7 @@ public class TaskbarNavButtonController implements TaskbarControllers.LoggableTa view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); switch (buttonType) { case BUTTON_BACK: - logEvent(LAUNCHER_TASKBAR_BACK_BUTTON_TAP); - mContextualEduStatsManager.updateEduStats(/* isTrackpadGesture= */ false, - GestureType.BACK); - executeBack(); + executeBack(/* keyEvent */ null); break; case BUTTON_HOME: logEvent(LAUNCHER_TASKBAR_HOME_BUTTON_TAP); @@ -182,7 +183,9 @@ public class TaskbarNavButtonController implements TaskbarControllers.LoggableTa // Provide the same haptic feedback that the system offers for long press. // The haptic feedback from long pressing on the home button is handled by circle to search. - if (buttonType != BUTTON_HOME) { + // There are no haptics for long pressing the back button if predictive back is enabled + if (buttonType != BUTTON_HOME + && (!predictiveBackThreeButtonNav() || buttonType != BUTTON_BACK)) { view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); } switch (buttonType) { @@ -320,8 +323,13 @@ public class TaskbarNavButtonController implements TaskbarControllers.LoggableTa mCallbacks.onToggleOverview(); } - private void executeBack() { - mSystemUiProxy.onBackPressed(); + void executeBack(@Nullable KeyEvent keyEvent) { + if (keyEvent == null || (keyEvent.getAction() == ACTION_UP && !keyEvent.isCanceled())) { + logEvent(LAUNCHER_TASKBAR_BACK_BUTTON_TAP); + mContextualEduStatsManager.updateEduStats(/* isTrackpadGesture= */ false, + GestureType.BACK); + } + mSystemUiProxy.onBackEvent(keyEvent); } private void onImeSwitcherPress() { diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java index bf086b4d9d..4a7e4f0915 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java @@ -155,7 +155,7 @@ public class TaskbarScrimViewController implements TaskbarControllers.LoggableTa } private void onClick() { - SystemUiProxy.INSTANCE.get(mActivity).onBackPressed(); + SystemUiProxy.INSTANCE.get(mActivity).onBackEvent(null); } @Override diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java index cd39c09fee..7f20509bf7 100644 --- a/quickstep/src/com/android/quickstep/SystemUiProxy.java +++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java @@ -43,6 +43,7 @@ import android.os.RemoteException; import android.os.UserHandle; import android.util.Log; import android.view.IRemoteAnimationRunner; +import android.view.KeyEvent; import android.view.MotionEvent; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; @@ -210,10 +211,10 @@ public class SystemUiProxy implements ISystemUiProxy, NavHandle { } @Override - public void onBackPressed() { + public void onBackEvent(KeyEvent backEvent) { if (mSystemUiProxy != null) { try { - mSystemUiProxy.onBackPressed(); + mSystemUiProxy.onBackEvent(backEvent); } catch (RemoteException e) { Log.w(TAG, "Failed call onBackPressed", e); } diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java index 253d9215fe..4b04dba4b4 100644 --- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java +++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java @@ -115,7 +115,7 @@ public class TaskbarNavButtonControllerTest { @Test public void testPressBack() { mNavButtonController.onButtonClick(BUTTON_BACK, mockView); - verify(mockSystemUiProxy, times(1)).onBackPressed(); + verify(mockSystemUiProxy, times(1)).onBackEvent(null); } @Test diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarScrimViewControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarScrimViewControllerTest.kt index 3912051143..436dfd31d0 100644 --- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarScrimViewControllerTest.kt +++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarScrimViewControllerTest.kt @@ -17,6 +17,7 @@ package com.android.launcher3.taskbar import android.animation.AnimatorTestRule +import android.view.KeyEvent import android.view.View.GONE import android.view.View.VISIBLE import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation @@ -47,8 +48,8 @@ class TaskbarScrimViewControllerTest { TaskbarWindowSandboxContext.create { builder -> builder.bindSystemUiProxy( object : SystemUiProxy(this) { - override fun onBackPressed() { - super.onBackPressed() + override fun onBackEvent(backEvent: KeyEvent?) { + super.onBackEvent(backEvent) backPressed = true } }