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