diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 10fd8b579b162ad868d0dff0f9a75b7988fcbd24..30392d2c9d859eee2c30d41d52270aed229e7397 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -34,6 +34,9 @@ <dimen name="navigation_handle_bottom">10dp</dimen> <dimen name="navigation_handle_sample_horizontal_margin">10dp</dimen> <dimen name="navigation_home_handle_width">108dp</dimen> + <!-- Used while animating the navbar during a long press. --> + <dimen name="navigation_home_handle_additional_width_for_animation">20dp</dimen> + <dimen name="navigation_home_handle_additional_height_for_animation">4dp</dimen> <!-- Size of the nav bar edge panels, should be greater to the edge sensitivity + the drag threshold --> 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 dc34ef75482522a7c4cab7e156e20a71e46c3c40..26e785d9a7044d696bfa0a9787af78bbc66a9304 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 @@ -144,5 +144,15 @@ interface ISystemUiProxy { */ oneway void onStatusBarTrackpadEvent(in MotionEvent event) = 52; - // Next id = 54 + /** + * Animate the nav bar being long-pressed. + * + * @param isTouchDown {@code true} if the button is starting to be pressed ({@code false} if + * released or canceled) + * @param durationMs how long the animation should take (for the {@code isTouchDown} case, this + * should be the same as the amount of time to trigger a long-press) + */ + oneway void animateNavBarLongPress(boolean isTouchDown, long durationMs) = 54; + + // Next id = 55 } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index ad1c77d055348a3b067fa6e03d0dd6d150545e2c..a985236cb38e0ff6ed219e90b2f210330e3386e4 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -112,7 +112,6 @@ import com.android.internal.statusbar.LetterboxDetails; import com.android.internal.util.LatencyTracker; import com.android.internal.view.AppearanceRegion; import com.android.systemui.Gefingerpoken; -import com.android.systemui.res.R; import com.android.systemui.assist.AssistManager; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.DisplayId; @@ -130,6 +129,7 @@ import com.android.systemui.navigationbar.gestural.QuickswitchOrientedNavHandle; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.recents.Recents; +import com.android.systemui.res.R; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.settings.UserContextProvider; import com.android.systemui.settings.UserTracker; @@ -398,6 +398,11 @@ public class NavigationBar extends ViewController<NavigationBarView> implements mAssistManagerLazy.get().setAssistantOverridesRequested(invocationTypes); } + @Override + public void animateNavBarLongPress(boolean isTouchDown, long durationMs) { + mView.getHomeHandle().animateLongPress(isTouchDown, durationMs); + } + @Override public void onHomeRotationEnabled(boolean enabled) { mView.getRotationButtonController().setHomeRotationEnabled(enabled); diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ButtonDispatcher.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ButtonDispatcher.java index 10084bd4ccada45dd1f36fdccf95323072a861ac..5fe830e6e4427c0ebb63604581799f51ad33c020 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ButtonDispatcher.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ButtonDispatcher.java @@ -247,6 +247,14 @@ public class ButtonDispatcher { } } + public void animateLongPress(boolean isTouchDown, long durationMs) { + for (int i = 0; i < mViews.size(); i++) { + if (mViews.get(i) instanceof ButtonInterface) { + ((ButtonInterface) mViews.get(i)).animateLongPress(isTouchDown, durationMs); + } + } + } + public void setLongClickable(boolean isLongClickable) { mLongClickable = isLongClickable; final int N = mViews.size(); diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ButtonInterface.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ButtonInterface.java index 8d291ddf5f19733510bd707f7ed7b08cd5c98016..356b2f7c7cb873c183944421c71d38567886b3a8 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ButtonInterface.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ButtonInterface.java @@ -30,4 +30,14 @@ public interface ButtonInterface { void setDarkIntensity(float intensity); void setDelayTouchFeedback(boolean shouldDelay); + + /** + * Animate the button being long-pressed. + * + * @param isTouchDown {@code true} if the button is starting to be pressed ({@code false} if + * released or canceled) + * @param durationMs how long the animation should take (for the {@code isTouchDown} case, this + * should be the same as the amount of time to trigger a long-press) + */ + default void animateLongPress(boolean isTouchDown, long durationMs) {} } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationHandle.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationHandle.java index 5a22c385cb5192eb5a50001c596e57397f5e508f..5bfc7dc5f7ae27f73123f5a42c10ee2f4f763177 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationHandle.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationHandle.java @@ -17,6 +17,7 @@ package com.android.systemui.navigationbar.gestural; import android.animation.ArgbEvaluator; +import android.animation.ObjectAnimator; import android.annotation.ColorInt; import android.content.Context; import android.content.res.Resources; @@ -24,12 +25,15 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.drawable.Drawable; import android.util.AttributeSet; +import android.util.FloatProperty; import android.view.ContextThemeWrapper; import android.view.View; +import android.view.animation.Interpolator; +import com.android.app.animation.Interpolators; import com.android.settingslib.Utils; -import com.android.systemui.res.R; import com.android.systemui.navigationbar.buttons.ButtonInterface; +import com.android.systemui.res.R; public class NavigationHandle extends View implements ButtonInterface { @@ -38,8 +42,26 @@ public class NavigationHandle extends View implements ButtonInterface { private @ColorInt final int mDarkColor; protected final float mRadius; protected final float mBottom; + private final float mAdditionalWidthForAnimation; + private final float mAdditionalHeightForAnimation; private boolean mRequiresInvalidate; + private ObjectAnimator mPulseAnimator = null; + private float mPulseAnimationProgress; + + private static final FloatProperty<NavigationHandle> PULSE_ANIMATION_PROGRESS = + new FloatProperty<>("pulseAnimationProgress") { + @Override + public Float get(NavigationHandle controller) { + return controller.getPulseAnimationProgress(); + } + + @Override + public void setValue(NavigationHandle controller, float progress) { + controller.setPulseAnimationProgress(progress); + } + }; + public NavigationHandle(Context context) { this(context, null); } @@ -49,6 +71,10 @@ public class NavigationHandle extends View implements ButtonInterface { final Resources res = context.getResources(); mRadius = res.getDimension(R.dimen.navigation_handle_radius); mBottom = res.getDimension(R.dimen.navigation_handle_bottom); + mAdditionalWidthForAnimation = + res.getDimension(R.dimen.navigation_home_handle_additional_width_for_animation); + mAdditionalHeightForAnimation = + res.getDimension(R.dimen.navigation_home_handle_additional_height_for_animation); final int dualToneDarkTheme = Utils.getThemeAttr(context, R.attr.darkIconTheme); final int dualToneLightTheme = Utils.getThemeAttr(context, R.attr.lightIconTheme); @@ -75,23 +101,24 @@ public class NavigationHandle extends View implements ButtonInterface { // Draw that bar int navHeight = getHeight(); - float height = mRadius * 2; - int width = getWidth(); - float y = (navHeight - mBottom - height); - canvas.drawRoundRect(0, y, width, y + height, mRadius, mRadius, mPaint); + float additionalHeight = mAdditionalHeightForAnimation * mPulseAnimationProgress; + float height = mRadius * 2 + additionalHeight; + float additionalWidth = mAdditionalWidthForAnimation * mPulseAnimationProgress; + float width = getWidth() + additionalWidth; + float x = -(additionalWidth / 2); + float y = navHeight - mBottom - height - (additionalHeight / 2); + float adjustedRadius = height / 2; + canvas.drawRoundRect(x, y, width, y + height, adjustedRadius, adjustedRadius, mPaint); } @Override - public void setImageDrawable(Drawable drawable) { - } + public void setImageDrawable(Drawable drawable) {} @Override - public void abortCurrentGesture() { - } + public void abortCurrentGesture() {} @Override - public void setVertical(boolean vertical) { - } + public void setVertical(boolean vertical) {} @Override public void setDarkIntensity(float intensity) { @@ -108,6 +135,43 @@ public class NavigationHandle extends View implements ButtonInterface { } @Override - public void setDelayTouchFeedback(boolean shouldDelay) { + public void setDelayTouchFeedback(boolean shouldDelay) {} + + @Override + public void animateLongPress(boolean isTouchDown, long durationMs) { + if (mPulseAnimator != null) { + mPulseAnimator.cancel(); + } + + Interpolator interpolator; + if (isTouchDown) { + // For now we animate the navbar expanding and contracting so that the navbar is the + // original size by the end of {@code duration}. This is because a screenshot is taken + // at that point and we don't want to capture the larger navbar. + // TODO(b/306400785): Determine a way to exclude navbar from the screenshot. + + // Fraction of the touch down animation to expand; remaining is used to contract again. + float expandFraction = 0.9f; + interpolator = t -> t <= expandFraction + ? Interpolators.clampToProgress(Interpolators.LEGACY, t, 0, expandFraction) + : 1 - Interpolators.clampToProgress( + Interpolators.LINEAR, t, expandFraction, 1); + } else { + interpolator = Interpolators.LEGACY_DECELERATE; + } + + mPulseAnimator = + ObjectAnimator.ofFloat(this, PULSE_ANIMATION_PROGRESS, isTouchDown ? 1 : 0); + mPulseAnimator.setDuration(durationMs).setInterpolator(interpolator); + mPulseAnimator.start(); + } + + private void setPulseAnimationProgress(float pulseAnimationProgress) { + mPulseAnimationProgress = pulseAnimationProgress; + invalidate(); + } + + private float getPulseAnimationProgress() { + return mPulseAnimationProgress; } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 4465504d12f571ecc331b69fe3aa8ac639808ae5..1334660ea4d5cbfe3f0c0b7a727c76f4803a99b6 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -244,6 +244,12 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis mShadeViewControllerLazy.get().handleExternalTouch(event)); } + @Override + public void animateNavBarLongPress(boolean isTouchDown, long durationMs) { + verifyCallerAndClearCallingIdentityPostMain("animateNavBarLongPress", () -> + notifyAnimateNavBarLongPress(isTouchDown, durationMs)); + } + @Override public void onBackPressed() { verifyCallerAndClearCallingIdentityPostMain("onBackPressed", () -> { @@ -914,6 +920,12 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis } } + private void notifyAnimateNavBarLongPress(boolean isTouchDown, long durationMs) { + for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) { + mConnectionCallbacks.get(i).animateNavBarLongPress(isTouchDown, durationMs); + } + } + public void notifyAssistantVisibilityChanged(float visibility) { try { if (mOverviewProxy != null) { @@ -1058,6 +1070,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis default void onAssistantGestureCompletion(float velocity) {} default void startAssistant(Bundle bundle) {} default void setAssistantOverridesRequested(int[] invocationTypes) {} + default void animateNavBarLongPress(boolean isTouchDown, long durationMs) {} } /**