From 733c39faaffb656e7a37edc95be2874c4ee18f5b Mon Sep 17 00:00:00 2001 From: Nick Chameyev Date: Thu, 24 Jun 2021 18:27:22 +0100 Subject: [PATCH] Update rotation suggestion button position to align it with taskbar This CL moves the rotation suggestion button to the middle of the taskbar when it is visible and not stashed. When the user stashes or unstashes the taskbar the button's position is animated accordingly. Test: manual Test: atest com.android.systemui.navigationbar.gestural.FloatingRotationButtonPositionCalculatorTest Fixes: 187410455 Change-Id: Id918e10a44631362f7c3a8a8bb624ee80bdd3c8b --- .../SystemUI/res/layout/rotate_suggestion.xml | 25 +- packages/SystemUI/res/values/dimens.xml | 4 +- .../shared/recents/ISystemUiProxy.aidl | 5 +- .../systemui/navigationbar/NavigationBar.java | 7 + .../navigationbar/NavigationBarView.java | 26 +- .../navigationbar/RotationButton.java | 12 +- .../RotationButtonController.java | 5 +- .../buttons/RotationContextButton.java | 15 +- .../gestural/FloatingRotationButton.java | 155 +++++++--- ...loatingRotationButtonPositionCalculator.kt | 65 ++++ .../recents/OverviewProxyService.java | 290 +++++++----------- .../NavigationBarRotationContextTest.java | 11 +- ...ingRotationButtonPositionCalculatorTest.kt | 127 ++++++++ 13 files changed, 475 insertions(+), 272 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculator.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt diff --git a/packages/SystemUI/res/layout/rotate_suggestion.xml b/packages/SystemUI/res/layout/rotate_suggestion.xml index 194d2e063e970..1c3eedba4f6fb 100644 --- a/packages/SystemUI/res/layout/rotate_suggestion.xml +++ b/packages/SystemUI/res/layout/rotate_suggestion.xml @@ -14,16 +14,19 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License --> - - \ No newline at end of file + > + + + \ No newline at end of file diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index ff0918caee70d..309cc22c6f13f 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -62,7 +62,9 @@ 0.05 40dp - 4dp + 20dp + 20dp + 10dp @*android:dimen/status_bar_icon_size diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl index f72245b9b252e..11557ad87929b 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl @@ -140,5 +140,8 @@ interface ISystemUiProxy { /** Notifies that a swipe-up gesture has started */ oneway void notifySwipeUpGestureStarted() = 46; - // Next id = 47 + /** Notifies when taskbar status updated */ + oneway void notifyTaskbarStatus(boolean visible, boolean stashed) = 47; + + // Next id = 48 } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index 90afec6a24c1b..3073f8353d38e 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -383,6 +383,13 @@ public class NavigationBar implements View.OnAttachStateChangeListener, mNavigationBarView.getRotationButtonController().setSkipOverrideUserLockPrefsOnce(); } + @Override + public void onTaskbarStatusUpdated(boolean visible, boolean stashed) { + mNavigationBarView + .getFloatingRotationButton() + .onTaskbarStateChanged(visible, stashed); + } + @Override public void onToggleRecentApps() { // The same case as onOverviewShown but only for 3-button navigation. diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java index 4816f1cf8d6a6..23c066a75732a 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java @@ -69,6 +69,7 @@ import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.animation.Interpolators; import com.android.systemui.model.SysUiState; +import com.android.systemui.navigationbar.RotationButton.RotationButtonUpdatesCallback; import com.android.systemui.navigationbar.buttons.ButtonDispatcher; import com.android.systemui.navigationbar.buttons.ContextualButton; import com.android.systemui.navigationbar.buttons.ContextualButtonGroup; @@ -275,14 +276,23 @@ public class NavigationBarView extends FrameLayout implements false /* inScreen */, false /* useNearestRegion */)); }; - private final Consumer mRotationButtonListener = (visible) -> { - if (visible) { - // If the button will actually become visible and the navbar is about to hide, - // tell the statusbar to keep it around for longer - mAutoHideController.touchAutoHide(); - } - notifyActiveTouchRegions(); - }; + private final RotationButtonUpdatesCallback mRotationButtonListener = + new RotationButtonUpdatesCallback() { + @Override + public void onVisibilityChanged(boolean visible) { + if (visible) { + // If the button will actually become visible and the navbar is about + // to hide, tell the statusbar to keep it around for longer + mAutoHideController.touchAutoHide(); + } + notifyActiveTouchRegions(); + } + + @Override + public void onPositionChanged() { + notifyActiveTouchRegions(); + } + }; private final Consumer mNavbarOverlayVisibilityChangeCallback = (visible) -> { if (visible) { diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButton.java b/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButton.java index e487858443470..3486c6e759315 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButton.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButton.java @@ -20,12 +20,10 @@ import android.view.View; import com.android.systemui.navigationbar.buttons.KeyButtonDrawable; -import java.util.function.Consumer; - /** Interface of a rotation button that interacts {@link RotationButtonController}. */ public interface RotationButton { void setRotationButtonController(RotationButtonController rotationButtonController); - void setVisibilityChangedCallback(Consumer visibilityChangedCallback); + void setUpdatesCallback(RotationButtonUpdatesCallback updatesCallback); View getCurrentView(); boolean show(); boolean hide(); @@ -39,4 +37,12 @@ public interface RotationButton { default boolean acceptRotationProposal() { return getCurrentView() != null; } + + /** + * Callback for updates provided by a rotation button + */ + interface RotationButtonUpdatesCallback { + void onVisibilityChanged(boolean isVisible); + void onPositionChanged(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java index a5b7911d3b3da..196625b3630e6 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java @@ -46,6 +46,7 @@ import com.android.internal.logging.UiEventLoggerImpl; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.animation.Interpolators; +import com.android.systemui.navigationbar.RotationButton.RotationButtonUpdatesCallback; import com.android.systemui.navigationbar.buttons.KeyButtonDrawable; import com.android.systemui.shared.recents.utilities.Utilities; import com.android.systemui.shared.recents.utilities.ViewRippler; @@ -140,12 +141,12 @@ public class RotationButtonController { } void setRotationButton(RotationButton rotationButton, - Consumer visibilityChangedCallback) { + RotationButtonUpdatesCallback updatesCallback) { mRotationButton = rotationButton; mRotationButton.setRotationButtonController(this); mRotationButton.setOnClickListener(this::onRotateSuggestionClick); mRotationButton.setOnHoverListener(this::onRotateSuggestionHover); - mRotationButton.setVisibilityChangedCallback(visibilityChangedCallback); + mRotationButton.setUpdatesCallback(updatesCallback); } void registerListeners() { diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/RotationContextButton.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/RotationContextButton.java index 6a97a3379939d..ebb67af43a374 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/RotationContextButton.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/RotationContextButton.java @@ -23,10 +23,6 @@ import android.view.View; import com.android.systemui.navigationbar.RotationButton; import com.android.systemui.navigationbar.RotationButtonController; -import com.android.systemui.navigationbar.buttons.ContextualButton; -import com.android.systemui.navigationbar.buttons.KeyButtonDrawable; - -import java.util.function.Consumer; /** Containing logic for the rotation button in nav bar. */ public class RotationContextButton extends ContextualButton implements RotationButton { @@ -48,13 +44,10 @@ public class RotationContextButton extends ContextualButton implements RotationB } @Override - public void setVisibilityChangedCallback(Consumer visibilityChangedCallback) { - setListener(new ContextButtonListener() { - @Override - public void onVisibilityChanged(ContextualButton button, boolean visible) { - if (visibilityChangedCallback != null) { - visibilityChangedCallback.accept(visible); - } + public void setUpdatesCallback(RotationButtonUpdatesCallback updatesCallback) { + setListener((button, visible) -> { + if (updatesCallback != null) { + updatesCallback.onVisibilityChanged(visible); } }); } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/FloatingRotationButton.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/FloatingRotationButton.java index 61118c5d26ac4..46057952e0792 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/FloatingRotationButton.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/FloatingRotationButton.java @@ -20,48 +20,72 @@ import android.content.Context; import android.content.res.Resources; import android.graphics.Color; import android.graphics.PixelFormat; -import android.view.Gravity; import android.view.LayoutInflater; -import android.view.Surface; import android.view.View; +import android.view.ViewGroup; import android.view.WindowManager; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.widget.FrameLayout; import com.android.systemui.R; import com.android.systemui.navigationbar.RotationButton; import com.android.systemui.navigationbar.RotationButtonController; import com.android.systemui.navigationbar.buttons.KeyButtonDrawable; import com.android.systemui.navigationbar.buttons.KeyButtonView; +import com.android.systemui.navigationbar.gestural.FloatingRotationButtonPositionCalculator.Position; -import java.util.function.Consumer; - -/** Containing logic for the rotation button on the physical left bottom corner of the screen. */ +/** + * Containing logic for the rotation button on the physical left bottom corner of the screen. + */ public class FloatingRotationButton implements RotationButton { private static final float BACKGROUND_ALPHA = 0.92f; + private static final int MARGIN_ANIMATION_DURATION_MILLIS = 300; - private final Context mContext; private final WindowManager mWindowManager; + private final ViewGroup mKeyButtonContainer; private final KeyButtonView mKeyButtonView; - private final int mDiameter; - private final int mMargin; + + private final int mContainerSize; + private KeyButtonDrawable mKeyButtonDrawable; private boolean mIsShowing; private boolean mCanShow = true; + private int mDisplayRotation; + + private boolean mIsTaskbarVisible = false; + private boolean mIsTaskbarStashed = false; + + private final FloatingRotationButtonPositionCalculator mPositionCalculator; private RotationButtonController mRotationButtonController; - private Consumer mVisibilityChangedCallback; + private RotationButtonUpdatesCallback mUpdatesCallback; + private Position mPosition; public FloatingRotationButton(Context context) { - mContext = context; - mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); - mKeyButtonView = (KeyButtonView) LayoutInflater.from(mContext).inflate( + mWindowManager = context.getSystemService(WindowManager.class); + mKeyButtonContainer = (ViewGroup) LayoutInflater.from(context).inflate( R.layout.rotate_suggestion, null); + mKeyButtonView = mKeyButtonContainer.findViewById(R.id.rotate_suggestion); mKeyButtonView.setVisibility(View.VISIBLE); - Resources res = mContext.getResources(); - mDiameter = res.getDimensionPixelSize(R.dimen.floating_rotation_button_diameter); - mMargin = Math.max(res.getDimensionPixelSize(R.dimen.floating_rotation_button_min_margin), + Resources res = context.getResources(); + + int defaultMargin = Math.max( + res.getDimensionPixelSize(R.dimen.floating_rotation_button_min_margin), res.getDimensionPixelSize(R.dimen.rounded_corner_content_padding)); + + int taskbarMarginLeft = + res.getDimensionPixelSize(R.dimen.floating_rotation_button_taskbar_left_margin); + int taskbarMarginBottom = + res.getDimensionPixelSize(R.dimen.floating_rotation_button_taskbar_bottom_margin); + + mPositionCalculator = new FloatingRotationButtonPositionCalculator(defaultMargin, + taskbarMarginLeft, taskbarMarginBottom); + + final int diameter = res.getDimensionPixelSize(R.dimen.floating_rotation_button_diameter); + mContainerSize = diameter + Math.max(defaultMargin, Math.max(taskbarMarginLeft, + taskbarMarginBottom)); } @Override @@ -72,8 +96,8 @@ public class FloatingRotationButton implements RotationButton { } @Override - public void setVisibilityChangedCallback(Consumer visibilityChangedCallback) { - mVisibilityChangedCallback = visibilityChangedCallback; + public void setUpdatesCallback(RotationButtonUpdatesCallback updatesCallback) { + mUpdatesCallback = updatesCallback; } @Override @@ -86,45 +110,39 @@ public class FloatingRotationButton implements RotationButton { if (!mCanShow || mIsShowing) { return false; } + mIsShowing = true; int flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; - final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(mDiameter, mDiameter, - mMargin, mMargin, WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, flags, + final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( + mContainerSize, + mContainerSize, + 0, 0, WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, flags, PixelFormat.TRANSLUCENT); + lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; lp.setTitle("FloatingRotationButton"); lp.setFitInsetsTypes(0 /*types */); - switch (mWindowManager.getDefaultDisplay().getRotation()) { - case Surface.ROTATION_0: - lp.gravity = Gravity.BOTTOM | Gravity.LEFT; - break; - case Surface.ROTATION_90: - lp.gravity = Gravity.BOTTOM | Gravity.RIGHT; - break; - case Surface.ROTATION_180: - lp.gravity = Gravity.TOP | Gravity.RIGHT; - break; - case Surface.ROTATION_270: - lp.gravity = Gravity.TOP | Gravity.LEFT; - break; - default: - break; - } - mWindowManager.addView(mKeyButtonView, lp); + + mDisplayRotation = mWindowManager.getDefaultDisplay().getRotation(); + mPosition = mPositionCalculator + .calculatePosition(mDisplayRotation, mIsTaskbarVisible, mIsTaskbarStashed); + + lp.gravity = mPosition.getGravity(); + ((FrameLayout.LayoutParams) mKeyButtonView.getLayoutParams()).gravity = + mPosition.getGravity(); + + updateTranslation(mPosition, /* animate */ false); + + mWindowManager.addView(mKeyButtonContainer, lp); if (mKeyButtonDrawable != null && mKeyButtonDrawable.canAnimate()) { mKeyButtonDrawable.resetAnimation(); mKeyButtonDrawable.startAnimation(); } - mKeyButtonView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { - @Override - public void onLayoutChange(View view, int i, int i1, int i2, int i3, int i4, int i5, - int i6, int i7) { - if (mIsShowing && mVisibilityChangedCallback != null) { - mVisibilityChangedCallback.accept(true); - } - mKeyButtonView.removeOnLayoutChangeListener(this); - } - }); + + if (mUpdatesCallback != null) { + mUpdatesCallback.onVisibilityChanged(true); + } + return true; } @@ -133,10 +151,10 @@ public class FloatingRotationButton implements RotationButton { if (!mIsShowing) { return false; } - mWindowManager.removeViewImmediate(mKeyButtonView); + mWindowManager.removeViewImmediate(mKeyButtonContainer); mIsShowing = false; - if (mVisibilityChangedCallback != null) { - mVisibilityChangedCallback.accept(false); + if (mUpdatesCallback != null) { + mUpdatesCallback.onVisibilityChanged(false); } return true; } @@ -183,4 +201,43 @@ public class FloatingRotationButton implements RotationButton { hide(); } } + + public void onTaskbarStateChanged(boolean taskbarVisible, boolean taskbarStashed) { + mIsTaskbarVisible = taskbarVisible; + mIsTaskbarStashed = taskbarStashed; + + if (!mIsShowing) return; + + final Position newPosition = mPositionCalculator + .calculatePosition(mDisplayRotation, mIsTaskbarVisible, mIsTaskbarStashed); + + if (newPosition.getTranslationX() != mPosition.getTranslationX() + || newPosition.getTranslationY() != mPosition.getTranslationY()) { + updateTranslation(newPosition, /* animate */ true); + mPosition = newPosition; + } + } + + private void updateTranslation(Position position, boolean animate) { + final int translationX = position.getTranslationX(); + final int translationY = position.getTranslationY(); + + if (animate) { + mKeyButtonView + .animate() + .translationX(translationX) + .translationY(translationY) + .setDuration(MARGIN_ANIMATION_DURATION_MILLIS) + .setInterpolator(new AccelerateDecelerateInterpolator()) + .withEndAction(() -> { + if (mUpdatesCallback != null && mIsShowing) { + mUpdatesCallback.onPositionChanged(); + } + }) + .start(); + } else { + mKeyButtonView.setTranslationX(translationX); + mKeyButtonView.setTranslationY(translationY); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculator.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculator.kt new file mode 100644 index 0000000000000..3ce51ad331c5c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculator.kt @@ -0,0 +1,65 @@ +package com.android.systemui.navigationbar.gestural + +import android.view.Gravity +import android.view.Surface + +/** + * Calculates gravity and translation that is necessary to display + * the button in the correct position based on the current state + */ +internal class FloatingRotationButtonPositionCalculator( + private val defaultMargin: Int, + private val taskbarMarginLeft: Int, + private val taskbarMarginBottom: Int +) { + + fun calculatePosition( + currentRotation: Int, + taskbarVisible: Boolean, + taskbarStashed: Boolean + ): Position { + + val isTaskbarSide = currentRotation == Surface.ROTATION_0 + || currentRotation == Surface.ROTATION_90 + val useTaskbarMargin = isTaskbarSide && taskbarVisible && !taskbarStashed + + val gravity = resolveGravity(currentRotation) + + val marginLeft = if (useTaskbarMargin) taskbarMarginLeft else defaultMargin + val marginBottom = if (useTaskbarMargin) taskbarMarginBottom else defaultMargin + + val translationX = + if (gravity and Gravity.RIGHT == Gravity.RIGHT) { + -marginLeft + } else { + marginLeft + } + val translationY = + if (gravity and Gravity.BOTTOM == Gravity.BOTTOM) { + -marginBottom + } else { + marginBottom + } + + return Position( + gravity = gravity, + translationX = translationX, + translationY = translationY + ) + } + + data class Position( + val gravity: Int, + val translationX: Int, + val translationY: Int + ) + + private fun resolveGravity(rotation: Int): Int = + when (rotation) { + Surface.ROTATION_0 -> Gravity.BOTTOM or Gravity.LEFT + Surface.ROTATION_90 -> Gravity.BOTTOM or Gravity.RIGHT + Surface.ROTATION_180 -> Gravity.TOP or Gravity.RIGHT + Surface.ROTATION_270 -> Gravity.TOP or Gravity.LEFT + else -> throw IllegalArgumentException("Invalid rotation $rotation") + } +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 53259435da02f..da9d8882c0911 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -110,6 +110,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.function.BiConsumer; +import java.util.function.Supplier; import javax.inject.Inject; @@ -172,55 +173,34 @@ public class OverviewProxyService extends CurrentUserTracker implements public ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() { @Override public void startScreenPinning(int taskId) { - if (!verifyCaller("startScreenPinning")) { - return; - } - final long token = Binder.clearCallingIdentity(); - try { - mHandler.post(() -> { + verifyCallerAndClearCallingIdentityPostMain("startScreenPinning", () -> mStatusBarOptionalLazy.ifPresent( statusBarLazy -> statusBarLazy.get().showScreenPinningRequest(taskId, - false /* allowCancel */)); - }); - } finally { - Binder.restoreCallingIdentity(token); - } + false /* allowCancel */))); } @Override public void stopScreenPinning() { - if (!verifyCaller("stopScreenPinning")) { - return; - } - final long token = Binder.clearCallingIdentity(); - try { - mHandler.post(() -> { - try { - ActivityTaskManager.getService().stopSystemLockTaskMode(); - } catch (RemoteException e) { - Log.e(TAG_OPS, "Failed to stop screen pinning"); - } - }); - } finally { - Binder.restoreCallingIdentity(token); - } + verifyCallerAndClearCallingIdentityPostMain("stopScreenPinning", () -> { + try { + ActivityTaskManager.getService().stopSystemLockTaskMode(); + } catch (RemoteException e) { + Log.e(TAG_OPS, "Failed to stop screen pinning"); + } + }); } // TODO: change the method signature to use (boolean inputFocusTransferStarted) @Override public void onStatusBarMotionEvent(MotionEvent event) { - if (!verifyCaller("onStatusBarMotionEvent")) { - return; - } - final long token = Binder.clearCallingIdentity(); - try { + verifyCallerAndClearCallingIdentity("onStatusBarMotionEvent", () -> { // TODO move this logic to message queue mStatusBarOptionalLazy.ifPresent(statusBarLazy -> { StatusBar statusBar = statusBarLazy.get(); if (event.getActionMasked() == ACTION_DOWN) { statusBar.getPanelController().startExpandLatencyTracking(); } - mHandler.post(()-> { + mHandler.post(() -> { int action = event.getActionMasked(); if (action == ACTION_DOWN) { mInputFocusTransferStarted = true; @@ -232,50 +212,38 @@ public class OverviewProxyService extends CurrentUserTracker implements } if (action == ACTION_UP || action == ACTION_CANCEL) { mInputFocusTransferStarted = false; + float velocity = (event.getY() - mInputFocusTransferStartY) + / (event.getEventTime() - mInputFocusTransferStartMillis); statusBar.onInputFocusTransfer(mInputFocusTransferStarted, action == ACTION_CANCEL, - (event.getY() - mInputFocusTransferStartY) - / (event.getEventTime() - mInputFocusTransferStartMillis)); + velocity); } event.recycle(); }); }); - } finally { - Binder.restoreCallingIdentity(token); - } + }); } @Override public void onBackPressed() throws RemoteException { - if (!verifyCaller("onBackPressed")) { - return; - } - final long token = Binder.clearCallingIdentity(); - try { - mHandler.post(() -> { - sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK); - sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK); + verifyCallerAndClearCallingIdentityPostMain("onBackPressed", () -> { + sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK); + sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK); - notifyBackAction(true, -1, -1, true, false); - }); - } finally { - Binder.restoreCallingIdentity(token); - } + notifyBackAction(true, -1, -1, true, false); + }); } @Override public void setHomeRotationEnabled(boolean enabled) { - if (!verifyCaller("setHomeRotationEnabled")) { - return; - } - final long token = Binder.clearCallingIdentity(); - try { - mHandler.post(() -> { - mHandler.post(() -> notifyHomeRotationEnabled(enabled)); - }); - } finally { - Binder.restoreCallingIdentity(token); - } + verifyCallerAndClearCallingIdentityPostMain("setHomeRotationEnabled", () -> + mHandler.post(() -> notifyHomeRotationEnabled(enabled))); + } + + @Override + public void notifyTaskbarStatus(boolean visible, boolean stashed) { + verifyCallerAndClearCallingIdentityPostMain("notifyTaskbarStatus", () -> + onTaskbarStatusUpdated(visible, stashed)); } private boolean sendEvent(int action, int code) { @@ -292,124 +260,74 @@ public class OverviewProxyService extends CurrentUserTracker implements @Override public void onOverviewShown(boolean fromHome) { - if (!verifyCaller("onOverviewShown")) { - return; - } - final long token = Binder.clearCallingIdentity(); - try { - mHandler.post(() -> { - for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) { - mConnectionCallbacks.get(i).onOverviewShown(fromHome); - } - }); - } finally { - Binder.restoreCallingIdentity(token); - } + verifyCallerAndClearCallingIdentityPostMain("onOverviewShown", () -> { + for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) { + mConnectionCallbacks.get(i).onOverviewShown(fromHome); + } + }); } @Override public Rect getNonMinimizedSplitScreenSecondaryBounds() { - if (!verifyCaller("getNonMinimizedSplitScreenSecondaryBounds")) { - return null; - } - final long token = Binder.clearCallingIdentity(); - try { - return mLegacySplitScreenOptional.map(splitScreen -> - splitScreen.getDividerView().getNonMinimizedSplitScreenSecondaryBounds()) - .orElse(null); - } finally { - Binder.restoreCallingIdentity(token); - } + return verifyCallerAndClearCallingIdentity( + "getNonMinimizedSplitScreenSecondaryBounds", + () -> mLegacySplitScreenOptional.map(splitScreen -> + splitScreen + .getDividerView() + .getNonMinimizedSplitScreenSecondaryBounds()) + .orElse(null) + ); } @Override public void setNavBarButtonAlpha(float alpha, boolean animate) { - if (!verifyCaller("setNavBarButtonAlpha")) { - return; - } - final long token = Binder.clearCallingIdentity(); - try { - mNavBarButtonAlpha = alpha; - mHandler.post(() -> notifyNavBarButtonAlphaChanged(alpha, animate)); - } finally { - Binder.restoreCallingIdentity(token); - } + verifyCallerAndClearCallingIdentityPostMain("setNavBarButtonAlpha", () -> + notifyNavBarButtonAlphaChanged(alpha, animate)); } @Override public void onAssistantProgress(@FloatRange(from = 0.0, to = 1.0) float progress) { - if (!verifyCaller("onAssistantProgress")) { - return; - } - final long token = Binder.clearCallingIdentity(); - try { - mHandler.post(() -> notifyAssistantProgress(progress)); - } finally { - Binder.restoreCallingIdentity(token); - } + verifyCallerAndClearCallingIdentityPostMain("onAssistantProgress", () -> + notifyAssistantProgress(progress)); } @Override public void onAssistantGestureCompletion(float velocity) { - if (!verifyCaller("onAssistantGestureCompletion")) { - return; - } - final long token = Binder.clearCallingIdentity(); - try { - mHandler.post(() -> notifyAssistantGestureCompletion(velocity)); - } finally { - Binder.restoreCallingIdentity(token); - } + verifyCallerAndClearCallingIdentityPostMain("onAssistantGestureCompletion", () -> + notifyAssistantGestureCompletion(velocity)); } @Override public void startAssistant(Bundle bundle) { - if (!verifyCaller("startAssistant")) { - return; - } - final long token = Binder.clearCallingIdentity(); - try { - mHandler.post(() -> notifyStartAssistant(bundle)); - } finally { - Binder.restoreCallingIdentity(token); - } + verifyCallerAndClearCallingIdentityPostMain("startAssistant", () -> + notifyStartAssistant(bundle)); } @Override public void notifyAccessibilityButtonClicked(int displayId) { - if (!verifyCaller("notifyAccessibilityButtonClicked")) { - return; - } - final long token = Binder.clearCallingIdentity(); - try { - AccessibilityManager.getInstance(mContext) - .notifyAccessibilityButtonClicked(displayId); - } finally { - Binder.restoreCallingIdentity(token); - } + verifyCallerAndClearCallingIdentity("notifyAccessibilityButtonClicked", () -> + AccessibilityManager.getInstance(mContext) + .notifyAccessibilityButtonClicked(displayId)); } @Override public void notifyAccessibilityButtonLongClicked() { - if (!verifyCaller("notifyAccessibilityButtonLongClicked")) { - return; - } - final long token = Binder.clearCallingIdentity(); - try { - final Intent intent = - new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON); - final String chooserClassName = AccessibilityButtonChooserActivity.class.getName(); - intent.setClassName(CHOOSER_PACKAGE_NAME, chooserClassName); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); - mContext.startActivityAsUser(intent, UserHandle.CURRENT); - } finally { - Binder.restoreCallingIdentity(token); - } + verifyCallerAndClearCallingIdentity("notifyAccessibilityButtonLongClicked", + () -> { + final Intent intent = + new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON); + final String chooserClassName = AccessibilityButtonChooserActivity + .class.getName(); + intent.setClassName(CHOOSER_PACKAGE_NAME, chooserClassName); + intent.addFlags( + Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + mContext.startActivityAsUser(intent, UserHandle.CURRENT); + }); } @Override public void handleImageAsScreenshot(Bitmap screenImage, Rect locationInScreen, - Insets visibleInsets, int taskId) { + Insets visibleInsets, int taskId) { // Deprecated } @@ -421,43 +339,22 @@ public class OverviewProxyService extends CurrentUserTracker implements @Override public void notifySwipeToHomeFinished() { - if (!verifyCaller("notifySwipeToHomeFinished")) { - return; - } - final long token = Binder.clearCallingIdentity(); - try { - mPipOptional.ifPresent( - pip -> pip.setPinnedStackAnimationType( - PipAnimationController.ANIM_TYPE_ALPHA)); - } finally { - Binder.restoreCallingIdentity(token); - } + verifyCallerAndClearCallingIdentity("notifySwipeToHomeFinished", () -> + mPipOptional.ifPresent( + pip -> pip.setPinnedStackAnimationType( + PipAnimationController.ANIM_TYPE_ALPHA))); } @Override public void notifySwipeUpGestureStarted() { - if (!verifyCaller("notifySwipeUpGestureStarted")) { - return; - } - final long token = Binder.clearCallingIdentity(); - try { - mHandler.post(() -> notifySwipeUpGestureStartedInternal()); - } finally { - Binder.restoreCallingIdentity(token); - } + verifyCallerAndClearCallingIdentityPostMain("notifySwipeUpGestureStarted", () -> + notifySwipeUpGestureStartedInternal()); } @Override public void notifyPrioritizedRotation(@Surface.Rotation int rotation) { - if (!verifyCaller("notifyPrioritizedRotation")) { - return; - } - final long token = Binder.clearCallingIdentity(); - try { - mHandler.post(() -> notifyPrioritizedRotationInternal(rotation)); - } finally { - Binder.restoreCallingIdentity(token); - } + verifyCallerAndClearCallingIdentityPostMain("notifyPrioritizedRotation", () -> + notifyPrioritizedRotationInternal(rotation)); } @Override @@ -477,15 +374,8 @@ public class OverviewProxyService extends CurrentUserTracker implements @Override public void expandNotificationPanel() { - if (!verifyCaller("expandNotificationPanel")) { - return; - } - final long token = Binder.clearCallingIdentity(); - try { - mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN); - } finally { - Binder.restoreCallingIdentity(token); - } + verifyCallerAndClearCallingIdentity("expandNotificationPanel", + () -> mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN)); } private boolean verifyCaller(String reason) { @@ -497,6 +387,29 @@ public class OverviewProxyService extends CurrentUserTracker implements } return true; } + + private T verifyCallerAndClearCallingIdentity(String reason, Supplier supplier) { + if (!verifyCaller(reason)) { + return null; + } + final long token = Binder.clearCallingIdentity(); + try { + return supplier.get(); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + private void verifyCallerAndClearCallingIdentity(String reason, Runnable runnable) { + verifyCallerAndClearCallingIdentity(reason, () -> { + runnable.run(); + return null; + }); + } + + private void verifyCallerAndClearCallingIdentityPostMain(String reason, Runnable runnable) { + verifyCallerAndClearCallingIdentity(reason, () -> mHandler.post(runnable)); + } }; private final Runnable mDeferredConnectionCallback = () -> { @@ -883,6 +796,12 @@ public class OverviewProxyService extends CurrentUserTracker implements } } + private void onTaskbarStatusUpdated(boolean visible, boolean stashed) { + for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) { + mConnectionCallbacks.get(i).onTaskbarStatusUpdated(visible, stashed); + } + } + private void notifyConnectionChanged() { for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) { mConnectionCallbacks.get(i).onConnectionChanged(mOverviewProxy != null); @@ -1049,6 +968,7 @@ public class OverviewProxyService extends CurrentUserTracker implements /** Notify changes in the nav bar button alpha */ default void onNavBarButtonAlphaChanged(float alpha, boolean animate) {} default void onHomeRotationEnabled(boolean enabled) {} + default void onTaskbarStatusUpdated(boolean visible, boolean stashed) {} default void onSystemUiStateChanged(int sysuiStateFlags) {} default void onAssistantProgress(@FloatRange(from = 0.0, to = 1.0) float progress) {} default void onAssistantGestureCompletion(float velocity) {} diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java index b9919767d63f8..a6ff2e8d2e153 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java @@ -59,7 +59,16 @@ public class NavigationBarRotationContextTest extends SysuiTestCase { final View view = new View(mContext); mRotationButton = mock(RotationButton.class); mRotationButtonController = new RotationButtonController(mContext, 0, 0); - mRotationButtonController.setRotationButton(mRotationButton, (visibility) -> {}); + mRotationButtonController.setRotationButton(mRotationButton, + new RotationButton.RotationButtonUpdatesCallback() { + @Override + public void onVisibilityChanged(boolean isVisible) { + } + + @Override + public void onPositionChanged() { + } + }); // Due to a mockito issue, only spy the object after setting the initial state mRotationButtonController = spy(mRotationButtonController); doReturn(view).when(mRotationButton).getCurrentView(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt new file mode 100644 index 0000000000000..0a20001070530 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt @@ -0,0 +1,127 @@ +package com.android.systemui.navigationbar.gestural + +import android.view.Gravity +import android.view.Surface +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.navigationbar.gestural.FloatingRotationButtonPositionCalculator.Position +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized + +@RunWith(Parameterized::class) +@SmallTest +internal class FloatingRotationButtonPositionCalculatorTest(private val testCase: TestCase) + : SysuiTestCase() { + + private val calculator = FloatingRotationButtonPositionCalculator( + MARGIN_DEFAULT, MARGIN_TASKBAR_LEFT, MARGIN_TASKBAR_BOTTOM + ) + + @Test + fun calculatePosition() { + val position = calculator.calculatePosition( + testCase.rotation, + testCase.taskbarVisible, + testCase.taskbarStashed + ) + + assertThat(position).isEqualTo(testCase.expectedPosition) + } + + internal class TestCase( + val rotation: Int, + val taskbarVisible: Boolean, + val taskbarStashed: Boolean, + val expectedPosition: Position + ) { + override fun toString(): String = + "when rotation = $rotation, " + + "taskbarVisible = $taskbarVisible, " + + "taskbarStashed = $taskbarStashed - " + + "expected $expectedPosition" + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection = + listOf( + TestCase( + rotation = Surface.ROTATION_0, + taskbarVisible = false, + taskbarStashed = false, + expectedPosition = Position( + gravity = Gravity.BOTTOM or Gravity.LEFT, + translationX = MARGIN_DEFAULT, + translationY = -MARGIN_DEFAULT + ) + ), + TestCase( + rotation = Surface.ROTATION_90, + taskbarVisible = false, + taskbarStashed = false, + expectedPosition = Position( + gravity = Gravity.BOTTOM or Gravity.RIGHT, + translationX = -MARGIN_DEFAULT, + translationY = -MARGIN_DEFAULT + ) + ), + TestCase( + rotation = Surface.ROTATION_180, + taskbarVisible = false, + taskbarStashed = false, + expectedPosition = Position( + gravity = Gravity.TOP or Gravity.RIGHT, + translationX = -MARGIN_DEFAULT, + translationY = MARGIN_DEFAULT + ) + ), + TestCase( + rotation = Surface.ROTATION_270, + taskbarVisible = false, + taskbarStashed = false, + expectedPosition = Position( + gravity = Gravity.TOP or Gravity.LEFT, + translationX = MARGIN_DEFAULT, + translationY = MARGIN_DEFAULT + ) + ), + TestCase( + rotation = Surface.ROTATION_0, + taskbarVisible = true, + taskbarStashed = false, + expectedPosition = Position( + gravity = Gravity.BOTTOM or Gravity.LEFT, + translationX = MARGIN_TASKBAR_LEFT, + translationY = -MARGIN_TASKBAR_BOTTOM + ) + ), + TestCase( + rotation = Surface.ROTATION_0, + taskbarVisible = true, + taskbarStashed = true, + expectedPosition = Position( + gravity = Gravity.BOTTOM or Gravity.LEFT, + translationX = MARGIN_DEFAULT, + translationY = -MARGIN_DEFAULT + ) + ), + TestCase( + rotation = Surface.ROTATION_90, + taskbarVisible = true, + taskbarStashed = false, + expectedPosition = Position( + gravity = Gravity.BOTTOM or Gravity.RIGHT, + translationX = -MARGIN_TASKBAR_LEFT, + translationY = -MARGIN_TASKBAR_BOTTOM + ) + ) + ) + + private const val MARGIN_DEFAULT = 10 + private const val MARGIN_TASKBAR_LEFT = 20 + private const val MARGIN_TASKBAR_BOTTOM = 30 + } +}