Merge "Sharesheet - Preview text RTL fix" into rvc-dev
diff --git a/core/java/android/view/ImeFocusController.java b/core/java/android/view/ImeFocusController.java
index a480072..825077ff 100644
--- a/core/java/android/view/ImeFocusController.java
+++ b/core/java/android/view/ImeFocusController.java
@@ -125,6 +125,13 @@
         final View viewForWindowFocus = focusedView != null ? focusedView : mViewRootImpl.mView;
         onViewFocusChanged(viewForWindowFocus, true);
 
+        // Skip starting input when the next focused view is same as served view and the served
+        // input connection still exists.
+        final boolean nextFocusIsServedView = mServedView != null && mServedView == focusedView;
+        if (nextFocusIsServedView && immDelegate.isAcceptingText()) {
+            forceFocus = false;
+        }
+
         immDelegate.startInputAsyncOnWindowFocusGain(viewForWindowFocus,
                 windowAttribute.softInputMode, windowAttribute.flags, forceFocus);
     }
@@ -247,6 +254,7 @@
         void setCurrentRootView(ViewRootImpl rootView);
         boolean isCurrentRootView(ViewRootImpl rootView);
         boolean isRestartOnNextWindowFocus(boolean reset);
+        boolean isAcceptingText();
     }
 
     public View getServedView() {
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 71dd665..477dd1d 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -616,12 +616,19 @@
                 // For some reason we didn't do a startInput + windowFocusGain, so
                 // we'll just do a window focus gain and call it a day.
                 try {
-                    if (DEBUG) Log.v(TAG, "Reporting focus gain, without startInput");
+                    View servedView = controller.getServedView();
+                    boolean nextFocusIsServedView = servedView != null && servedView == focusedView;
+                    if (DEBUG) {
+                        Log.v(TAG, "Reporting focus gain, without startInput"
+                                + ", nextFocusIsServedView=" + nextFocusIsServedView);
+                    }
                     mService.startInputOrWindowGainedFocus(
                             StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY, mClient,
                             focusedView.getWindowToken(), startInputFlags, softInputMode,
                             windowFlags,
-                            null, null, 0 /* missingMethodFlags */,
+                            nextFocusIsServedView ? mCurrentTextBoxAttribute : null,
+                            nextFocusIsServedView ? mServedInputConnectionWrapper : null,
+                            0 /* missingMethodFlags */,
                             mCurRootView.mContext.getApplicationInfo().targetSdkVersion);
                 } catch (RemoteException e) {
                     throw e.rethrowFromSystemServer();
@@ -646,8 +653,7 @@
         public void setCurrentRootView(ViewRootImpl rootView) {
             synchronized (mH) {
                 if (mCurRootView != null) {
-                    // Reset the last served view and restart window focus state of the root view.
-                    mCurRootView.getImeFocusController().setServedView(null);
+                    // Restart the input when the next window focus state of the root view changed.
                     mRestartOnNextWindowFocus = true;
                 }
                 mCurRootView = rootView;
@@ -677,6 +683,18 @@
             }
             return result;
         }
+
+        /**
+         * For {@link ImeFocusController} to check if the currently served view is accepting full
+         * text edits.
+         */
+        @Override
+        public boolean isAcceptingText() {
+            synchronized (mH) {
+                return mServedInputConnectionWrapper != null
+                        && mServedInputConnectionWrapper.getInputConnection() != null;
+            }
+        }
     }
 
     /** @hide */
diff --git a/core/java/com/android/internal/inputmethod/StartInputReason.java b/core/java/com/android/internal/inputmethod/StartInputReason.java
index a01c459..a4eaa21 100644
--- a/core/java/com/android/internal/inputmethod/StartInputReason.java
+++ b/core/java/com/android/internal/inputmethod/StartInputReason.java
@@ -50,8 +50,9 @@
     int WINDOW_FOCUS_GAIN = 1;
     /**
      * {@link android.view.Window} gained focus but there is no {@link android.view.View} that is
-     * eligible to have IME focus. {@link android.view.inputmethod.InputMethodManager} just reports
-     * this window focus change event.
+     * eligible to have IME focus, or the focused view is same as current served view and its
+     * input connection remains. {@link android.view.inputmethod.InputMethodManager} just reports
+     * this window focus change event to sync IME input target for system.
      */
     int WINDOW_FOCUS_GAIN_REPORT_ONLY = 2;
     /**
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index 1f2ae5f..b7cdead 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -21,6 +21,10 @@
 import static com.android.internal.widget.MessagingPropertyAnimator.ALPHA_IN;
 import static com.android.internal.widget.MessagingPropertyAnimator.ALPHA_OUT;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ValueAnimator;
 import android.annotation.AttrRes;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -36,6 +40,7 @@
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.Typeface;
+import android.graphics.drawable.GradientDrawable;
 import android.graphics.drawable.Icon;
 import android.os.Bundle;
 import android.os.Parcelable;
@@ -93,8 +98,12 @@
     public static final Interpolator LINEAR_OUT_SLOW_IN = new PathInterpolator(0f, 0f, 0.2f, 1f);
     public static final Interpolator FAST_OUT_LINEAR_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
     public static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
+    public static final Interpolator OVERSHOOT = new PathInterpolator(0.4f, 0f, 0.2f, 1.4f);
     public static final OnLayoutChangeListener MESSAGING_PROPERTY_ANIMATOR
             = new MessagingPropertyAnimator();
+    public static final int IMPORTANCE_ANIM_GROW_DURATION = 250;
+    public static final int IMPORTANCE_ANIM_SHRINK_DURATION = 200;
+    public static final int IMPORTANCE_ANIM_SHRINK_DELAY = 25;
     private List<MessagingMessage> mMessages = new ArrayList<>();
     private List<MessagingMessage> mHistoricMessages = new ArrayList<>();
     private MessagingLinearLayout mMessagingLinearLayout;
@@ -331,14 +340,74 @@
         mNameReplacement = nameReplacement;
     }
 
-    /**
-     * Sets this conversation as "important", adding some additional UI treatment.
-     */
+    /** Sets this conversation as "important", adding some additional UI treatment. */
     @RemotableViewMethod
     public void setIsImportantConversation(boolean isImportantConversation) {
+        setIsImportantConversation(isImportantConversation, false);
+    }
+
+    /** @hide **/
+    public void setIsImportantConversation(boolean isImportantConversation, boolean animate) {
         mImportantConversation = isImportantConversation;
-        mImportanceRingView.setVisibility(isImportantConversation
-                && mIcon.getVisibility() != GONE ? VISIBLE : GONE);
+        mImportanceRingView.setVisibility(isImportantConversation && mIcon.getVisibility() != GONE
+                ? VISIBLE : GONE);
+
+        if (animate && isImportantConversation) {
+            GradientDrawable ring = (GradientDrawable) mImportanceRingView.getDrawable();
+            ring.mutate();
+            GradientDrawable bg = (GradientDrawable) mConversationIconBadgeBg.getDrawable();
+            bg.mutate();
+            int ringColor = getResources()
+                    .getColor(R.color.conversation_important_highlight);
+            int standardThickness = getResources()
+                    .getDimensionPixelSize(R.dimen.importance_ring_stroke_width);
+            int largeThickness = getResources()
+                    .getDimensionPixelSize(R.dimen.importance_ring_anim_max_stroke_width);
+            int standardSize = getResources().getDimensionPixelSize(
+                    R.dimen.importance_ring_size);
+            int baseSize = standardSize - standardThickness * 2;
+            int bgSize = getResources()
+                    .getDimensionPixelSize(R.dimen.conversation_icon_size_badged);
+
+            ValueAnimator.AnimatorUpdateListener animatorUpdateListener = animation -> {
+                int strokeWidth = Math.round((float) animation.getAnimatedValue());
+                ring.setStroke(strokeWidth, ringColor);
+                int newSize = baseSize + strokeWidth * 2;
+                ring.setSize(newSize, newSize);
+                mImportanceRingView.invalidate();
+            };
+
+            ValueAnimator growAnimation = ValueAnimator.ofFloat(0, largeThickness);
+            growAnimation.setInterpolator(LINEAR_OUT_SLOW_IN);
+            growAnimation.setDuration(IMPORTANCE_ANIM_GROW_DURATION);
+            growAnimation.addUpdateListener(animatorUpdateListener);
+
+            ValueAnimator shrinkAnimation =
+                    ValueAnimator.ofFloat(largeThickness, standardThickness);
+            shrinkAnimation.setDuration(IMPORTANCE_ANIM_SHRINK_DURATION);
+            shrinkAnimation.setStartDelay(IMPORTANCE_ANIM_SHRINK_DELAY);
+            shrinkAnimation.setInterpolator(OVERSHOOT);
+            shrinkAnimation.addUpdateListener(animatorUpdateListener);
+            shrinkAnimation.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationStart(Animator animation) {
+                    // Shrink the badge bg so that it doesn't peek behind the animation
+                    bg.setSize(baseSize, baseSize);
+                    mConversationIconBadgeBg.invalidate();
+                }
+
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    // Reset bg back to normal size
+                    bg.setSize(bgSize, bgSize);
+                    mConversationIconBadgeBg.invalidate();
+                }
+            });
+
+            AnimatorSet anims = new AnimatorSet();
+            anims.playSequentially(growAnimation, shrinkAnimation);
+            anims.start();
+        }
     }
 
     public boolean isImportantConversation() {
diff --git a/core/res/res/drawable/conversation_badge_background.xml b/core/res/res/drawable/conversation_badge_background.xml
index 0dd0dcd..9e6405d 100644
--- a/core/res/res/drawable/conversation_badge_background.xml
+++ b/core/res/res/drawable/conversation_badge_background.xml
@@ -22,7 +22,7 @@
         android:color="#ffffff"/>
 
     <size
-        android:width="26dp"
-        android:height="26dp"/>
+        android:width="20dp"
+        android:height="20dp"/>
 </shape>
 
diff --git a/core/res/res/drawable/conversation_badge_ring.xml b/core/res/res/drawable/conversation_badge_ring.xml
index 11ba8ad..eee53d1 100644
--- a/core/res/res/drawable/conversation_badge_ring.xml
+++ b/core/res/res/drawable/conversation_badge_ring.xml
@@ -16,17 +16,18 @@
   -->
 
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="oval">
-
-    <solid
-        android:color="@color/transparent"/>
+    android:shape="oval"
+>
+    <solid android:color="@color/transparent" />
 
     <stroke
         android:color="@color/conversation_important_highlight"
-        android:width="2dp"/>
+        android:width="@dimen/importance_ring_stroke_width"
+    />
 
     <size
-        android:width="26dp"
-        android:height="26dp"/>
+        android:width="@dimen/importance_ring_size"
+        android:height="@dimen/importance_ring_size"
+    />
 </shape>
 
diff --git a/core/res/res/layout/notification_template_material_conversation.xml b/core/res/res/layout/notification_template_material_conversation.xml
index 9a9d8b9..0411f55 100644
--- a/core/res/res/layout/notification_template_material_conversation.xml
+++ b/core/res/res/layout/notification_template_material_conversation.xml
@@ -38,6 +38,8 @@
         <FrameLayout
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:clipChildren="false"
+            android:clipToPadding="false"
             android:layout_gravity="top|center_horizontal"
         >
 
@@ -63,13 +65,17 @@
                 android:layout_height="@dimen/conversation_icon_size_badged"
                 android:layout_marginLeft="@dimen/conversation_badge_side_margin"
                 android:layout_marginTop="@dimen/conversation_badge_side_margin"
+                android:clipChildren="false"
+                android:clipToPadding="false"
             >
                 <com.android.internal.widget.CachingIconView
                     android:id="@+id/conversation_icon_badge_bg"
                     android:layout_width="match_parent"
                     android:layout_height="match_parent"
+                    android:layout_gravity="center"
                     android:src="@drawable/conversation_badge_background"
                     android:forceHasOverlappingRendering="false"
+                    android:scaleType="center"
                 />
                 <com.android.internal.widget.CachingIconView
                     android:id="@+id/icon"
@@ -81,11 +87,14 @@
                 />
                 <com.android.internal.widget.CachingIconView
                     android:id="@+id/conversation_icon_badge_ring"
-                    android:layout_width="match_parent"
-                    android:layout_height="match_parent"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="center"
                     android:src="@drawable/conversation_badge_ring"
                     android:visibility="gone"
                     android:forceHasOverlappingRendering="false"
+                    android:clipToPadding="false"
+                    android:scaleType="center"
                 />
             </FrameLayout>
         </FrameLayout>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 59bb052..a771904 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -720,6 +720,12 @@
     <dimen name="conversation_face_pile_protection_width_expanded">1dp</dimen>
     <!-- The padding of the expanded message container-->
     <dimen name="expanded_group_conversation_message_padding">14dp</dimen>
+    <!-- The stroke width of the ring used to visually mark a conversation as important -->
+    <dimen name="importance_ring_stroke_width">2dp</dimen>
+    <!-- The maximum stroke width used for the animation shown when a conversation is marked as important -->
+    <dimen name="importance_ring_anim_max_stroke_width">10dp</dimen>
+    <!-- The size of the importance ring -->
+    <dimen name="importance_ring_size">20dp</dimen>
 
     <!-- The top padding of the conversation icon container in the regular state-->
     <dimen name="conversation_icon_container_top_padding">9dp</dimen>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index ab4005b..23ae1e7 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3903,6 +3903,12 @@
   <java-symbol type="array" name="config_defaultImperceptibleKillingExemptionPkgs" />
   <java-symbol type="array" name="config_defaultImperceptibleKillingExemptionProcStates" />
 
+  <java-symbol type="color" name="conversation_important_highlight" />
+  <java-symbol type="dimen" name="importance_ring_stroke_width" />
+  <java-symbol type="dimen" name="importance_ring_anim_max_stroke_width" />
+  <java-symbol type="dimen" name="importance_ring_size" />
+  <java-symbol type="dimen" name="conversation_icon_size_badged" />
+
   <java-symbol type="id" name="header_icon_container" />
   <java-symbol type="attr" name="notificationHeaderTextAppearance" />
   <java-symbol type="string" name="conversation_single_line_name_display" />
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index 1d4cfdc..a89cf37 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -60,6 +60,7 @@
 import com.android.internal.R;
 import com.android.internal.util.ArrayUtils;
 import com.android.settingslib.Utils;
+import com.android.settingslib.utils.ThreadUtils;
 
 import java.io.File;
 import java.io.IOException;
@@ -1588,6 +1589,15 @@
             this.size = SIZE_UNKNOWN;
             this.sizeStale = true;
             ensureLabel(context);
+            // Speed up the cache of the icon and label description if they haven't been created.
+            ThreadUtils.postOnBackgroundThread(() -> {
+                if (this.icon == null) {
+                    this.ensureIconLocked(context);
+                }
+                if (this.labelDescription == null) {
+                    this.ensureLabelDescriptionLocked(context);
+                }
+            });
         }
 
         public void ensureLabel(Context context) {
diff --git a/packages/SystemUI/res/layout/controls_base_item.xml b/packages/SystemUI/res/layout/controls_base_item.xml
index 477a70f..5f83f45 100644
--- a/packages/SystemUI/res/layout/controls_base_item.xml
+++ b/packages/SystemUI/res/layout/controls_base_item.xml
@@ -52,6 +52,7 @@
         android:singleLine="true"
         android:ellipsize="marquee"
         android:marqueeRepeatLimit = "marquee_forever"
+        android:textDirection="locale"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintBottom_toBottomOf="@+id/icon"
         app:layout_constraintStart_toEndOf="@+id/icon" />
@@ -67,6 +68,7 @@
         android:focusable="false"
         android:maxLines="1"
         android:ellipsize="end"
+        android:textDirection="locale"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintBottom_toTopOf="@id/barrier"/>
@@ -90,6 +92,7 @@
         android:focusable="false"
         android:maxLines="1"
         android:ellipsize="end"
+        android:textDirection="locale"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintEnd_toStartOf="@id/favorite"
         app:layout_constraintTop_toTopOf="@id/favorite"
diff --git a/packages/SystemUI/res/layout/controls_more_item.xml b/packages/SystemUI/res/layout/controls_more_item.xml
index df03787..da9c43c 100644
--- a/packages/SystemUI/res/layout/controls_more_item.xml
+++ b/packages/SystemUI/res/layout/controls_more_item.xml
@@ -20,5 +20,6 @@
     android:layout_height="wrap_content"
     android:layout_gravity="start"
     android:paddingStart="@dimen/control_menu_horizontal_padding"
-    android:paddingEnd="@dimen/control_menu_horizontal_padding"/>
+    android:paddingEnd="@dimen/control_menu_horizontal_padding"
+    android:textDirection="locale"/>
 
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 0731920..45ba1e6 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
@@ -190,11 +190,6 @@
     fun countFavoritesForComponent(componentName: ComponentName): Int
 
     /**
-     * TEMPORARY for testing
-     */
-    fun resetFavorites()
-
-    /**
      * Interface for structure to pass data to [ControlsFavoritingActivity].
      */
     interface LoadData {
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 93f0c7f..ebdcdcc 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -524,13 +524,6 @@
         }
     }
 
-    override fun resetFavorites() {
-        executor.execute {
-            Favorites.clear()
-            persistenceWrapper.storeFavorites(Favorites.getAllStructures())
-        }
-    }
-
     override fun refreshStatus(componentName: ComponentName, control: Control) {
         if (!confirmAvailability()) {
             Log.d(TAG, "Controls not available")
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 d31b6eb..0f5aef7 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -19,16 +19,13 @@
 import android.animation.Animator
 import android.animation.AnimatorListenerAdapter
 import android.animation.ObjectAnimator
-import android.app.AlertDialog
 import android.content.ComponentName
 import android.content.Context
-import android.content.DialogInterface
 import android.content.Intent
 import android.content.SharedPreferences
 import android.content.res.Configuration
 import android.graphics.drawable.Drawable
 import android.graphics.drawable.LayerDrawable
-import android.os.Process
 import android.service.controls.Control
 import android.util.Log
 import android.util.TypedValue
@@ -36,7 +33,6 @@
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
-import android.view.WindowManager
 import android.view.animation.AccelerateInterpolator
 import android.view.animation.DecelerateInterpolator
 import android.widget.AdapterView
@@ -272,8 +268,7 @@
     private fun createMenu() {
         val items = arrayOf(
             context.resources.getString(R.string.controls_menu_add),
-            context.resources.getString(R.string.controls_menu_edit),
-            "Reset"
+            context.resources.getString(R.string.controls_menu_edit)
         )
         var adapter = ArrayAdapter<String>(context, R.layout.controls_more_item, items)
 
@@ -298,10 +293,6 @@
                                 0 -> startFavoritingActivity(view.context, selectedStructure)
                                 // 1: Edit controls
                                 1 -> startEditingActivity(view.context, selectedStructure)
-                                // 2: TEMPORARY for reset controls
-                                2 -> showResetConfirmation()
-                                else -> Log.w(ControlsUiController.TAG,
-                                    "Unsupported index ($pos) on 'more' menu selection")
                             }
                             dismiss()
                         }
@@ -312,39 +303,6 @@
         })
     }
 
-    private fun showResetConfirmation() {
-        val builder = AlertDialog.Builder(
-            context,
-            android.R.style.Theme_DeviceDefault_Dialog_Alert
-        ).apply {
-            setMessage("For testing purposes: Would you like to " +
-                "reset your favorited device controls?")
-            setPositiveButton(
-                android.R.string.ok,
-                DialogInterface.OnClickListener { dialog, _ ->
-                    val userHandle = Process.myUserHandle()
-                    val userContext = context.createContextAsUser(userHandle, 0)
-                    val prefs = userContext.getSharedPreferences(
-                        "controls_prefs", Context.MODE_PRIVATE)
-                    prefs.edit().remove("SeedingCompleted").apply()
-                    controlsController.get().resetFavorites()
-                    dialog.dismiss()
-                    context.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
-            })
-            setNegativeButton(
-                android.R.string.cancel,
-                DialogInterface.OnClickListener {
-                    dialog, _ -> dialog.cancel()
-                }
-            )
-        }
-        builder.create().apply {
-            getWindow().apply {
-                setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY)
-            }
-        }.show()
-    }
-
     private fun createDropDown(items: List<SelectionItem>) {
         items.forEach {
             RenderInfo.registerComponentIcon(it.componentName, it.icon)
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPopupMenu.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPopupMenu.java
index 6b71f1e..1dbbb4d 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPopupMenu.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPopupMenu.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.Resources;
+import android.util.LayoutDirection;
 import android.view.View;
 import android.view.View.MeasureSpec;
 import android.view.WindowManager;
@@ -106,7 +107,11 @@
             listView.setPadding(0, mMenuVerticalPadding, 0, mMenuVerticalPadding);
 
             setWidth(width);
-            setHorizontalOffset(getAnchorView().getWidth() - mGlobalActionsSidePadding - width);
+            if (getAnchorView().getLayoutDirection() == LayoutDirection.LTR) {
+                setHorizontalOffset(getAnchorView().getWidth() - mGlobalActionsSidePadding - width);
+            } else {
+                setHorizontalOffset(mGlobalActionsSidePadding);
+            }
         }
 
         super.show();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
index fc6c2be..1972b86 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
@@ -19,13 +19,16 @@
 import android.app.Notification
 import android.content.Context
 import android.content.pm.LauncherApps
+import android.os.Handler
 import android.service.notification.NotificationListenerService.Ranking
 import android.service.notification.NotificationListenerService.RankingMap
 import com.android.internal.statusbar.NotificationVisibility
 import com.android.internal.widget.ConversationLayout
+import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.row.NotificationContentView
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator
 import com.android.systemui.statusbar.phone.NotificationGroupManager
 import java.util.concurrent.ConcurrentHashMap
 import javax.inject.Inject
@@ -62,7 +65,8 @@
 class ConversationNotificationManager @Inject constructor(
     private val notificationEntryManager: NotificationEntryManager,
     private val notificationGroupManager: NotificationGroupManager,
-    private val context: Context
+    private val context: Context,
+    @Main private val mainHandler: Handler
 ) {
     // Need this state to be thread safe, since it's accessed from the ui thread
     // (NotificationEntryListener) and a bg thread (NotificationContentInflater)
@@ -72,32 +76,41 @@
 
     init {
         notificationEntryManager.addNotificationEntryListener(object : NotificationEntryListener {
-
             override fun onNotificationRankingUpdated(rankingMap: RankingMap) {
                 fun getLayouts(view: NotificationContentView) =
                         sequenceOf(view.contractedChild, view.expandedChild, view.headsUpChild)
                 val ranking = Ranking()
-                states.keys.asSequence()
+                val activeConversationEntries = states.keys.asSequence()
                         .mapNotNull { notificationEntryManager.getActiveNotificationUnfiltered(it) }
-                        .forEach { entry ->
-                            if (rankingMap.getRanking(entry.sbn.key, ranking) &&
-                                    ranking.isConversation) {
-                                val important = ranking.channel.isImportantConversation
-                                var changed = false
-                                entry.row?.layouts?.asSequence()
-                                        ?.flatMap(::getLayouts)
-                                        ?.mapNotNull { it as? ConversationLayout }
-                                        ?.forEach {
-                                            if (important != it.isImportantConversation) {
-                                                it.setIsImportantConversation(important)
-                                                changed = true
-                                            }
-                                        }
-                                if (changed) {
-                                    notificationGroupManager.updateIsolation(entry)
-                                }
+                for (entry in activeConversationEntries) {
+                    if (rankingMap.getRanking(entry.sbn.key, ranking) && ranking.isConversation) {
+                        val important = ranking.channel.isImportantConversation
+                        val layouts = entry.row?.layouts?.asSequence()
+                                ?.flatMap(::getLayouts)
+                                ?.mapNotNull { it as? ConversationLayout }
+                                ?: emptySequence()
+                        var changed = false
+                        for (layout in layouts) {
+                            if (important == layout.isImportantConversation) {
+                                continue
+                            }
+                            changed = true
+                            if (important && entry.isMarkedForUserTriggeredMovement) {
+                                // delay this so that it doesn't animate in until after
+                                // the notif has been moved in the shade
+                                mainHandler.postDelayed({
+                                    layout.setIsImportantConversation(
+                                            important, true /* animate */)
+                                }, IMPORTANCE_ANIMATION_DELAY.toLong())
+                            } else {
+                                layout.setIsImportantConversation(important)
                             }
                         }
+                        if (changed) {
+                            notificationGroupManager.updateIsolation(entry)
+                        }
+                    }
+                }
             }
 
             override fun onEntryInflated(entry: NotificationEntry) {
@@ -177,9 +190,16 @@
 
     private fun resetBadgeUi(row: ExpandableNotificationRow): Unit =
             (row.layouts?.asSequence() ?: emptySequence())
-                    .flatMap { layout -> layout.allViews.asSequence()}
+                    .flatMap { layout -> layout.allViews.asSequence() }
                     .mapNotNull { view -> view as? ConversationLayout }
                     .forEach { convoLayout -> convoLayout.setUnreadCount(0) }
 
     private data class ConversationState(val unreadCount: Int, val notification: Notification)
+
+    companion object {
+        private const val IMPORTANCE_ANIMATION_DELAY =
+                StackStateAnimator.ANIMATION_DURATION_STANDARD +
+                StackStateAnimator.ANIMATION_DURATION_PRIORITY_CHANGE +
+                100
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/AppOpsInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/AppOpsInfo.java
index 87612f1..e445c9d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/AppOpsInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/AppOpsInfo.java
@@ -188,7 +188,9 @@
 
     @Override
     public boolean handleCloseControls(boolean save, boolean force) {
-        mMetricsLogger.visibility(MetricsEvent.APP_OPS_GUTS, false);
+        if (mMetricsLogger != null) {
+            mMetricsLogger.visibility(MetricsEvent.APP_OPS_GUTS, false);
+        }
         return false;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index e9d8958..f4afb91 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -53,7 +53,6 @@
 import android.transition.TransitionSet;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.util.Slog;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.accessibility.AccessibilityEvent;
@@ -508,10 +507,10 @@
         mBgHandler.post(
                 new UpdateChannelRunnable(mINotificationManager, mPackageName,
                         mAppUid, mSelectedAction, mNotificationChannel));
-        mMainHandler.postDelayed(() -> {
-            mEntry.markForUserTriggeredMovement(true);
-            mVisualStabilityManager.temporarilyAllowReordering();
-        }, StackStateAnimator.ANIMATION_DURATION_STANDARD);
+        mEntry.markForUserTriggeredMovement(true);
+        mMainHandler.postDelayed(
+                mVisualStabilityManager::temporarilyAllowReordering,
+                StackStateAnimator.ANIMATION_DURATION_STANDARD);
     }
 
     private boolean shouldShowPriorityOnboarding() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index 4c9cb20..c747a7c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -262,7 +262,10 @@
         stack.push(mView);
         while (!stack.isEmpty()) {
             View child = stack.pop();
-            if (child instanceof ImageView) {
+            if (child instanceof ImageView
+                    // Skip the importance ring for conversations, disabled cropping is needed for
+                    // its animation
+                    && child.getId() != com.android.internal.R.id.conversation_icon_badge_ring) {
                 ((ImageView) child).setCropToPadding(true);
             } else if (child instanceof ViewGroup){
                 ViewGroup group = (ViewGroup) child;
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 34998a0..b9240c7 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -898,11 +898,16 @@
                         /* ignore */
                     }
                     mSurfaceControl = surfaceControl;
-                    mService.mTransactionFactory.get().setLayer(mSurfaceControl,
-                            mService.mPolicy.getWindowLayerFromTypeLw(TYPE_MAGNIFICATION_OVERLAY)
-                                    * WindowManagerService.TYPE_LAYER_MULTIPLIER)
-                            .setPosition(mSurfaceControl, 0, 0)
-                            .apply();
+
+                    final SurfaceControl.Transaction t = mService.mTransactionFactory.get();
+                    final int layer =
+                            mService.mPolicy.getWindowLayerFromTypeLw(TYPE_MAGNIFICATION_OVERLAY) *
+                                    WindowManagerService.TYPE_LAYER_MULTIPLIER;
+                    t.setLayer(mSurfaceControl, layer).setPosition(mSurfaceControl, 0, 0);
+                    InputMonitor.setTrustedOverlayInputInfo(mSurfaceControl, t,
+                            mDisplayContent.getDisplayId(), "Magnification Overlay");
+                    t.apply();
+
                     mSurface.copyFrom(mSurfaceControl);
 
                     mAnimationController = new AnimationController(context,
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 5f591b5..7c935d0 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -4873,6 +4873,10 @@
                     return;
                 }
 
+                if (isInPictureInPictureMode(activity)) {
+                    throw new IllegalStateException("Activity is already in PIP mode");
+                }
+
                 final boolean canEnterPictureInPicture = activity.checkEnterPictureInPictureState(
                         "requestPictureInPictureMode", /* beforeStopping */ false);
                 if (!canEnterPictureInPicture) {
diff --git a/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
index 2165b0e..c9cc944 100644
--- a/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
+++ b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
@@ -66,6 +66,9 @@
             t.setLayer(ctrl, zOrder);
             t.setPosition(ctrl, 0, 0);
             t.show(ctrl);
+            // Ensure we aren't considered as obscuring for Input purposes.
+            InputMonitor.setTrustedOverlayInputInfo(ctrl, t,
+                    dc.getDisplayId(), "EmulatorDisplayOverlay");
             mSurface.copyFrom(ctrl);
         } catch (OutOfResourcesException e) {
         }
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index efcd61d..8734b5e 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -16,13 +16,17 @@
 
 package com.android.server.wm;
 
+import static android.os.Process.myPid;
+import static android.os.Process.myUid;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION;
 import static android.view.WindowManager.INPUT_CONSUMER_PIP;
 import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
 import static android.view.WindowManager.INPUT_CONSUMER_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS;
+import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
@@ -550,4 +554,26 @@
             }
         }
     }
+
+    /**
+     * Helper function to generate an InputInfo with type SECURE_SYSTEM_OVERLAY. This input
+     * info will not have an input channel or be touchable, but is used to omit Surfaces
+     * from occlusion detection, so that System global overlays like the Watermark aren't
+     * counted by the InputDispatcher as occluding applications below.
+     */
+    static void setTrustedOverlayInputInfo(SurfaceControl sc, SurfaceControl.Transaction t,
+            int displayId, String name) {
+        InputWindowHandle inputWindowHandle = new InputWindowHandle(null, displayId);
+        inputWindowHandle.name = name;
+        inputWindowHandle.layoutParamsType = TYPE_SECURE_SYSTEM_OVERLAY;
+        inputWindowHandle.dispatchingTimeoutNanos = -1;
+        inputWindowHandle.visible = true;
+        inputWindowHandle.canReceiveKeys = false;
+        inputWindowHandle.hasFocus = false;
+        inputWindowHandle.ownerPid = myPid();
+        inputWindowHandle.ownerUid = myUid();
+        inputWindowHandle.inputFeatures = INPUT_FEATURE_NO_INPUT_CHANNEL;
+        inputWindowHandle.scaleFactor = 1;
+        t.setInputWindowInfo(sc, inputWindowHandle);
+    }
 }
diff --git a/services/core/java/com/android/server/wm/StrictModeFlash.java b/services/core/java/com/android/server/wm/StrictModeFlash.java
index f537005..fa62daa 100644
--- a/services/core/java/com/android/server/wm/StrictModeFlash.java
+++ b/services/core/java/com/android/server/wm/StrictModeFlash.java
@@ -54,6 +54,10 @@
             t.setLayer(ctrl, WindowManagerService.TYPE_LAYER_MULTIPLIER * 101);
             t.setPosition(ctrl, 0, 0);
             t.show(ctrl);
+            // Ensure we aren't considered as obscuring for Input purposes.
+            InputMonitor.setTrustedOverlayInputInfo(ctrl, t, dc.getDisplayId(),
+                    "StrictModeFlash");
+
             mSurface.copyFrom(ctrl);
         } catch (OutOfResourcesException e) {
         }
diff --git a/services/core/java/com/android/server/wm/Watermark.java b/services/core/java/com/android/server/wm/Watermark.java
index 4e1b217..3d49ebe 100644
--- a/services/core/java/com/android/server/wm/Watermark.java
+++ b/services/core/java/com/android/server/wm/Watermark.java
@@ -29,6 +29,7 @@
 import android.util.Log;
 import android.util.TypedValue;
 import android.view.Display;
+import android.view.InputWindowHandle;
 import android.view.Surface;
 import android.view.Surface.OutOfResourcesException;
 import android.view.SurfaceControl;
@@ -124,6 +125,8 @@
             t.setLayer(ctrl, WindowManagerService.TYPE_LAYER_MULTIPLIER * 100)
                     .setPosition(ctrl, 0, 0)
                     .show(ctrl);
+            // Ensure we aren't considered as obscuring for Input purposes.
+            InputMonitor.setTrustedOverlayInputInfo(ctrl, t, dc.getDisplayId(), "Watermark");
             mSurface.copyFrom(ctrl);
         } catch (OutOfResourcesException e) {
         }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index bd616a3..64b5eca 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -17,7 +17,6 @@
 package com.android.server.wm;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
@@ -111,7 +110,7 @@
         final ActivityStack stack = new StackBuilder(mRootWindowContainer).build();
         final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity();
         ClientLifecycleManager lifecycleManager = mService.getLifecycleManager();
-        doNothing().when(lifecycleManager).scheduleTransaction(any());
+        doReturn(false).when(activity).inPinnedWindowingMode();
         doReturn(false).when(activity).checkEnterPictureInPictureState(anyString(), anyBoolean());
 
         mService.requestPictureInPictureMode(activity.token);
@@ -120,6 +119,19 @@
         verify(lifecycleManager, times(0)).scheduleTransaction(any());
     }
 
+    @Test(expected = IllegalStateException.class)
+    public void testOnPictureInPictureRequested_alreadyInPIPMode() throws RemoteException {
+        final ActivityStack stack = new StackBuilder(mRootWindowContainer).build();
+        final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity();
+        ClientLifecycleManager lifecycleManager = mService.getLifecycleManager();
+        doReturn(true).when(activity).inPinnedWindowingMode();
+
+        mService.requestPictureInPictureMode(activity.token);
+
+        // Check that no transactions with enter pip requests are made.
+        verify(lifecycleManager, times(0)).scheduleTransaction(any());
+    }
+
     @Test
     public void testDisplayWindowListener() {
         final ArrayList<Integer> added = new ArrayList<>();