Merge "min_sdk_version should be (level) and not (kind+level)." into tm-qpr-dev
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 953f17a..f9e8411 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -9883,9 +9883,12 @@
     }
 
     void checkThread() {
-        if (mThread != Thread.currentThread()) {
+        Thread current = Thread.currentThread();
+        if (mThread != current) {
             throw new CalledFromWrongThreadException(
-                    "Only the original thread that created a view hierarchy can touch its views.");
+                    "Only the original thread that created a view hierarchy can touch its views."
+                            + " Expected: " + mThread.getName()
+                            + " Calling: " + current.getName());
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 1239cdc..4578523 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -194,7 +194,8 @@
             DisplayController displayController,
             SyncTransactionQueue syncQueue,
             Optional<DesktopModeController> desktopModeController,
-            Optional<DesktopTasksController> desktopTasksController) {
+            Optional<DesktopTasksController> desktopTasksController,
+            Optional<SplitScreenController> splitScreenController) {
         if (DesktopModeStatus.isAnyEnabled()) {
             return new DesktopModeWindowDecorViewModel(
                     context,
@@ -204,7 +205,8 @@
                     displayController,
                     syncQueue,
                     desktopModeController,
-                    desktopTasksController);
+                    desktopTasksController,
+                    splitScreenController);
         }
         return new CaptionWindowDecorViewModel(
                     context,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index c7ad4fd..94b9e90 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -422,6 +422,11 @@
         mStageCoordinator.goToFullscreenFromSplit();
     }
 
+    /** Move the specified task to fullscreen, regardless of focus state. */
+    public void moveTaskToFullscreen(int taskId) {
+        mStageCoordinator.moveTaskToFullscreen(taskId);
+    }
+
     public boolean isLaunchToSplit(TaskInfo taskInfo) {
         return mStageCoordinator.isLaunchToSplit(taskInfo);
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index f102f8e..a673384 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -2390,6 +2390,20 @@
         mSplitLayout.flingDividerToDismiss(!leftOrTop, EXIT_REASON_FULLSCREEN_SHORTCUT);
     }
 
+    /** Move the specified task to fullscreen, regardless of focus state. */
+    public void moveTaskToFullscreen(int taskId) {
+        boolean leftOrTop;
+        if (mMainStage.containsTask(taskId)) {
+            leftOrTop = (mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT);
+        } else if (mSideStage.containsTask(taskId)) {
+            leftOrTop = (mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT);
+        } else {
+            return;
+        }
+        mSplitLayout.flingDividerToDismiss(!leftOrTop, EXIT_REASON_FULLSCREEN_SHORTCUT);
+
+    }
+
     boolean isLaunchToSplit(TaskInfo taskInfo) {
         return getActivateSplitPosition(taskInfo) != SPLIT_POSITION_UNDEFINED;
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 8dfa18c..c517f7b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -20,10 +20,14 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
+
 import android.app.ActivityManager;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityTaskManager;
 import android.content.Context;
+import android.graphics.Rect;
 import android.hardware.input.InputManager;
 import android.os.Handler;
 import android.os.Looper;
@@ -37,7 +41,6 @@
 import android.view.SurfaceControl;
 import android.view.View;
 import android.window.WindowContainerToken;
-import android.window.WindowContainerTransaction;
 
 import androidx.annotation.Nullable;
 
@@ -50,6 +53,7 @@
 import com.android.wm.shell.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.desktopmode.DesktopTasksController;
 import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
+import com.android.wm.shell.splitscreen.SplitScreenController;
 
 import java.util.Optional;
 
@@ -80,6 +84,8 @@
     private final InputMonitorFactory mInputMonitorFactory;
     private TaskOperations mTaskOperations;
 
+    private Optional<SplitScreenController> mSplitScreenController;
+
     public DesktopModeWindowDecorViewModel(
             Context context,
             Handler mainHandler,
@@ -88,7 +94,8 @@
             DisplayController displayController,
             SyncTransactionQueue syncQueue,
             Optional<DesktopModeController> desktopModeController,
-            Optional<DesktopTasksController> desktopTasksController) {
+            Optional<DesktopTasksController> desktopTasksController,
+            Optional<SplitScreenController> splitScreenController) {
         this(
                 context,
                 mainHandler,
@@ -98,6 +105,7 @@
                 syncQueue,
                 desktopModeController,
                 desktopTasksController,
+                splitScreenController,
                 new DesktopModeWindowDecoration.Factory(),
                 new InputMonitorFactory());
     }
@@ -112,6 +120,7 @@
             SyncTransactionQueue syncQueue,
             Optional<DesktopModeController> desktopModeController,
             Optional<DesktopTasksController> desktopTasksController,
+            Optional<SplitScreenController> splitScreenController,
             DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory,
             InputMonitorFactory inputMonitorFactory) {
         mContext = context;
@@ -120,6 +129,7 @@
         mActivityTaskManager = mContext.getSystemService(ActivityTaskManager.class);
         mTaskOrganizer = taskOrganizer;
         mDisplayController = displayController;
+        mSplitScreenController = splitScreenController;
         mSyncQueue = syncQueue;
         mDesktopModeController = desktopModeController;
         mDesktopTasksController = desktopTasksController;
@@ -230,6 +240,15 @@
             final int id = v.getId();
             if (id == R.id.close_window || id == R.id.close_button) {
                 mTaskOperations.closeTask(mTaskToken);
+                if (mSplitScreenController.isPresent()
+                        && mSplitScreenController.get().isSplitScreenVisible()) {
+                    int remainingTaskPosition = mTaskId == mSplitScreenController.get()
+                            .getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT).taskId
+                            ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT;
+                    ActivityManager.RunningTaskInfo remainingTask = mSplitScreenController.get()
+                            .getTaskInfo(remainingTaskPosition);
+                    mSplitScreenController.get().moveTaskToFullscreen(remainingTask.taskId);
+                }
             } else if (id == R.id.back_button) {
                 mTaskOperations.injectBackKey();
             } else if (id == R.id.caption_handle) {
@@ -261,9 +280,6 @@
                     if (taskInfo.isFocused) {
                         return mDragDetector.isDragEvent();
                     }
-                    final WindowContainerTransaction wct = new WindowContainerTransaction();
-                    wct.reorder(mTaskToken, true /* onTop */);
-                    mSyncQueue.queue(wct);
                     return false;
                 case MotionEvent.ACTION_UP:
                 case MotionEvent.ACTION_CANCEL:
@@ -401,14 +417,14 @@
      * @param ev the {@link MotionEvent} received by {@link EventReceiver}
      */
     private void handleReceivedMotionEvent(MotionEvent ev, InputMonitor inputMonitor) {
+        final DesktopModeWindowDecoration relevantDecor = getRelevantWindowDecor(ev);
         if (DesktopModeStatus.isProto2Enabled()) {
-            final DesktopModeWindowDecoration focusedDecor = getFocusedDecor();
-            if (focusedDecor == null
-                    || focusedDecor.mTaskInfo.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
-                handleCaptionThroughStatusBar(ev);
+            if (relevantDecor == null
+                    || relevantDecor.mTaskInfo.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
+                handleCaptionThroughStatusBar(ev, relevantDecor);
             }
         }
-        handleEventOutsideFocusedCaption(ev);
+        handleEventOutsideFocusedCaption(ev, relevantDecor);
         // Prevent status bar from reacting to a caption drag.
         if (DesktopModeStatus.isProto2Enabled()) {
             if (mTransitionDragActive) {
@@ -422,16 +438,16 @@
     }
 
     // If an UP/CANCEL action is received outside of caption bounds, turn off handle menu
-    private void handleEventOutsideFocusedCaption(MotionEvent ev) {
+    private void handleEventOutsideFocusedCaption(MotionEvent ev,
+            DesktopModeWindowDecoration relevantDecor) {
         final int action = ev.getActionMasked();
         if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
-            final DesktopModeWindowDecoration focusedDecor = getFocusedDecor();
-            if (focusedDecor == null) {
+            if (relevantDecor == null) {
                 return;
             }
 
             if (!mTransitionDragActive) {
-                focusedDecor.closeHandleMenuIfNeeded(ev);
+                relevantDecor.closeHandleMenuIfNeeded(ev);
             }
         }
     }
@@ -441,39 +457,38 @@
      * Perform caption actions if not able to through normal means.
      * Turn on desktop mode if handle is dragged below status bar.
      */
-    private void handleCaptionThroughStatusBar(MotionEvent ev) {
+    private void handleCaptionThroughStatusBar(MotionEvent ev,
+            DesktopModeWindowDecoration relevantDecor) {
         switch (ev.getActionMasked()) {
             case MotionEvent.ACTION_DOWN: {
                 // Begin drag through status bar if applicable.
-                final DesktopModeWindowDecoration focusedDecor = getFocusedDecor();
-                if (focusedDecor != null) {
+                if (relevantDecor != null) {
                     boolean dragFromStatusBarAllowed = false;
                     if (DesktopModeStatus.isProto2Enabled()) {
                         // In proto2 any full screen task can be dragged to freeform
-                        dragFromStatusBarAllowed = focusedDecor.mTaskInfo.getWindowingMode()
+                        dragFromStatusBarAllowed = relevantDecor.mTaskInfo.getWindowingMode()
                                 == WINDOWING_MODE_FULLSCREEN;
                     }
 
-                    if (dragFromStatusBarAllowed && focusedDecor.checkTouchEventInHandle(ev)) {
+                    if (dragFromStatusBarAllowed && relevantDecor.checkTouchEventInHandle(ev)) {
                         mTransitionDragActive = true;
                     }
                 }
                 break;
             }
             case MotionEvent.ACTION_UP: {
-                final DesktopModeWindowDecoration focusedDecor = getFocusedDecor();
-                if (focusedDecor == null) {
+                if (relevantDecor == null) {
                     mTransitionDragActive = false;
                     return;
                 }
                 if (mTransitionDragActive) {
                     mTransitionDragActive = false;
                     final int statusBarHeight = mDisplayController
-                            .getDisplayLayout(focusedDecor.mTaskInfo.displayId).stableInsets().top;
+                            .getDisplayLayout(relevantDecor.mTaskInfo.displayId).stableInsets().top;
                     if (ev.getY() > statusBarHeight) {
                         if (DesktopModeStatus.isProto2Enabled()) {
                             mDesktopTasksController.ifPresent(
-                                    c -> c.moveToDesktop(focusedDecor.mTaskInfo));
+                                    c -> c.moveToDesktop(relevantDecor.mTaskInfo));
                         } else if (DesktopModeStatus.isProto1Enabled()) {
                             mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(true));
                         }
@@ -481,7 +496,7 @@
                         return;
                     }
                 }
-                focusedDecor.checkClickEvent(ev);
+                relevantDecor.checkClickEvent(ev);
                 break;
             }
             case MotionEvent.ACTION_CANCEL: {
@@ -491,6 +506,38 @@
     }
 
     @Nullable
+    private DesktopModeWindowDecoration getRelevantWindowDecor(MotionEvent ev) {
+        if (mSplitScreenController.isPresent()
+                && mSplitScreenController.get().isSplitScreenVisible()) {
+            // We can't look at focused task here as only one task will have focus.
+            return getSplitScreenDecor(ev);
+        } else {
+            return getFocusedDecor();
+        }
+    }
+
+    @Nullable
+    private DesktopModeWindowDecoration getSplitScreenDecor(MotionEvent ev) {
+        ActivityManager.RunningTaskInfo topOrLeftTask =
+                mSplitScreenController.get().getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT);
+        ActivityManager.RunningTaskInfo bottomOrRightTask =
+                mSplitScreenController.get().getTaskInfo(SPLIT_POSITION_BOTTOM_OR_RIGHT);
+        if (topOrLeftTask != null && topOrLeftTask.getConfiguration()
+                .windowConfiguration.getBounds().contains((int) ev.getX(), (int) ev.getY())) {
+            return mWindowDecorByTaskId.get(topOrLeftTask.taskId);
+        } else if (bottomOrRightTask != null && bottomOrRightTask.getConfiguration()
+                .windowConfiguration.getBounds().contains((int) ev.getX(), (int) ev.getY())) {
+            Rect bottomOrRightBounds = bottomOrRightTask.getConfiguration().windowConfiguration
+                    .getBounds();
+            ev.offsetLocation(-bottomOrRightBounds.left, -bottomOrRightBounds.top);
+            return mWindowDecorByTaskId.get(bottomOrRightTask.taskId);
+        } else {
+            return null;
+        }
+
+    }
+
+    @Nullable
     private DesktopModeWindowDecoration getFocusedDecor() {
         final int size = mWindowDecorByTaskId.size();
         DesktopModeWindowDecoration focusedDecor = null;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java
index 3550721..1d1aa79 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java
@@ -49,6 +49,7 @@
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.desktopmode.DesktopModeController;
 import com.android.wm.shell.desktopmode.DesktopTasksController;
+import com.android.wm.shell.splitscreen.SplitScreenController;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -73,6 +74,7 @@
     @Mock private Choreographer mMainChoreographer;
     @Mock private ShellTaskOrganizer mTaskOrganizer;
     @Mock private DisplayController mDisplayController;
+    @Mock private SplitScreenController mSplitScreenController;
     @Mock private SyncTransactionQueue mSyncQueue;
     @Mock private DesktopModeController mDesktopModeController;
     @Mock private DesktopTasksController mDesktopTasksController;
@@ -98,6 +100,7 @@
                 mSyncQueue,
                 Optional.of(mDesktopModeController),
                 Optional.of(mDesktopTasksController),
+                Optional.of(mSplitScreenController),
                 mDesktopModeWindowDecorFactory,
                 mMockInputMonitorFactory
             );
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 464ce03..48f257a 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2262,6 +2262,10 @@
          panel (embedded activity) instead of controls rendered by SystemUI [CHAR LIMIT=NONE] -->
     <string name="controls_panel_authorization">When you add <xliff:g id="appName" example="My app">%s</xliff:g>, it can add controls and content to this panel. In some apps, you can choose which controls show up here.</string>
 
+    <!-- Shows in a dialog presented to the user to authorize this app removal from a Device
+         controls panel [CHAR LIMIT=NONE] -->
+    <string name="controls_panel_remove_app_authorization">Remove controls for <xliff:g example="My app" id="appName">%s</xliff:g>?</string>
+
     <!-- a11y state description for a control that is currently favorited [CHAR LIMIT=NONE] -->
     <string name="accessibility_control_favorite">Favorited</string>
     <!-- a11y state description for a control that is currently favorited with its position [CHAR LIMIT=NONE] -->
@@ -2302,6 +2306,8 @@
     <string name="controls_dialog_title">Add to device controls</string>
     <!-- Controls dialog add to favorites [CHAR LIMIT=40] -->
     <string name="controls_dialog_ok">Add</string>
+    <!-- Controls dialog remove app from a panel [CHAR LIMIT=40] -->
+    <string name="controls_dialog_remove">Remove</string>
     <!-- Controls dialog message. Indicates app that suggested this control [CHAR LIMIT=NONE] -->
     <string name="controls_dialog_message">Suggested by <xliff:g id="app" example="System UI">%s</xliff:g></string>
     <!-- Controls tile secondary label when device is locked and user does not want access to controls from lockscreen [CHAR LIMIT=20] -->
@@ -2419,6 +2425,8 @@
     <string name="controls_menu_edit">Edit controls</string>
     <!-- Controls menu, add another app [CHAR LIMIT=30] -->
     <string name="controls_menu_add_another_app">Add app</string>
+    <!-- Controls menu, remove app [CHAR_LIMIT=30] -->
+    <string name="controls_menu_remove">Remove app</string>
 
     <!-- Title for the media output dialog with media related devices [CHAR LIMIT=50] -->
     <string name="media_output_dialog_add_output">Add outputs</string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
index b927155..8690b36 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
@@ -62,7 +62,7 @@
      */
     public void updateThumbnailMatrix(Rect thumbnailBounds, ThumbnailData thumbnailData,
             int canvasWidth, int canvasHeight, int screenWidthPx, int screenHeightPx,
-            int taskbarSize, boolean isTablet,
+            int taskbarSize, boolean isLargeScreen,
             int currentRotation, boolean isRtl) {
         boolean isRotated = false;
         boolean isOrientationDifferent;
@@ -95,7 +95,7 @@
             canvasScreenRatio = (float) canvasWidth / screenWidthPx;
         }
         scaledTaskbarSize = taskbarSize * canvasScreenRatio;
-        thumbnailClipHint.bottom = isTablet ? scaledTaskbarSize : 0;
+        thumbnailClipHint.bottom = isLargeScreen ? scaledTaskbarSize : 0;
 
         float scale = thumbnailData.scale;
         final float thumbnailScale;
@@ -103,7 +103,7 @@
         // Landscape vs portrait change.
         // Note: Disable rotation in grid layout.
         boolean windowingModeSupportsRotation =
-                thumbnailData.windowingMode == WINDOWING_MODE_FULLSCREEN && !isTablet;
+                thumbnailData.windowingMode == WINDOWING_MODE_FULLSCREEN && !isLargeScreen;
         isOrientationDifferent = isOrientationChange(deltaRotate)
                 && windowingModeSupportsRotation;
         if (canvasWidth == 0 || canvasHeight == 0 || scale == 0) {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
index 77a13bd..751a3f8 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
@@ -137,7 +137,7 @@
 
     /** @return whether or not {@param context} represents that of a large screen device or not */
     @TargetApi(Build.VERSION_CODES.R)
-    public static boolean isTablet(Context context) {
+    public static boolean isLargeScreen(Context context) {
         final WindowManager windowManager = context.getSystemService(WindowManager.class);
         final Rect bounds = windowManager.getCurrentWindowMetrics().getBounds();
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
index d221e22..a010c9a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
@@ -26,6 +26,7 @@
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.ViewGroup.MarginLayoutParams;
+import android.view.WindowInsets;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodManager;
@@ -156,6 +157,15 @@
         // TODO: Remove this workaround by ensuring such a race condition never happens.
         mMainExecutor.executeDelayed(
                 this::updateSwitchImeButton, DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON);
+        mView.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
+            @Override
+            public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
+                if (!mKeyguardViewController.isBouncerShowing()) {
+                    mView.hideKeyboard();
+                }
+                return insets;
+            }
+        });
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
index cef415c..f6217f1 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
@@ -129,8 +129,8 @@
         AnimatorSet animatorSet = new AnimatorSet();
         animatorSet.playTogether(textSizeAnimator, textOpacityAnimator, textFadeAnimator);
 
-        // For tablet docking animation, we don't play the background scrim.
-        if (!Utilities.isTablet(context)) {
+        // For large screens docking animation, we don't play the background scrim.
+        if (!Utilities.isLargeScreen(context)) {
             ValueAnimator scrimFadeInAnimator = ObjectAnimator.ofArgb(this,
                     "backgroundColor", Color.TRANSPARENT, SCRIM_COLOR);
             scrimFadeInAnimator.setDuration(SCRIM_FADE_DURATION);
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
index 822190f..3555d0a 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
@@ -166,6 +166,19 @@
     )
 
     /**
+     * Removes favorites for a given component
+     * @param componentName the name of the service that provides the [Control]
+     * @return true when favorites is scheduled for deletion
+     */
+    fun removeFavorites(componentName: ComponentName): Boolean
+
+    /**
+     * Checks if the favorites can be removed. You can't remove components from the preferred list.
+     * @param componentName the name of the service that provides the [Control]
+     */
+    fun canRemoveFavorites(componentName: ComponentName): Boolean
+
+    /**
      * Replaces the favorites for the given structure.
      *
      * Calling this method will eliminate the previous selection of favorites and replace it with a
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index 278ee70..8547903 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -497,6 +497,21 @@
         }
     }
 
+    override fun canRemoveFavorites(componentName: ComponentName): Boolean =
+            !authorizedPanelsRepository.getPreferredPackages().contains(componentName.packageName)
+
+    override fun removeFavorites(componentName: ComponentName): Boolean {
+        if (!confirmAvailability()) return false
+        if (!canRemoveFavorites(componentName)) return false
+
+        executor.execute {
+            Favorites.removeStructures(componentName)
+            authorizedPanelsRepository.removeAuthorizedPanels(setOf(componentName.packageName))
+            persistenceWrapper.storeFavorites(Favorites.getAllStructures())
+        }
+        return true
+    }
+
     override fun replaceFavoritesForStructure(structureInfo: StructureInfo) {
         if (!confirmAvailability()) return
         executor.execute {
@@ -655,10 +670,11 @@
         return true
     }
 
-    fun removeStructures(componentName: ComponentName) {
+    fun removeStructures(componentName: ComponentName): Boolean {
         val newFavMap = favMap.toMutableMap()
-        newFavMap.remove(componentName)
+        val removed = newFavMap.remove(componentName) != null
         favMap = newFavMap
+        return removed
     }
 
     fun addFavorite(
diff --git a/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepository.kt b/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepository.kt
index 3e672f3..ae9c37a 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepository.kt
@@ -26,6 +26,14 @@
     /** A set of package names that the user has previously authorized to show panels. */
     fun getAuthorizedPanels(): Set<String>
 
+    /** Preferred applications to query controls suggestions from */
+    fun getPreferredPackages(): Set<String>
+
     /** Adds [packageNames] to the set of packages that the user has authorized to show panels. */
     fun addAuthorizedPanels(packageNames: Set<String>)
+
+    /**
+     * Removes [packageNames] from the set of packages that the user has authorized to show panels.
+     */
+    fun removeAuthorizedPanels(packageNames: Set<String>)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImpl.kt
index f7e43a7..e51e832 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImpl.kt
@@ -37,10 +37,20 @@
         return getAuthorizedPanelsInternal(instantiateSharedPrefs())
     }
 
+    override fun getPreferredPackages(): Set<String> =
+        context.resources.getStringArray(R.array.config_controlsPreferredPackages).toSet()
+
     override fun addAuthorizedPanels(packageNames: Set<String>) {
         addAuthorizedPanelsInternal(instantiateSharedPrefs(), packageNames)
     }
 
+    override fun removeAuthorizedPanels(packageNames: Set<String>) {
+        with(instantiateSharedPrefs()) {
+            val currentSet = getAuthorizedPanelsInternal(this)
+            edit().putStringSet(KEY, currentSet - packageNames).apply()
+        }
+    }
+
     private fun getAuthorizedPanelsInternal(sharedPreferences: SharedPreferences): Set<String> {
         return sharedPreferences.getStringSet(KEY, emptySet())!!
     }
@@ -63,15 +73,7 @@
 
         // If we've never run this (i.e., the key doesn't exist), add the default packages
         if (sharedPref.getStringSet(KEY, null) == null) {
-            sharedPref
-                .edit()
-                .putStringSet(
-                    KEY,
-                    context.resources
-                        .getStringArray(R.array.config_controlsPreferredPackages)
-                        .toSet()
-                )
-                .apply()
+            sharedPref.edit().putStringSet(KEY, getPreferredPackages()).apply()
         }
         return sharedPref
     }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialogsFactory.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialogsFactory.kt
new file mode 100644
index 0000000..d6cfb79
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialogsFactory.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2023 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.controls.ui
+
+import android.app.Dialog
+import android.content.Context
+import android.content.DialogInterface
+import com.android.systemui.R
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import java.util.function.Consumer
+import javax.inject.Inject
+
+class ControlsDialogsFactory(private val internalDialogFactory: (Context) -> SystemUIDialog) {
+
+    @Inject constructor() : this({ SystemUIDialog(it) })
+
+    fun createRemoveAppDialog(
+        context: Context,
+        appName: CharSequence,
+        response: Consumer<Boolean>
+    ): Dialog {
+        val listener =
+            DialogInterface.OnClickListener { _, which ->
+                response.accept(which == DialogInterface.BUTTON_POSITIVE)
+            }
+        return internalDialogFactory(context).apply {
+            setTitle(context.getString(R.string.controls_panel_remove_app_authorization, appName))
+            setCanceledOnTouchOutside(true)
+            setOnCancelListener { response.accept(false) }
+            setPositiveButton(R.string.controls_dialog_remove, listener)
+            setNeutralButton(R.string.cancel, listener)
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 860601b..c61dad6 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -21,6 +21,7 @@
 import android.animation.ObjectAnimator
 import android.app.Activity
 import android.app.ActivityOptions
+import android.app.Dialog
 import android.app.PendingIntent
 import android.content.ComponentName
 import android.content.Context
@@ -52,7 +53,6 @@
 import com.android.systemui.R
 import com.android.systemui.controls.ControlsMetricsLogger
 import com.android.systemui.controls.ControlsServiceInfo
-import com.android.systemui.controls.settings.ControlsSettingsRepository
 import com.android.systemui.controls.CustomIconCache
 import com.android.systemui.controls.controller.ControlsController
 import com.android.systemui.controls.controller.StructureInfo
@@ -64,6 +64,7 @@
 import com.android.systemui.controls.management.ControlsListingController
 import com.android.systemui.controls.management.ControlsProviderSelectorActivity
 import com.android.systemui.controls.panels.AuthorizedPanelsRepository
+import com.android.systemui.controls.settings.ControlsSettingsRepository
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
@@ -83,7 +84,7 @@
 import dagger.Lazy
 import java.io.PrintWriter
 import java.text.Collator
-import java.util.Optional
+import java.util.*
 import java.util.function.Consumer
 import javax.inject.Inject
 
@@ -108,6 +109,7 @@
         private val controlsSettingsRepository: ControlsSettingsRepository,
         private val authorizedPanelsRepository: AuthorizedPanelsRepository,
         private val featureFlags: FeatureFlags,
+        private val dialogsFactory: ControlsDialogsFactory,
         dumpManager: DumpManager
 ) : ControlsUiController, Dumpable {
 
@@ -122,6 +124,7 @@
         private const val ADD_CONTROLS_ID = 1L
         private const val ADD_APP_ID = 2L
         private const val EDIT_CONTROLS_ID = 3L
+        private const val REMOVE_APP_ID = 4L
     }
 
     private var selectedItem: SelectedItem = SelectedItem.EMPTY_SELECTION
@@ -151,6 +154,7 @@
 
     private var openAppIntent: Intent? = null
     private var overflowMenuAdapter: BaseAdapter? = null
+    private var removeAppDialog: Dialog? = null
 
     private val onSeedingComplete = Consumer<Boolean> {
         accepted ->
@@ -330,6 +334,31 @@
         }
     }
 
+    @VisibleForTesting
+    internal fun startRemovingApp(componentName: ComponentName, appName: CharSequence) {
+        removeAppDialog?.cancel()
+        removeAppDialog = dialogsFactory.createRemoveAppDialog(context, appName) {
+            if (!controlsController.get().removeFavorites(componentName)) {
+                return@createRemoveAppDialog
+            }
+            if (
+                    sharedPreferences.getString(PREF_COMPONENT, "") ==
+                    componentName.flattenToString()
+            ) {
+                sharedPreferences
+                        .edit()
+                        .remove(PREF_COMPONENT)
+                        .remove(PREF_STRUCTURE_OR_APP_NAME)
+                        .remove(PREF_IS_PANEL)
+                        .commit()
+            }
+
+            allStructures = controlsController.get().getFavorites()
+            selectedItem = getPreferredSelectedItem(allStructures)
+            reload(parent)
+        }.apply { show() }
+    }
+
     private fun startTargetedActivity(si: StructureInfo, klazz: Class<*>) {
         val i = Intent(activityContext, klazz)
         putIntentExtras(i, si)
@@ -433,7 +462,10 @@
         val currentApps = panelsAndStructures.map { it.componentName }.toSet()
         val allApps = controlsListingController.get()
                 .getCurrentServices().map { it.componentName }.toSet()
-        createMenu(extraApps = (allApps - currentApps).isNotEmpty())
+        createMenu(
+                selectionItem = selectionItem,
+                extraApps = (allApps - currentApps).isNotEmpty(),
+        )
     }
 
     private fun createPanelView(componentName: ComponentName) {
@@ -472,7 +504,7 @@
         }
     }
 
-    private fun createMenu(extraApps: Boolean) {
+    private fun createMenu(selectionItem: SelectionItem, extraApps: Boolean) {
         val isPanel = selectedItem is SelectedItem.PanelItem
         val selectedStructure = (selectedItem as? SelectedItem.StructureItem)?.structure
                 ?: EMPTY_STRUCTURE
@@ -490,6 +522,13 @@
                             ADD_APP_ID
                     ))
                 }
+                if (featureFlags.isEnabled(Flags.APP_PANELS_REMOVE_APPS_ALLOWED) &&
+                        controlsController.get().canRemoveFavorites(selectedItem.componentName)) {
+                    add(OverflowMenuAdapter.MenuItem(
+                            context.getText(R.string.controls_menu_remove),
+                            REMOVE_APP_ID,
+                    ))
+                }
             } else {
                 add(OverflowMenuAdapter.MenuItem(
                         context.getText(R.string.controls_menu_add),
@@ -529,6 +568,9 @@
                                 ADD_APP_ID -> startProviderSelectorActivity()
                                 ADD_CONTROLS_ID -> startFavoritingActivity(selectedStructure)
                                 EDIT_CONTROLS_ID -> startEditingActivity(selectedStructure)
+                                REMOVE_APP_ID -> startRemovingApp(
+                                        selectedStructure.componentName, selectionItem.appName
+                                )
                             }
                             dismiss()
                         }
@@ -732,6 +774,7 @@
             it.value.dismiss()
         }
         controlActionCoordinator.closeDialogs()
+        removeAppDialog?.cancel()
     }
 
     override fun hide(parent: ViewGroup) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index f964cb3..8ae171f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -950,9 +950,9 @@
             return false
         }
 
-        // We don't do the shared element on tablets because they're large and the smartspace has to
-        // fly across large distances, which is distracting.
-        if (Utilities.isTablet(context)) {
+        // We don't do the shared element on large screens because the smartspace has to fly across
+        // large distances, which is distracting.
+        if (Utilities.isLargeScreen(context)) {
             return false
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt
index d4991f9..9b9d561 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt
@@ -33,7 +33,7 @@
 import com.android.systemui.mediaprojection.appselector.data.RecentTask
 import com.android.systemui.shared.recents.model.ThumbnailData
 import com.android.systemui.shared.recents.utilities.PreviewPositionHelper
-import com.android.systemui.shared.recents.utilities.Utilities.isTablet
+import com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen
 
 /**
  * Custom view that shows a thumbnail preview of one recent task based on [ThumbnailData].
@@ -150,9 +150,9 @@
         val displayWidthPx = windowMetrics.bounds.width()
         val displayHeightPx = windowMetrics.bounds.height()
         val isRtl = layoutDirection == LAYOUT_DIRECTION_RTL
-        val isTablet = isTablet(context)
+        val isLargeScreen = isLargeScreen(context)
         val taskbarSize =
-            if (isTablet) {
+            if (isLargeScreen) {
                 resources.getDimensionPixelSize(AndroidR.dimen.taskbar_frame_height)
             } else {
                 0
@@ -166,7 +166,7 @@
             displayWidthPx,
             displayHeightPx,
             taskbarSize,
-            isTablet,
+            isLargeScreen,
             currentRotation,
             isRtl
         )
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt
index 88d5eaa..1c90154 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt
@@ -23,7 +23,7 @@
 import com.android.internal.R as AndroidR
 import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorScope
 import com.android.systemui.mediaprojection.appselector.view.TaskPreviewSizeProvider.TaskPreviewSizeListener
-import com.android.systemui.shared.recents.utilities.Utilities.isTablet
+import com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen
 import com.android.systemui.statusbar.policy.CallbackController
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
@@ -61,8 +61,8 @@
         val width = windowMetrics.bounds.width()
         var height = maximumWindowHeight
 
-        val isTablet = isTablet(context)
-        if (isTablet) {
+        val isLargeScreen = isLargeScreen(context)
+        if (isLargeScreen) {
             val taskbarSize =
                 context.resources.getDimensionPixelSize(AndroidR.dimen.taskbar_frame_height)
             height -= taskbarSize
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 97c290d..f817439 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -37,7 +37,7 @@
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS;
 import static com.android.systemui.navigationbar.NavBarHelper.transitionMode;
 import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
-import static com.android.systemui.shared.recents.utilities.Utilities.isTablet;
+import static com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY;
@@ -1724,7 +1724,7 @@
 
     private void setNavigationIconHints(int hints) {
         if (hints == mNavigationIconHints) return;
-        if (!isTablet(mContext)) {
+        if (!isLargeScreen(mContext)) {
             // All IME functions handled by launcher via Sysui flags for large screen
             final boolean newBackAlt = (hints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
             final boolean oldBackAlt =
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 8c19111..153d5a6 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -21,7 +21,7 @@
 import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
 
 import static com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler.DEBUG_MISSING_GESTURE_TAG;
-import static com.android.systemui.shared.recents.utilities.Utilities.isTablet;
+import static com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen;
 
 import android.content.Context;
 import android.content.pm.ActivityInfo;
@@ -91,7 +91,7 @@
     private final DisplayManager mDisplayManager;
     private final TaskbarDelegate mTaskbarDelegate;
     private int mNavMode;
-    @VisibleForTesting boolean mIsTablet;
+    @VisibleForTesting boolean mIsLargeScreen;
 
     /** A displayId - nav bar maps. */
     @VisibleForTesting
@@ -138,16 +138,16 @@
                 navBarHelper, navigationModeController, sysUiFlagsContainer,
                 dumpManager, autoHideController, lightBarController, pipOptional,
                 backAnimation.orElse(null), taskStackChangeListeners);
-        mIsTablet = isTablet(mContext);
+        mIsLargeScreen = isLargeScreen(mContext);
         dumpManager.registerDumpable(this);
     }
 
     @Override
     public void onConfigChanged(Configuration newConfig) {
-        boolean isOldConfigTablet = mIsTablet;
-        mIsTablet = isTablet(mContext);
+        boolean isOldConfigLargeScreen = mIsLargeScreen;
+        mIsLargeScreen = isLargeScreen(mContext);
         boolean willApplyConfig = mConfigChanges.applyNewConfig(mContext.getResources());
-        boolean largeScreenChanged = mIsTablet != isOldConfigTablet;
+        boolean largeScreenChanged = mIsLargeScreen != isOldConfigLargeScreen;
         // TODO(b/243765256): Disable this logging once b/243765256 is fixed.
         Log.i(DEBUG_MISSING_GESTURE_TAG, "NavbarController: newConfig=" + newConfig
                 + " mTaskbarDelegate initialized=" + mTaskbarDelegate.isInitialized()
@@ -235,8 +235,9 @@
 
     /** @return {@code true} if taskbar is enabled, false otherwise */
     private boolean initializeTaskbarIfNecessary() {
-        // Enable for tablet or (phone AND flag is set); assuming phone = !mIsTablet
-        boolean taskbarEnabled = mIsTablet || mFeatureFlags.isEnabled(Flags.HIDE_NAVBAR_WINDOW);
+        // Enable for large screens or (phone AND flag is set); assuming phone = !mIsLargeScreen
+        boolean taskbarEnabled = mIsLargeScreen || mFeatureFlags.isEnabled(
+                Flags.HIDE_NAVBAR_WINDOW);
 
         if (taskbarEnabled) {
             Trace.beginSection("NavigationBarController#initializeTaskbarIfNecessary");
@@ -258,7 +259,7 @@
     @Override
     public void onDisplayReady(int displayId) {
         Display display = mDisplayManager.getDisplay(displayId);
-        mIsTablet = isTablet(mContext);
+        mIsLargeScreen = isLargeScreen(mContext);
         createNavigationBar(display, null /* savedState */, null /* result */);
     }
 
@@ -470,7 +471,7 @@
 
     @Override
     public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
-        pw.println("mIsTablet=" + mIsTablet);
+        pw.println("mIsLargeScreen=" + mIsLargeScreen);
         pw.println("mNavMode=" + mNavMode);
         for (int i = 0; i < mNavigationBars.size(); i++) {
             if (i > 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 645b125..346acf9 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.recents;
 
-import static com.android.systemui.shared.recents.utilities.Utilities.isTablet;
+import static com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen;
 import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE;
 import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE;
 import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE;
@@ -265,7 +265,7 @@
                     .setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE);
             View buttons = mLayout.findViewById(R.id.screen_pinning_buttons);
             if (!QuickStepContract.isGesturalMode(mNavBarMode)
-            	    && hasSoftNavigationBar(mContext.getDisplayId()) && !isTablet(mContext)) {
+            	    && hasSoftNavigationBar(mContext.getDisplayId()) && !isLargeScreen(mContext)) {
                 buttons.setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE);
                 swapChildrenIfRtlAndVertical(buttons);
             } else {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
index d912793..ed928702 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
@@ -18,8 +18,10 @@
 
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import android.view.View
 import android.view.inputmethod.InputMethodManager
 import android.widget.EditText
+import android.widget.ImageView
 import androidx.test.filters.SmallTest
 import com.android.internal.util.LatencyTracker
 import com.android.internal.widget.LockPatternUtils
@@ -30,6 +32,7 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
 import org.mockito.ArgumentMatchers.anyBoolean
 import org.mockito.ArgumentMatchers.anyString
 import org.mockito.Mock
@@ -37,6 +40,7 @@
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when`
+import org.mockito.Mockito.mock
 import org.mockito.MockitoAnnotations
 
 @SmallTest
@@ -76,7 +80,9 @@
     Mockito.`when`(keyguardPasswordView.findViewById<EditText>(R.id.passwordEntry))
         .thenReturn(passwordEntry)
     `when`(keyguardPasswordView.resources).thenReturn(context.resources)
-    keyguardPasswordViewController =
+    `when`(keyguardPasswordView.findViewById<ImageView>(R.id.switch_ime_button))
+        .thenReturn(mock(ImageView::class.java))
+     keyguardPasswordViewController =
         KeyguardPasswordViewController(
             keyguardPasswordView,
             keyguardUpdateMonitor,
@@ -113,6 +119,18 @@
   }
 
   @Test
+  fun onApplyWindowInsetsListener_onApplyWindowInsets() {
+      `when`(keyguardViewController.isBouncerShowing).thenReturn(false)
+      val argumentCaptor = ArgumentCaptor.forClass(View.OnApplyWindowInsetsListener::class.java)
+
+      keyguardPasswordViewController.onViewAttached()
+      verify(keyguardPasswordView).setOnApplyWindowInsetsListener(argumentCaptor.capture())
+      argumentCaptor.value.onApplyWindowInsets(keyguardPasswordView, null)
+
+      verify(keyguardPasswordView).hideKeyboard()
+  }
+
+  @Test
   fun testHideKeyboardWhenOnPause() {
     keyguardPasswordViewController.onPause()
     keyguardPasswordView.post {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
index e35b2a3..28e80057 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
@@ -39,11 +39,9 @@
 import com.android.systemui.settings.UserFileManager
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
-import java.io.File
-import java.util.Optional
-import java.util.function.Consumer
 import org.junit.After
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
@@ -58,7 +56,9 @@
 import org.mockito.Captor
 import org.mockito.Mock
 import org.mockito.Mockito
+import org.mockito.Mockito.`when`
 import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.clearInvocations
 import org.mockito.Mockito.inOrder
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.never
@@ -66,9 +66,10 @@
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyNoMoreInteractions
-import org.mockito.Mockito.`when`
-import org.mockito.Mockito.clearInvocations
 import org.mockito.MockitoAnnotations
+import java.io.File
+import java.util.*
+import java.util.function.Consumer
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
@@ -146,6 +147,7 @@
     fun setUp() {
         MockitoAnnotations.initMocks(this)
 
+        whenever(authorizedPanelsRepository.getAuthorizedPanels()).thenReturn(setOf())
         `when`(userTracker.userHandle).thenReturn(UserHandle.of(user))
 
         delayableExecutor = FakeExecutor(FakeSystemClock())
@@ -945,6 +947,28 @@
         controller.bindComponentForPanel(TEST_COMPONENT)
         verify(bindingController).bindServiceForPanel(TEST_COMPONENT)
     }
+
+    @Test
+    fun testRemoveFavoriteRemovesFavorite() {
+        val componentName = ComponentName(context, "test.Cls")
+        controller.addFavorite(
+                componentName,
+                "test structure",
+                ControlInfo(
+                        controlId = "testId",
+                        controlTitle = "Test Control",
+                        controlSubtitle = "test control subtitle",
+                        deviceType = DeviceTypes.TYPE_LIGHT,
+                ),
+        )
+
+        controller.removeFavorites(componentName)
+        delayableExecutor.runAllReady()
+
+        verify(authorizedPanelsRepository)
+                .removeAuthorizedPanels(eq(setOf(componentName.packageName)))
+        assertThat(controller.getFavorites()).isEmpty()
+    }
 }
 
 private class DidRunRunnable() : Runnable {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt
index b91a3fd..7ac1953 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt
@@ -115,6 +115,18 @@
         assertThat(sharedPrefs.getStringSet(KEY, null)).containsExactly(TEST_PACKAGE)
     }
 
+    @Test
+    fun testRemoveAuthorizedPackageRemovesIt() {
+        val sharedPrefs = FakeSharedPreferences()
+        val fileManager = FakeUserFileManager(mapOf(0 to sharedPrefs))
+        val repository = createRepository(fileManager)
+        repository.addAuthorizedPanels(setOf(TEST_PACKAGE))
+
+        repository.removeAuthorizedPanels(setOf(TEST_PACKAGE))
+
+        assertThat(sharedPrefs.getStringSet(KEY, null)).isEmpty()
+    }
+
     private fun createRepository(userFileManager: UserFileManager): AuthorizedPanelsRepositoryImpl {
         return AuthorizedPanelsRepositoryImpl(mContext, userFileManager, userTracker)
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsDialogsFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsDialogsFactoryTest.kt
new file mode 100644
index 0000000..1e8cd41
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsDialogsFactoryTest.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2023 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.controls.ui
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.FakeSystemUIDialogController
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.verify
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class ControlsDialogsFactoryTest : SysuiTestCase() {
+
+    private companion object {
+        const val APP_NAME = "Test App"
+    }
+
+    private val fakeDialogController = FakeSystemUIDialogController()
+
+    private lateinit var underTest: ControlsDialogsFactory
+
+    @Before
+    fun setup() {
+        underTest = ControlsDialogsFactory { fakeDialogController.dialog }
+    }
+
+    @Test
+    fun testCreatesRemoveAppDialog() {
+        val dialog = underTest.createRemoveAppDialog(context, APP_NAME) {}
+
+        verify(dialog)
+            .setTitle(
+                eq(context.getString(R.string.controls_panel_remove_app_authorization, APP_NAME))
+            )
+        verify(dialog).setCanceledOnTouchOutside(eq(true))
+    }
+
+    @Test
+    fun testPositiveClickRemoveAppDialogWorks() {
+        var dialogResult: Boolean? = null
+        underTest.createRemoveAppDialog(context, APP_NAME) { dialogResult = it }
+
+        fakeDialogController.clickPositive()
+
+        assertThat(dialogResult).isTrue()
+    }
+
+    @Test
+    fun testNeutralClickRemoveAppDialogWorks() {
+        var dialogResult: Boolean? = null
+        underTest.createRemoveAppDialog(context, APP_NAME) { dialogResult = it }
+
+        fakeDialogController.clickNeutral()
+
+        assertThat(dialogResult).isFalse()
+    }
+
+    @Test
+    fun testCancelRemoveAppDialogWorks() {
+        var dialogResult: Boolean? = null
+        underTest.createRemoveAppDialog(context, APP_NAME) { dialogResult = it }
+
+        fakeDialogController.cancel()
+
+        assertThat(dialogResult).isFalse()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
index aa90e2a..23faa99 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
@@ -52,6 +52,7 @@
 import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.util.FakeSharedPreferences
+import com.android.systemui.util.FakeSystemUIDialogController
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
@@ -63,21 +64,20 @@
 import com.android.wm.shell.TaskView
 import com.android.wm.shell.TaskViewFactory
 import com.google.common.truth.Truth.assertThat
-import dagger.Lazy
-import java.util.Optional
-import java.util.function.Consumer
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
+import org.mockito.Mockito.`when`
 import org.mockito.Mockito.anyInt
 import org.mockito.Mockito.anyString
 import org.mockito.Mockito.clearInvocations
 import org.mockito.Mockito.never
 import org.mockito.Mockito.spy
 import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
+import java.util.Optional
+import java.util.function.Consumer
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
@@ -98,13 +98,15 @@
     @Mock lateinit var authorizedPanelsRepository: AuthorizedPanelsRepository
     @Mock lateinit var featureFlags: FeatureFlags
     @Mock lateinit var packageManager: PackageManager
-    val sharedPreferences = FakeSharedPreferences()
-    lateinit var controlsSettingsRepository: FakeControlsSettingsRepository
 
-    var uiExecutor = FakeExecutor(FakeSystemClock())
-    var bgExecutor = FakeExecutor(FakeSystemClock())
-    lateinit var underTest: ControlsUiControllerImpl
-    lateinit var parent: FrameLayout
+    private val sharedPreferences = FakeSharedPreferences()
+    private val fakeDialogController = FakeSystemUIDialogController()
+    private val uiExecutor = FakeExecutor(FakeSystemClock())
+    private val bgExecutor = FakeExecutor(FakeSystemClock())
+
+    private lateinit var controlsSettingsRepository: FakeControlsSettingsRepository
+    private lateinit var parent: FrameLayout
+    private lateinit var underTest: ControlsUiControllerImpl
 
     @Before
     fun setup() {
@@ -125,12 +127,12 @@
 
         underTest =
             ControlsUiControllerImpl(
-                Lazy { controlsController },
+                { controlsController },
                 context,
                 packageManager,
                 uiExecutor,
                 bgExecutor,
-                Lazy { controlsListingController },
+                { controlsListingController },
                 controlActionCoordinator,
                 activityStarter,
                 iconCache,
@@ -142,7 +144,8 @@
                 controlsSettingsRepository,
                 authorizedPanelsRepository,
                 featureFlags,
-                dumpManager
+                ControlsDialogsFactory { fakeDialogController.dialog },
+                dumpManager,
             )
         `when`(
                 userFileManager.getSharedPreferences(
@@ -410,8 +413,45 @@
         verify(controlsListingController, never()).removeCallback(any())
     }
 
+    @Test
+    fun testRemovingAppsRemovesFavorite() {
+        val componentName = ComponentName(context, "cls")
+        whenever(controlsController.removeFavorites(eq(componentName))).thenReturn(true)
+        val panel = SelectedItem.PanelItem("App name", componentName)
+        sharedPreferences
+            .edit()
+            .putString("controls_component", panel.componentName.flattenToString())
+            .putString("controls_structure", panel.appName.toString())
+            .putBoolean("controls_is_panel", true)
+            .commit()
+        underTest.show(parent, {}, context)
+        underTest.startRemovingApp(componentName, "Test App")
+
+        fakeDialogController.clickPositive()
+
+        verify(controlsController).removeFavorites(eq(componentName))
+        assertThat(underTest.getPreferredSelectedItem(emptyList()))
+            .isEqualTo(SelectedItem.EMPTY_SELECTION)
+        with(sharedPreferences) {
+            assertThat(contains("controls_component")).isFalse()
+            assertThat(contains("controls_structure")).isFalse()
+            assertThat(contains("controls_is_panel")).isFalse()
+        }
+    }
+
+    @Test
+    fun testHideCancelsTheRemoveAppDialog() {
+        val componentName = ComponentName(context, "cls")
+        underTest.show(parent, {}, context)
+        underTest.startRemovingApp(componentName, "Test App")
+
+        underTest.hide(parent)
+
+        verify(fakeDialogController.dialog).cancel()
+    }
+
     private fun setUpPanel(panel: SelectedItem.PanelItem): ControlsServiceInfo {
-        val activity = ComponentName("pkg", "activity")
+        val activity = ComponentName(context, "activity")
         sharedPreferences
             .edit()
             .putString("controls_component", panel.componentName.flattenToString())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
index aacbf8f..0a91a0e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
@@ -143,8 +143,8 @@
 
     @Test
     public void testCreateNavigationBarsIncludeDefaultTrue() {
-        // Tablets may be using taskbar and the logic is different
-        mNavigationBarController.mIsTablet = false;
+        // Large screens may be using taskbar and the logic is different
+        mNavigationBarController.mIsLargeScreen = false;
         doNothing().when(mNavigationBarController).createNavigationBar(any(), any(), any());
 
         mNavigationBarController.createNavigationBars(true, null);
@@ -292,7 +292,7 @@
     @Test
     public void testConfigurationChange_taskbarNotInitialized() {
         Configuration configuration = mContext.getResources().getConfiguration();
-        when(Utilities.isTablet(any())).thenReturn(true);
+        when(Utilities.isLargeScreen(any())).thenReturn(true);
         mNavigationBarController.onConfigChanged(configuration);
         verify(mTaskbarDelegate, never()).onConfigurationChanged(configuration);
     }
@@ -300,7 +300,7 @@
     @Test
     public void testConfigurationChange_taskbarInitialized() {
         Configuration configuration = mContext.getResources().getConfiguration();
-        when(Utilities.isTablet(any())).thenReturn(true);
+        when(Utilities.isLargeScreen(any())).thenReturn(true);
         when(mTaskbarDelegate.isInitialized()).thenReturn(true);
         mNavigationBarController.onConfigChanged(configuration);
         verify(mTaskbarDelegate, times(1)).onConfigurationChanged(configuration);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/FakeSystemUIDialogController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/FakeSystemUIDialogController.kt
new file mode 100644
index 0000000..0c9ce0f
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/FakeSystemUIDialogController.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2023 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.util
+
+import android.content.DialogInterface
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import org.mockito.ArgumentCaptor
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.verify
+import org.mockito.stubbing.Stubber
+
+class FakeSystemUIDialogController {
+
+    val dialog: SystemUIDialog = mock()
+
+    private val clickListeners: MutableMap<Int, DialogInterface.OnClickListener> = mutableMapOf()
+
+    init {
+        saveListener(DialogInterface.BUTTON_POSITIVE)
+            .whenever(dialog)
+            .setPositiveButton(any(), any())
+        saveListener(DialogInterface.BUTTON_POSITIVE)
+            .whenever(dialog)
+            .setPositiveButton(any(), any(), any())
+
+        saveListener(DialogInterface.BUTTON_NEGATIVE)
+            .whenever(dialog)
+            .setNegativeButton(any(), any())
+        saveListener(DialogInterface.BUTTON_NEGATIVE)
+            .whenever(dialog)
+            .setNegativeButton(any(), any(), any())
+
+        saveListener(DialogInterface.BUTTON_NEUTRAL).whenever(dialog).setNeutralButton(any(), any())
+        saveListener(DialogInterface.BUTTON_NEUTRAL)
+            .whenever(dialog)
+            .setNeutralButton(any(), any(), any())
+    }
+
+    fun clickNegative() {
+        performClick(DialogInterface.BUTTON_NEGATIVE, "This dialog has no negative button")
+    }
+
+    fun clickPositive() {
+        performClick(DialogInterface.BUTTON_POSITIVE, "This dialog has no positive button")
+    }
+
+    fun clickNeutral() {
+        performClick(DialogInterface.BUTTON_NEUTRAL, "This dialog has no neutral button")
+    }
+
+    fun cancel() {
+        val captor = ArgumentCaptor.forClass(DialogInterface.OnCancelListener::class.java)
+        verify(dialog).setOnCancelListener(captor.capture())
+        captor.value.onCancel(dialog)
+    }
+
+    private fun performClick(which: Int, errorMessage: String) {
+        clickListeners
+            .getOrElse(which) { throw IllegalAccessException(errorMessage) }
+            .onClick(dialog, which)
+    }
+
+    private fun saveListener(which: Int): Stubber = doAnswer {
+        val listener = it.getArgument<DialogInterface.OnClickListener>(1)
+        clickListeners[which] = listener
+        Unit
+    }
+}