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) {}
     }
 
     /**