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 2e2d53be4fa1f..411e6851a0130 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 + } +}