Merge "Set shelf screenshot landscape size and window insets" into 24D1-dev
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 8e73379..ae10bef 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -415,7 +415,7 @@
 }
 
 flag {
-    name: "screenshot_shelf_ui"
+    name: "screenshot_shelf_ui2"
     namespace: "systemui"
     description: "Use new shelf UI flow for screenshots"
     bug: "329659738"
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ReferenceScreenshotModule.java b/packages/SystemUI/src/com/android/systemui/screenshot/ReferenceScreenshotModule.java
index afb0280..d1b08f1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ReferenceScreenshotModule.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ReferenceScreenshotModule.java
@@ -35,4 +35,10 @@
     @Binds
     ScreenshotActionsProvider.Factory bindScreenshotActionsProviderFactory(
             DefaultScreenshotActionsProvider.Factory defaultScreenshotActionsProviderFactory);
+
+    /** */
+    @Provides
+    static ThumbnailObserver providesThumbnailObserver() {
+        return new ThumbnailObserver();
+    };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 3207db9..0217fc2 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -19,7 +19,7 @@
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
 
-import static com.android.systemui.Flags.screenshotShelfUi;
+import static com.android.systemui.Flags.screenshotShelfUi2;
 import static com.android.systemui.screenshot.LogConfig.DEBUG_ANIM;
 import static com.android.systemui.screenshot.LogConfig.DEBUG_CALLBACK;
 import static com.android.systemui.screenshot.LogConfig.DEBUG_INPUT;
@@ -403,7 +403,7 @@
             return;
         }
 
-        if (screenshotShelfUi()) {
+        if (screenshotShelfUi2()) {
             final UUID requestId = UUID.randomUUID();
             final String screenshotId = String.format("Screenshot_%s", requestId);
             mActionsProvider = mActionsProviderFactory.create(
@@ -454,7 +454,7 @@
         // ignore system bar insets for the purpose of window layout
         mWindow.getDecorView().setOnApplyWindowInsetsListener(
                 (v, insets) -> WindowInsets.CONSUMED);
-        if (!screenshotShelfUi()) {
+        if (!screenshotShelfUi2()) {
             mScreenshotHandler.cancelTimeout(); // restarted after animation
         }
     }
@@ -503,7 +503,7 @@
     }
 
     boolean isPendingSharedTransition() {
-        if (screenshotShelfUi()) {
+        if (screenshotShelfUi2()) {
             return mActionExecutor.isPendingSharedTransition();
         } else {
             return mViewProxy.isPendingSharedTransition();
@@ -624,7 +624,7 @@
                 (response) -> {
                     mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_IMPRESSION,
                             0, response.getPackageName());
-                    if (screenshotShelfUi() && mActionsProvider != null) {
+                    if (screenshotShelfUi2() && mActionsProvider != null) {
                         mActionsProvider.onScrollChipReady(
                                 () -> onScrollButtonClicked(owner, response));
                     } else {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt
index 3d72bca..9b754f3 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt
@@ -32,6 +32,7 @@
 import android.window.OnBackInvokedCallback
 import android.window.OnBackInvokedDispatcher
 import androidx.core.animation.doOnEnd
+import androidx.core.animation.doOnStart
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.log.DebugLogger.debugLog
 import com.android.systemui.res.R
@@ -56,6 +57,7 @@
     private val logger: UiEventLogger,
     private val viewModel: ScreenshotViewModel,
     private val windowManager: WindowManager,
+    private val thumbnailObserver: ThumbnailObserver,
     @Assisted private val context: Context,
     @Assisted private val displayId: Int
 ) : ScreenshotViewProxy {
@@ -100,6 +102,10 @@
             info.touchableRegion.set(touchableRegion)
         }
         screenshotPreview = view.screenshotPreview
+        thumbnailObserver.setViews(
+            view.screenshotPreview,
+            view.requireViewById(R.id.screenshot_preview_border)
+        )
     }
 
     override fun reset() {
@@ -114,8 +120,12 @@
 
     override fun createScreenshotDropInAnimation(screenRect: Rect, showFlash: Boolean): Animator {
         val entrance = animationController.getEntranceAnimation(screenRect, showFlash)
-        // reset the timeout when animation finishes
-        entrance.doOnEnd { callbacks?.onUserInteraction() }
+        entrance.doOnStart { thumbnailObserver.onEntranceStarted() }
+        entrance.doOnEnd {
+            // reset the timeout when animation finishes
+            callbacks?.onUserInteraction()
+            thumbnailObserver.onEntranceComplete()
+        }
         return entrance
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ThumbnailObserver.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ThumbnailObserver.kt
new file mode 100644
index 0000000..cf62a14
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ThumbnailObserver.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 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.screenshot
+
+import android.view.View
+import android.widget.ImageView
+
+/** An observer of thumbnail UI and entrance state that can be overridden if needed. */
+open class ThumbnailObserver {
+    /** Thumbnail image and border views. */
+    open fun setViews(image: ImageView, border: View) {}
+
+    /** Entrance animation has begun. */
+    open fun onEntranceStarted() {}
+
+    /** Entrance animation has completed/stopped. */
+    open fun onEntranceComplete() {}
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
index b3eaa91..dbb3cb6 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.screenshot.dagger;
 
-import static com.android.systemui.Flags.screenshotShelfUi;
+import static com.android.systemui.Flags.screenshotShelfUi2;
 
 import android.app.Service;
 import android.view.accessibility.AccessibilityManager;
@@ -102,7 +102,7 @@
     static ScreenshotViewProxy.Factory providesScreenshotViewProxyFactory(
             ScreenshotShelfViewProxy.Factory shelfScreenshotViewProxyFactory,
             LegacyScreenshotViewProxy.Factory legacyScreenshotViewProxyFactory) {
-        if (screenshotShelfUi()) {
+        if (screenshotShelfUi2()) {
             return shelfScreenshotViewProxyFactory;
         } else {
             return legacyScreenshotViewProxyFactory;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt
index d642b7d..4eceb17 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt
@@ -156,6 +156,12 @@
 
         val previewAnimator = AnimatorSet()
         previewAnimator.play(previewXAndScaleAnimator).with(previewYAnimator)
+        previewAnimator.doOnEnd {
+            screenshotPreview.scaleX = 1f
+            screenshotPreview.scaleY = 1f
+            screenshotPreview.x = endPos.x - screenshotPreview.width / 2f
+            screenshotPreview.y = endPos.y - screenshotPreview.height / 2f
+        }
 
         previewAnimator.doOnStart { screenshotPreview.visibility = View.VISIBLE }
         return previewAnimator
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 2d1aba4..b23628d 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -2691,7 +2691,7 @@
                 for (Record r : mRecords) {
                     if (r.matchTelephonyCallbackEvent(
                             TelephonyCallback.EVENT_RADIO_POWER_STATE_CHANGED)
-                            && idMatch(r, subId, phoneId)) {
+                            && idMatchRelaxed(r, subId, phoneId)) {
                         try {
                             r.callback.onRadioPowerStateChanged(state);
                         } catch (RemoteException ex) {
@@ -4089,6 +4089,42 @@
         }
     }
 
+    /**
+     * Match the sub id or phone id of the event to the record with relaxed rules
+     *
+     * We follow the rules below:
+     * 1) If sub id of the event is invalid, phone id should be used.
+     * 2) If record's phoneId is also invalid then allow phone 0 notifications
+     * 3) The event on default sub should be notified to the records
+     * which register the default sub id.
+     * 4) Sub id should be exactly matched for all other cases.
+     * TODO: b/337878785 for longterm fix
+     */
+    boolean idMatchRelaxed(Record r, int subId, int phoneId) {
+
+        if (subId < 0) {
+            // Invalid case, we need compare phoneId.
+            // If the record does not have a valid phone Id send phone 0 notifications.
+            // A record's phoneId can get invalid if there is no SIM or modem was restarting
+            // when caller registered.
+            if (r.phoneId == INVALID_SIM_SLOT_INDEX) {
+                return (phoneId == 0);
+            } else {
+                return (r.phoneId == phoneId);
+            }
+        }
+
+        if (r.subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
+            // if the registered record does not have a valid phoneId then use the phone 0
+            if (r.phoneId == INVALID_SIM_SLOT_INDEX) {
+                return (phoneId == 0);
+            }
+            return (subId == mDefaultSubId);
+        } else {
+            return (r.subId == subId);
+        }
+    }
+
     private boolean checkFineLocationAccess(Record r) {
         return checkFineLocationAccess(r, Build.VERSION_CODES.BASE);
     }
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index cd96806..89c5a17 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -518,8 +518,17 @@
         final SurfaceControl leash = mAdapter.mCapturedLeash;
         mControlTarget = target;
         updateVisibility();
+        boolean initiallyVisible = mClientVisible;
+        if (mSource.getType() == WindowInsets.Type.ime()) {
+            // The IME cannot be initially visible, see ControlAdapter#startAnimation below.
+            // Also, the ImeInsetsSourceConsumer clears the client visibility upon losing control,
+            // but this won't have reached here yet by the time the new control is created.
+            // Note: The DisplayImeController needs the correct previous client's visibility, so we
+            // only override the initiallyVisible here.
+            initiallyVisible = false;
+        }
         mControl = new InsetsSourceControl(mSource.getId(), mSource.getType(), leash,
-                mClientVisible, surfacePosition, getInsetsHint());
+                initiallyVisible, surfacePosition, getInsetsHint());
 
         ProtoLog.d(WM_DEBUG_WINDOW_INSETS,
                 "InsetsSource Control %s for target %s", mControl, mControlTarget);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 21c16eb..606070c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -4154,11 +4154,49 @@
     }
 
     @Test
+    @EnableCompatChanges({ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION})
+    public void testPortraitCloseToSquareDisplayWithTaskbar_insetsOverridden_notLetterboxed() {
+        // Set up portrait close to square display.
+        setUpDisplaySizeWithApp(2200, 2280);
+        final DisplayContent display = mActivity.mDisplayContent;
+        display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        // Simulate insets, final app bounds are (0, 0, 2200, 2130) - landscape.
+        final WindowState navbar = createWindow(null, TYPE_NAVIGATION_BAR, mDisplayContent,
+                "navbar");
+        final Binder owner = new Binder();
+        navbar.mAttrs.providedInsets = new InsetsFrameProvider[] {
+                new InsetsFrameProvider(owner, 0, WindowInsets.Type.navigationBars())
+                        .setInsetsSize(Insets.of(0, 0, 0, 150))
+        };
+        display.getDisplayPolicy().addWindowLw(navbar, navbar.mAttrs);
+        assertTrue(display.getDisplayPolicy().updateDecorInsetsInfo());
+        display.sendNewConfiguration();
+
+        final ActivityRecord activity = new ActivityBuilder(mAtm)
+                .setTask(mTask)
+                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
+                .setComponent(ComponentName.createRelative(mContext,
+                        SizeCompatTests.class.getName()))
+                .setUid(android.os.Process.myUid())
+                .build();
+
+        // Activity should not be letterboxed and should have portrait app bounds even though
+        // orientation is not respected with insets as insets have been decoupled.
+        final Rect appBounds = activity.getWindowConfiguration().getAppBounds();
+        final Rect displayBounds = display.getBounds();
+        assertFalse(activity.isLetterboxedForFixedOrientationAndAspectRatio());
+        assertNotNull(appBounds);
+        assertEquals(displayBounds.width(), appBounds.width());
+        assertEquals(displayBounds.height(), appBounds.height());
+    }
+
+    @Test
     @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED})
     public void testPortraitCloseToSquareDisplayWithTaskbar_letterboxed() {
         // Set up portrait close to square display
         setUpDisplaySizeWithApp(2200, 2280);
         final DisplayContent display = mActivity.mDisplayContent;
+        display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
         // Simulate taskbar, final app bounds are (0, 0, 2200, 2130) - landscape
         final WindowState navbar = createWindow(null, TYPE_NAVIGATION_BAR, mDisplayContent,
                 "navbar");