diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 31266db9e1446715f23b9b6762ba23b5e8737fa4..68022b4fee24baf535435ac208ab79a3075b1325 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -26,6 +26,7 @@ import static android.view.InsetsState.containsType;
 import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
 import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_NAVIGATION_BARS;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
 
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NAV_BAR_HANDLE_FORCE_OPAQUE;
 import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
@@ -56,6 +57,8 @@ import android.content.IntentFilter;
 import android.content.res.Configuration;
 import android.database.ContentObserver;
 import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
 import android.inputmethodservice.InputMethodService;
 import android.net.Uri;
 import android.os.Binder;
@@ -71,6 +74,7 @@ import android.telecom.TelecomManager;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.Display;
+import android.view.Gravity;
 import android.view.InsetsState.InternalInsetsType;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -104,6 +108,7 @@ import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.stackdivider.Divider;
 import com.android.systemui.statusbar.AutoHideUiElement;
 import com.android.systemui.statusbar.CommandQueue;
@@ -133,7 +138,7 @@ import dagger.Lazy;
  * on clicks and view states of the nav bar.
  */
 public class NavigationBarFragment extends LifecycleFragment implements Callbacks,
-        NavigationModeController.ModeChangedListener {
+        NavigationModeController.ModeChangedListener, DisplayManager.DisplayListener {
 
     public static final String TAG = "NavigationBar";
     private static final boolean DEBUG = false;
@@ -141,6 +146,8 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
     private static final String EXTRA_DISABLE2_STATE = "disabled2_state";
     private static final String EXTRA_APPEARANCE = "appearance";
     private static final String EXTRA_TRANSIENT_STATE = "transient_state";
+    private static final String FIXED_ROTATION_TRANSFORM_SETTING_NAME = "fixed_rotation_transform";
+
 
     /** Allow some time inbetween the long press for back and recents. */
     private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200;
@@ -199,6 +206,23 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
     private boolean mIsOnDefaultDisplay;
     public boolean mHomeBlockedThisTouch;
 
+    /**
+     * When user is QuickSwitching between apps of different orientations, we'll draw a fake
+     * home handle on the orientation they originally touched down to start their swipe
+     * gesture to indicate to them that they can continue in that orientation without having to
+     * rotate the phone
+     * The secondary handle will show when we get
+     * {@link OverviewProxyListener#onQuickSwitchToNewTask(int)} callback with the
+     * original handle hidden and we'll flip the visibilities once the
+     * {@link #mTasksFrozenListener} fires
+     */
+    private NavigationHandle mOrientationHandle;
+    private WindowManager.LayoutParams mOrientationParams;
+    private boolean mFrozenTasks;
+    private int mStartingQuickSwitchRotation;
+    private int mCurrentRotation;
+    private boolean mFixedRotationEnabled;
+
     /** Only for default display */
     @Nullable
     private AssistHandleViewController mAssistHandlerViewController;
@@ -248,6 +272,12 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
             mShadeController.collapsePanel(true /* animate */);
         }
 
+        @Override
+        public void onQuickSwitchToNewTask(@Surface.Rotation int rotation) {
+            mStartingQuickSwitchRotation = rotation;
+            orientSecondaryHomeHandle();
+        }
+
         @Override
         public void startAssistant(Bundle bundle) {
             mAssistManager.startAssist(bundle);
@@ -271,6 +301,22 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
         }
     };
 
+    private TaskStackChangeListener mTasksFrozenListener = new TaskStackChangeListener() {
+        @Override
+        public void onRecentTaskListFrozenChanged(boolean frozen) {
+            mFrozenTasks = frozen;
+            orientSecondaryHomeHandle();
+        }
+    };
+
+    private NavigationBarTransitions.DarkIntensityListener mOrientationHandleIntensityListener =
+            new NavigationBarTransitions.DarkIntensityListener() {
+                @Override
+                public void onDarkIntensity(float darkIntensity) {
+                    mOrientationHandle.setDarkIntensity(darkIntensity);
+                }
+            };
+
     private final ContextButtonListener mRotationButtonListener = (button, visible) -> {
         if (visible) {
             // If the button will actually become visible and the navbar is about to hide,
@@ -294,6 +340,14 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
         }
     };
 
+    private final ContentObserver mFixedRotationObserver = new ContentObserver(
+            new Handler(Looper.getMainLooper())) {
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            updatedFixedRotation();
+        }
+    };
+
     private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener =
             new DeviceConfig.OnPropertiesChangedListener() {
         @Override
@@ -351,6 +405,10 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
                 Settings.Secure.getUriFor(Settings.Secure.ASSISTANT),
                 false /* notifyForDescendants */, mAssistContentObserver, UserHandle.USER_ALL);
 
+        mContentResolver.registerContentObserver(
+                Settings.Global.getUriFor(FIXED_ROTATION_TRANSFORM_SETTING_NAME),
+                false /* notifyForDescendants */, mFixedRotationObserver, UserHandle.USER_ALL);
+
         if (savedInstanceState != null) {
             mDisabledFlags1 = savedInstanceState.getInt(EXTRA_DISABLE_STATE, 0);
             mDisabledFlags2 = savedInstanceState.getInt(EXTRA_DISABLE2_STATE, 0);
@@ -376,6 +434,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
         mNavigationModeController.removeListener(this);
         mAccessibilityManagerWrapper.removeCallback(mAccessibilityListener);
         mContentResolver.unregisterContentObserver(mAssistContentObserver);
+        mContentResolver.unregisterContentObserver(mFixedRotationObserver);
 
         DeviceConfig.removeOnPropertiesChangedListener(mOnPropertiesChangedListener);
     }
@@ -406,6 +465,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
         }
         mNavigationBarView.setNavigationIconHints(mNavigationIconHints);
         mNavigationBarView.setWindowVisible(isNavBarWindowVisible());
+        updatedFixedRotation();
 
         prepareNavigationBarView();
         checkNavBarModes();
@@ -442,6 +502,9 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
                 new AssistHandleViewController(mHandler, mNavigationBarView);
             getBarTransitions().addDarkIntensityListener(mAssistHandlerViewController);
         }
+
+        initSecondaryHomeHandleForRotation();
+        ActivityManagerWrapper.getInstance().registerTaskStackListener(mTasksFrozenListener);
     }
 
     @Override
@@ -458,6 +521,13 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
         }
         mOverviewProxyService.removeCallback(mOverviewProxyListener);
         mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver);
+        ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTasksFrozenListener);
+        if (mOrientationHandle != null) {
+            resetSecondaryHandle();
+            getContext().getSystemService(DisplayManager.class).unregisterDisplayListener(this);
+            getBarTransitions().removeDarkIntensityListener(mOrientationHandleIntensityListener);
+            mWindowManager.removeView(mOrientationHandle);
+        }
     }
 
     @Override
@@ -490,6 +560,88 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
         repositionNavigationBar();
     }
 
+    private void initSecondaryHomeHandleForRotation() {
+        if (!canShowSecondaryHandle()) {
+            return;
+        }
+
+        getContext().getSystemService(DisplayManager.class)
+                .registerDisplayListener(this, new Handler(Looper.getMainLooper()));
+
+        mOrientationHandle = new VerticalNavigationHandle(getContext());
+
+        getBarTransitions().addDarkIntensityListener(mOrientationHandleIntensityListener);
+        mOrientationParams = new WindowManager.LayoutParams(0, 0,
+                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                        | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
+                        | WindowManager.LayoutParams.FLAG_SLIPPERY,
+                PixelFormat.TRANSLUCENT);
+        mWindowManager.addView(mOrientationHandle, mOrientationParams);
+        mOrientationHandle.setVisibility(View.GONE);
+    }
+
+    private void orientSecondaryHomeHandle() {
+        if (!canShowSecondaryHandle()) {
+            return;
+        }
+
+        if (!mFrozenTasks) {
+            resetSecondaryHandle();
+        } else {
+            int deltaRotation = deltaRotation(mCurrentRotation, mStartingQuickSwitchRotation);
+            int height = 0;
+            int width = 0;
+            Rect dispSize = mWindowManager.getCurrentWindowMetrics().getBounds();
+            switch (deltaRotation) {
+                case Surface.ROTATION_90:
+                case Surface.ROTATION_270:
+                    height = dispSize.height();
+                    width = getResources()
+                            .getDimensionPixelSize(R.dimen.navigation_bar_height);
+                    break;
+                case Surface.ROTATION_180:
+                case Surface.ROTATION_0:
+                    // TODO(b/152683657): Need to determine best UX for this
+                    resetSecondaryHandle();
+                    return;
+            }
+
+            mOrientationParams.gravity =
+                    deltaRotation == Surface.ROTATION_90 ? Gravity.LEFT : Gravity.RIGHT;
+            mOrientationParams.height = height;
+            mOrientationParams.width = width;
+            mWindowManager.updateViewLayout(mOrientationHandle, mOrientationParams);
+            mNavigationBarView.setVisibility(View.GONE);
+            mOrientationHandle.setVisibility(View.VISIBLE);
+        }
+    }
+
+    private void resetSecondaryHandle() {
+        if (mOrientationHandle != null) {
+            // Case where nav mode is changed w/o ever invoking a quickstep
+            // mOrientedHandle is initialized lazily
+            mOrientationHandle.setVisibility(View.GONE);
+        }
+        mNavigationBarView.setVisibility(View.VISIBLE);
+    }
+
+    private int deltaRotation(int oldRotation, int newRotation) {
+        int delta = newRotation - oldRotation;
+        if (delta < 0) delta += 4;
+        return delta;
+    }
+
+    private void updatedFixedRotation() {
+        mFixedRotationEnabled = Settings.Global.getInt(getContext().getContentResolver(),
+                FIXED_ROTATION_TRANSFORM_SETTING_NAME, 0) != 0;
+        if (!canShowSecondaryHandle()) {
+            resetSecondaryHandle();
+        }
+    }
+
     @Override
     public void dump(String prefix, FileDescriptor fd, PrintWriter pw, String[] args) {
         if (mNavigationBarView != null) {
@@ -1115,6 +1267,10 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
         mNavBarMode = mode;
         updateScreenPinningGestures();
 
+        if (!canShowSecondaryHandle()) {
+            resetSecondaryHandle();
+        }
+
         // Workaround for b/132825155, for secondary users, we currently don't receive configuration
         // changes on overlay package change since SystemUI runs for the system user. In this case,
         // trigger a new configuration change to ensure that the nav bar is updated in the same way.
@@ -1159,6 +1315,34 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
     private final AccessibilityServicesStateChangeListener mAccessibilityListener =
             this::updateAccessibilityServicesState;
 
+    @Override
+    public void onDisplayAdded(int displayId) {
+
+    }
+
+    @Override
+    public void onDisplayRemoved(int displayId) {
+
+    }
+
+    @Override
+    public void onDisplayChanged(int displayId) {
+        if (!canShowSecondaryHandle()) {
+            return;
+        }
+
+        int rotation = getContext().getResources().getConfiguration()
+                .windowConfiguration.getRotation();
+        if (rotation != mCurrentRotation) {
+            mCurrentRotation = rotation;
+            orientSecondaryHomeHandle();
+        }
+    }
+
+    private boolean canShowSecondaryHandle() {
+        return mFixedRotationEnabled && mNavBarMode == NAV_BAR_MODE_GESTURAL;
+    }
+
     private final Consumer<Integer> mRotationWatcher = rotation -> {
         if (mNavigationBarView != null
                 && mNavigationBarView.needsReorient(rotation)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationHandle.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationHandle.java
index abceb11b36e99cc07eb08071a7b9166755d14a55..b87479505d00a0e33fa94bb7aa9ac7222befa5ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationHandle.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationHandle.java
@@ -32,11 +32,11 @@ import com.android.systemui.R;
 
 public class NavigationHandle extends View implements ButtonInterface {
 
-    private final Paint mPaint = new Paint();
+    protected final Paint mPaint = new Paint();
     private @ColorInt final int mLightColor;
     private @ColorInt final int mDarkColor;
-    private final int mRadius;
-    private final int mBottom;
+    protected final int mRadius;
+    protected final int mBottom;
     private boolean mRequiresInvalidate;
 
     public NavigationHandle(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/VerticalNavigationHandle.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/VerticalNavigationHandle.java
new file mode 100644
index 0000000000000000000000000000000000000000..a15ca9532a88ec95f530a38885094cda3e73dd04
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/VerticalNavigationHandle.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.content.Context;
+import android.graphics.Canvas;
+
+import com.android.systemui.R;
+
+/** Temporarily shown view when using QuickSwitch to switch between apps of different rotations */
+public class VerticalNavigationHandle extends NavigationHandle {
+    private final int mWidth;
+
+    public VerticalNavigationHandle(Context context) {
+        super(context);
+        mWidth = context.getResources().getDimensionPixelSize(R.dimen.navigation_home_handle_width);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        int left;
+        int top;
+        int bottom;
+        int right;
+
+        int radiusOffset = mRadius * 2;
+        right = getWidth() - mBottom;
+        top = getHeight() / 2 - (mWidth / 2); /* (height of screen / 2) - (height of bar / 2) */
+        left = getWidth() - mBottom - radiusOffset;
+        bottom = getHeight() / 2 + (mWidth / 2);
+        canvas.drawRoundRect(left, top, right, bottom, mRadius, mRadius, mPaint);
+    }
+}