From 84dc3a45ee4e7d2e2cb7a91b305e0c8bed234817 Mon Sep 17 00:00:00 2001 From: Vinit Nayak Date: Wed, 6 May 2020 13:18:13 -0700 Subject: [PATCH] Notify SysUi on device rotation for back gesture Whenever device rotates, we notify sysui to hide/show the back gesture if the foreground app also rotates. We also notify sysui when device rotates to match the orientation of the current foreground app (this is for apps with fixed rotations). Fixes: 154580671 Test: Created test apps of different rotations and ensured that back functionality was present when attempting to go back. Change-Id: I33a71698411d9bc2416b6660f8dbd53233628917 (cherry picked from commit 80303ac4b9b2f881d146af619aaad28b390a4ca0) Merged-In: I33a71698411d9bc2416b6660f8dbd53233628917 --- .../RecentsAnimationDeviceState.java | 119 ++++++++++++++++-- .../quickstep/util/RecentsOrientedState.java | 6 +- 2 files changed, 115 insertions(+), 10 deletions(-) diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java index 07aed52f6e..79b38f23e1 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java @@ -16,6 +16,7 @@ package com.android.quickstep; import static android.content.Intent.ACTION_USER_UNLOCKED; +import static android.view.Surface.ROTATION_0; import static com.android.launcher3.util.DefaultDisplay.CHANGE_ALL; import static com.android.launcher3.util.DefaultDisplay.CHANGE_FRAME_DELAY; @@ -48,15 +49,18 @@ import android.os.UserManager; import android.provider.Settings; import android.text.TextUtils; import android.view.MotionEvent; +import android.view.OrientationEventListener; import androidx.annotation.BinderThread; import com.android.launcher3.R; import com.android.launcher3.Utilities; +import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.util.DefaultDisplay; import com.android.launcher3.util.SecureSettingsObserver; import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener; import com.android.quickstep.util.NavBarPosition; +import com.android.quickstep.util.RecentsOrientedState; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags; @@ -112,9 +116,53 @@ public class RecentsAnimationDeviceState implements } enableMultipleRegions(false); } + + @Override + public void onActivityRotation(int displayId) { + super.onActivityRotation(displayId); + // This always gets called before onDisplayInfoChanged() so we know how to process + // the rotation in that method. This is done to avoid having a race condition between + // the sensor readings and onDisplayInfoChanged() call + if (displayId != mDisplayId) { + return; + } + + mPrioritizeDeviceRotation = true; + if (mInOverview) { + // reset, launcher must be rotating + mExitOverviewRunnable.run(); + } + } + }; + + private Runnable mExitOverviewRunnable = new Runnable() { + @Override + public void run() { + mInOverview = false; + enableMultipleRegions(false); + } }; private OrientationTouchTransformer mOrientationTouchTransformer; + /** + * Used to listen for when the device rotates into the orientation of the current + * foreground app. For example, if a user quickswitches from a portrait to a fixed landscape + * app and then rotates rotates the device to match that orientation, this triggers calls to + * sysui to adjust the navbar. + */ + private OrientationEventListener mOrientationListener; + private int mPreviousRotation = ROTATION_0; + /** + * This is the configuration of the foreground app or the app that will be in the foreground + * once a quickstep gesture finishes. + */ + private int mCurrentAppRotation = -1; + /** + * This flag is set to true when the device physically changes orientations. When true, + * we will always report the current rotation of the foreground app whenever the display + * changes, as it would indicate the user's intention to rotate the foreground app. + */ + private boolean mPrioritizeDeviceRotation = false; private Region mExclusionRegion; private SystemGestureExclusionListenerCompat mExclusionListener; @@ -193,6 +241,26 @@ public class RecentsAnimationDeviceState implements userSetupObserver.register(); runOnDestroy(userSetupObserver::unregister); } + + mOrientationListener = new OrientationEventListener(context) { + @Override + public void onOrientationChanged(int degrees) { + int newRotation = RecentsOrientedState.getRotationForUserDegreesRotated(degrees, + mPreviousRotation); + if (newRotation == mPreviousRotation) { + return; + } + + mPreviousRotation = newRotation; + mPrioritizeDeviceRotation = true; + + if (newRotation == mCurrentAppRotation) { + // When user rotates device to the orientation of the foreground app after + // quickstepping + toggleSecondaryNavBarsForRotation(false); + } + } + }; } private void setupOrientationSwipeHandler() { @@ -268,6 +336,18 @@ public class RecentsAnimationDeviceState implements mNavBarPosition = new NavBarPosition(mMode, info); updateGestureTouchRegions(); mOrientationTouchTransformer.createOrAddTouchRegion(info); + mCurrentAppRotation = mDisplayRotation; + + /* Update nav bars on the following: + * a) if we're not expecting quickswitch, this is coming from an activity rotation + * b) we launch an app in the orientation that user is already in + * c) We're not in overview, since overview will always be portrait (w/o home rotation) + */ + if ((mPrioritizeDeviceRotation + || mCurrentAppRotation == mPreviousRotation) // switch to an app of orientation user is in + && !mInOverview) { + toggleSecondaryNavBarsForRotation(false); + } } /** @@ -553,9 +633,13 @@ public class RecentsAnimationDeviceState implements mOrientationTouchTransformer.transform(event); } - void enableMultipleRegions(boolean enable) { - mOrientationTouchTransformer.enableMultipleRegions(enable, mDefaultDisplay.getInfo()); - notifySysuiForRotation(mOrientationTouchTransformer.getQuickStepStartingRotation()); + private void enableMultipleRegions(boolean enable) { + toggleSecondaryNavBarsForRotation(enable); + if (enable && !TestProtocol.sDisableSensorRotation) { + mOrientationListener.enable(); + } else { + mOrientationListener.disable(); + } } private void notifySysuiForRotation(int rotation) { @@ -581,10 +665,7 @@ public class RecentsAnimationDeviceState implements // If we're in landscape w/o ever quickswitching, show the navbar in landscape enableMultipleRegions(true); } - activityInterface.onExitOverview(this, () -> { - mInOverview = false; - enableMultipleRegions(false); - }); + activityInterface.onExitOverview(this, mExitOverviewRunnable); } else if (endTarget == GestureState.GestureEndTarget.HOME) { enableMultipleRegions(false); } else if (endTarget == GestureState.GestureEndTarget.NEW_TASK) { @@ -594,6 +675,11 @@ public class RecentsAnimationDeviceState implements } else { notifySysuiForRotation(mOrientationTouchTransformer.getCurrentActiveRotation()); } + + // A new gesture is starting, reset the current device rotation + // This is done under the assumption that the user won't rotate the phone and then + // quickswitch in the old orientation. + mPrioritizeDeviceRotation = false; } else if (endTarget == GestureState.GestureEndTarget.LAST_TASK) { if (!mTaskListFrozen) { // touched nav bar but didn't go anywhere and not quickswitching, do nothing @@ -603,7 +689,24 @@ public class RecentsAnimationDeviceState implements } } - int getCurrentActiveRotation() { + private void notifySysuiOfCurrentRotation(int rotation) { + UI_HELPER_EXECUTOR.execute(() -> SystemUiProxy.INSTANCE.get(mContext) + .onQuickSwitchToNewTask(rotation)); + } + + /** + * Disables/Enables multiple nav bars on {@link OrientationTouchTransformer} and then + * notifies system UI of the primary rotation the user is interacting with + * + * @param enable if {@code true}, this will report to sysUI the navbar of the region the gesture + * started in (during ACTION_DOWN), otherwise will report {@param displayRotation} + */ + private void toggleSecondaryNavBarsForRotation(boolean enable) { + mOrientationTouchTransformer.enableMultipleRegions(enable, mDefaultDisplay.getInfo()); + notifySysuiOfCurrentRotation(mOrientationTouchTransformer.getQuickStepStartingRotation()); + } + + public int getCurrentActiveRotation() { if (!mMode.hasGestures) { // touch rotation should always match that of display for 3 button return mDisplayRotation; diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java index 7715cca60f..fe5a6c90b1 100644 --- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java +++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java @@ -445,7 +445,8 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre } break; case ROTATION_270: - if (degrees < (90 - threshold)) { + if (degrees < (90 - threshold) || + (degrees > (270 + threshold) && degrees < 360)) { return ROTATION_0; } if (degrees > (90 + threshold) && degrees < 180) { @@ -468,7 +469,8 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre if (degrees < (270 - threshold) && degrees > 90) { return ROTATION_180; } - if (degrees > (270 + threshold) && degrees < 360) { + if (degrees > (270 + threshold) && degrees < 360 + || (degrees >= 0 && degrees < threshold)) { return ROTATION_0; } // flip from landscape to seascape