Merge changes Ic3ce1b9d,I5f4b9817 into main

* changes:
  [SB][Screen Chips] Add share-to-app and cast-to-other-device chips.
  [SB][Screen Chips] Hook up new chip flow to CollapsedStatusBarFragment.
diff --git a/packages/SystemUI/res/layout/ongoing_activity_chip.xml b/packages/SystemUI/res/layout/ongoing_activity_chip.xml
index a33be12..cd5c37d 100644
--- a/packages/SystemUI/res/layout/ongoing_activity_chip.xml
+++ b/packages/SystemUI/res/layout/ongoing_activity_chip.xml
@@ -40,6 +40,7 @@
 
         <ImageView
             android:src="@*android:drawable/ic_phone"
+            android:id="@+id/ongoing_activity_chip_icon"
             android:layout_width="@dimen/ongoing_activity_chip_icon_size"
             android:layout_height="@dimen/ongoing_activity_chip_icon_size"
             android:tint="?android:attr/colorPrimary"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt
new file mode 100644
index 0000000..ac16d26
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt
@@ -0,0 +1,79 @@
+/*
+ * 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.statusbar.chips.mediaprojection.domain.interactor
+
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.mediaprojection.data.model.MediaProjectionState
+import com.android.systemui.mediaprojection.data.repository.MediaProjectionRepository
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.chips.domain.interactor.OngoingActivityChipInteractor
+import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
+import com.android.systemui.util.time.SystemClock
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * Interactor for media-projection-related chips in the status bar.
+ *
+ * There are two kinds of media projection events that will show chips in the status bar:
+ * 1) Share-to-app: Sharing your phone screen content to another app on the same device. (Triggered
+ *    from within each individual app.)
+ * 2) Cast-to-other-device: Sharing your phone screen content to a different device. (Triggered from
+ *    the Quick Settings Cast tile or from the Settings app.) This interactor handles both of those
+ *    event types (though maybe not audio-only casting -- see b/342169876).
+ */
+@SysUISingleton
+class MediaProjectionChipInteractor
+@Inject
+constructor(
+    @Application scope: CoroutineScope,
+    mediaProjectionRepository: MediaProjectionRepository,
+    val systemClock: SystemClock,
+) : OngoingActivityChipInteractor {
+    override val chip: StateFlow<OngoingActivityChipModel> =
+        mediaProjectionRepository.mediaProjectionState
+            .map { state ->
+                when (state) {
+                    is MediaProjectionState.NotProjecting -> OngoingActivityChipModel.Hidden
+                    is MediaProjectionState.EntireScreen,
+                    is MediaProjectionState.SingleTask -> {
+                        // TODO(b/332662551): Distinguish between cast-to-other-device and
+                        // share-to-app.
+                        OngoingActivityChipModel.Shown(
+                            icon =
+                                Icon.Resource(
+                                    R.drawable.ic_cast_connected,
+                                    ContentDescription.Resource(R.string.accessibility_casting)
+                                ),
+                            // TODO(b/332662551): See if we can use a MediaProjection API to fetch
+                            // this time.
+                            startTimeMs = systemClock.elapsedRealtime()
+                        ) {
+                            // TODO(b/332662551): Implement the pause dialog.
+                        }
+                    }
+                }
+            }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), OngoingActivityChipModel.Hidden)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt
index bff5686..585ff5f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt
@@ -34,7 +34,7 @@
 
 /** Interactor for the screen recording chip shown in the status bar. */
 @SysUISingleton
-open class ScreenRecordChipInteractor
+class ScreenRecordChipInteractor
 @Inject
 constructor(
     @Application scope: CoroutineScope,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/ChipChronometerBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/ChipChronometerBinder.kt
new file mode 100644
index 0000000..2032ec8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/ChipChronometerBinder.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.statusbar.chips.ui.binder
+
+import com.android.systemui.statusbar.chips.ui.view.ChipChronometer
+
+object ChipChronometerBinder {
+    /**
+     * Updates the given [view] chronometer with a new start time and starts it.
+     *
+     * @param startTimeMs the time this event started, relative to
+     *   [com.android.systemui.util.time.SystemClock.elapsedRealtime]. See
+     *   [android.widget.Chronometer.setBase].
+     */
+    fun bind(startTimeMs: Long, view: ChipChronometer) {
+        view.base = startTimeMs
+        view.start()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
index 208eb50..edb2983 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
@@ -20,6 +20,7 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.statusbar.chips.call.domain.interactor.CallChipInteractor
 import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractor
 import com.android.systemui.statusbar.chips.screenrecord.domain.interactor.ScreenRecordChipInteractor
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
@@ -40,6 +41,7 @@
 constructor(
     @Application scope: CoroutineScope,
     screenRecordChipInteractor: ScreenRecordChipInteractor,
+    mediaProjectionChipInteractor: MediaProjectionChipInteractor,
     callChipInteractor: CallChipInteractor,
 ) {
 
@@ -51,10 +53,19 @@
      * actually displaying the chip.
      */
     val chip: StateFlow<OngoingActivityChipModel> =
-        combine(screenRecordChipInteractor.chip, callChipInteractor.chip) { screenRecord, call ->
+        combine(
+                screenRecordChipInteractor.chip,
+                mediaProjectionChipInteractor.chip,
+                callChipInteractor.chip
+            ) { screenRecord, mediaProjection, call ->
                 // This `when` statement shows the priority order of the chips
                 when {
+                    // Screen recording also activates the media projection APIs, so whenever the
+                    // screen recording chip is active, the media projection chip would also be
+                    // active. We want the screen-recording-specific chip shown in this case, so we
+                    // give the screen recording chip priority. See b/296461748.
                     screenRecord is OngoingActivityChipModel.Shown -> screenRecord
+                    mediaProjection is OngoingActivityChipModel.Shown -> mediaProjection
                     else -> call
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index 97f9e06..aac211a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -39,6 +39,7 @@
 import com.android.app.animation.InterpolatorsAndroidX;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.Dumpable;
+import com.android.systemui.Flags;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.demomode.DemoMode;
 import com.android.systemui.demomode.DemoModeController;
@@ -51,7 +52,6 @@
 import com.android.systemui.statusbar.OperatorNameView;
 import com.android.systemui.statusbar.OperatorNameViewController;
 import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModel;
 import com.android.systemui.statusbar.disableflags.DisableFlagsLogger.DisableState;
 import com.android.systemui.statusbar.events.SystemStatusAnimationCallback;
 import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
@@ -135,8 +135,6 @@
     private final CollapsedStatusBarFragmentLogger mCollapsedStatusBarFragmentLogger;
     private final OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
     private final OngoingCallController mOngoingCallController;
-    // TODO(b/332662551): Use this view model to show the ongoing activity chips.
-    private final OngoingActivityChipsViewModel mOngoingActivityChipsViewModel;
     private final SystemStatusAnimationScheduler mAnimationScheduler;
     private final StatusBarLocationPublisher mLocationPublisher;
     private final NotificationIconAreaController mNotificationIconAreaController;
@@ -207,6 +205,11 @@
     private boolean mTransitionFromLockscreenToDreamStarted = false;
 
     /**
+     * True if there's an active ongoing activity that should be showing a chip and false otherwise.
+     */
+    private boolean mHasOngoingActivity;
+
+    /**
      * Listener that updates {@link #mWaitingForWindowStateChangeAfterCameraLaunch} when it receives
      * a new status bar window state.
      */
@@ -216,11 +219,12 @@
     };
     private DisposableHandle mNicBindingDisposable;
 
+    private boolean mAnimationsEnabled = true;
+
     @Inject
     public CollapsedStatusBarFragment(
             StatusBarFragmentComponent.Factory statusBarFragmentComponentFactory,
             OngoingCallController ongoingCallController,
-            OngoingActivityChipsViewModel ongoingActivityChipsViewModel,
             SystemStatusAnimationScheduler animationScheduler,
             StatusBarLocationPublisher locationPublisher,
             NotificationIconAreaController notificationIconAreaController,
@@ -246,7 +250,6 @@
             DemoModeController demoModeController) {
         mStatusBarFragmentComponentFactory = statusBarFragmentComponentFactory;
         mOngoingCallController = ongoingCallController;
-        mOngoingActivityChipsViewModel = ongoingActivityChipsViewModel;
         mAnimationScheduler = animationScheduler;
         mLocationPublisher = locationPublisher;
         mNotificationIconAreaController = notificationIconAreaController;
@@ -401,6 +404,17 @@
         return mBlockedIcons;
     }
 
+
+    @VisibleForTesting
+    void enableAnimationsForTesting() {
+        mAnimationsEnabled = true;
+    }
+
+    @VisibleForTesting
+    void disableAnimationsForTesting() {
+        mAnimationsEnabled = false;
+    }
+
     @Override
     public void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
@@ -476,7 +490,7 @@
             notificationIconArea.addView(mNotificationIconAreaInner);
         }
 
-        updateNotificationIconAreaAndCallChip(/* animate= */ false);
+        updateNotificationIconAreaAndOngoingActivityChip(/* animate= */ false);
         Trace.endSection();
     }
 
@@ -493,15 +507,21 @@
 
     private StatusBarVisibilityChangeListener mStatusBarVisibilityChangeListener =
             new StatusBarVisibilityChangeListener() {
-        @Override
-        public void onStatusBarVisibilityMaybeChanged() {
-            updateStatusBarVisibilities(/* animate= */ true);
-        }
+                @Override
+                public void onStatusBarVisibilityMaybeChanged() {
+                    updateStatusBarVisibilities(/* animate= */ true);
+                }
 
-        @Override
-        public void onTransitionFromLockscreenToDreamStarted() {
-            mTransitionFromLockscreenToDreamStarted = true;
-        }
+                @Override
+                public void onTransitionFromLockscreenToDreamStarted() {
+                    mTransitionFromLockscreenToDreamStarted = true;
+                }
+
+                @Override
+                public void onOngoingActivityStatusChanged(boolean hasOngoingActivity) {
+                    mHasOngoingActivity = hasOngoingActivity;
+                    updateStatusBarVisibilities(/* animate= */ true);
+                }
     };
 
     @Override
@@ -532,11 +552,14 @@
             }
         }
 
-        // The ongoing call chip and notification icon visibilities are intertwined, so update both
-        // if either change.
-        if (newModel.getShowNotificationIcons() != previousModel.getShowNotificationIcons()
-                || newModel.getShowOngoingCallChip() != previousModel.getShowOngoingCallChip()) {
-            updateNotificationIconAreaAndCallChip(animate);
+        // The ongoing activity chip and notification icon visibilities are intertwined, so update
+        // both if either change.
+        boolean notifsChanged =
+                newModel.getShowNotificationIcons() != previousModel.getShowNotificationIcons();
+        boolean ongoingActivityChanged =
+                newModel.getShowOngoingActivityChip() != previousModel.getShowOngoingActivityChip();
+        if (notifsChanged || ongoingActivityChanged) {
+            updateNotificationIconAreaAndOngoingActivityChip(animate);
         }
 
         // The clock may have already been hidden, but we might want to shift its
@@ -566,45 +589,58 @@
             return new StatusBarVisibilityModel(
                     /* showClock= */ false,
                     /* showNotificationIcons= */ false,
-                    /* showOngoingCallChip= */ false,
+                    /* showOngoingActivityChip= */ false,
                     /* showSystemInfo= */ false);
         }
 
         boolean showClock = externalModel.getShowClock() && !headsUpVisible;
-        boolean showOngoingCallChip = mOngoingCallController.hasOngoingCall() && !headsUpVisible;
+
+        boolean showOngoingActivityChip;
+        if (Flags.statusBarScreenSharingChips()) {
+            // If this flag is on, the ongoing activity status comes from
+            // CollapsedStatusBarViewBinder, which updates the mHasOngoingActivity variable.
+            showOngoingActivityChip = mHasOngoingActivity;
+        } else {
+            // If this flag is off, the only ongoing activity is the ongoing call, and we pull it
+            // from the controller directly.
+            showOngoingActivityChip = mOngoingCallController.hasOngoingCall();
+        }
+
         return new StatusBarVisibilityModel(
                 showClock,
                 externalModel.getShowNotificationIcons(),
-                showOngoingCallChip,
+                showOngoingActivityChip && !headsUpVisible,
                 externalModel.getShowSystemInfo());
     }
 
     /**
-     * Updates the visibility of the notification icon area and ongoing call chip based on disabled1
-     * state.
+     * Updates the visibility of the notification icon area and ongoing activity chip based on
+     * mLastModifiedVisibility.
      */
-    private void updateNotificationIconAreaAndCallChip(boolean animate) {
+    private void updateNotificationIconAreaAndOngoingActivityChip(boolean animate) {
         StatusBarVisibilityModel visibilityModel = mLastModifiedVisibility;
         boolean disableNotifications = !visibilityModel.getShowNotificationIcons();
-        boolean hasOngoingCall = visibilityModel.getShowOngoingCallChip();
+        boolean hasOngoingActivity = visibilityModel.getShowOngoingActivityChip();
 
-        // Hide notifications if the disable flag is set or we have an ongoing call.
-        if (disableNotifications || hasOngoingCall) {
-            hideNotificationIconArea(animate && !hasOngoingCall);
+        // Hide notifications if the disable flag is set or we have an ongoing activity.
+        if (disableNotifications || hasOngoingActivity) {
+            hideNotificationIconArea(animate && !hasOngoingActivity);
         } else {
             showNotificationIconArea(animate);
         }
 
-        // Show the ongoing call chip only if there is an ongoing call *and* notification icons
-        // are allowed. (The ongoing call chip occupies the same area as the notification icons,
-        // so if the icons are disabled then the call chip should be, too.)
-        boolean showOngoingCallChip = hasOngoingCall && !disableNotifications;
-        if (showOngoingCallChip) {
+        // Show the ongoing activity chip only if there is an ongoing activity *and* notification
+        // icons are allowed. (The ongoing activity chip occupies the same area as the notification,
+        // icons so if the icons are disabled then the activity chip should be, too.)
+        boolean showOngoingActivityChip = hasOngoingActivity && !disableNotifications;
+        if (showOngoingActivityChip) {
             showOngoingActivityChip(animate);
         } else {
             hideOngoingActivityChip(animate);
         }
-        mOngoingCallController.notifyChipVisibilityChanged(showOngoingCallChip);
+        if (!Flags.statusBarScreenSharingChips()) {
+            mOngoingCallController.notifyChipVisibilityChanged(showOngoingActivityChip);
+        }
     }
 
     private boolean shouldHideStatusBar() {
@@ -702,8 +738,9 @@
     /**
      * Displays the ongoing activity chip.
      *
-     * For now, this chip will only ever contain the ongoing call information, but after b/332662551
-     * feature is implemented, it will support different kinds of ongoing activities.
+     * If Flags.statusBarScreenSharingChips is disabled, this chip will only ever contain the
+     * ongoing call information, If that flag is enabled, it will support different kinds of ongoing
+     * activities. See b/332662551.
      */
     private void showOngoingActivityChip(boolean animate) {
         animateShow(mOngoingActivityChip, animate);
@@ -746,7 +783,7 @@
      */
     private void animateHiddenState(final View v, int state, boolean animate) {
         v.animate().cancel();
-        if (!animate) {
+        if (!animate || !mAnimationsEnabled) {
             v.setAlpha(0f);
             v.setVisibility(state);
             return;
@@ -773,7 +810,7 @@
     private void animateShow(View v, boolean animate) {
         v.animate().cancel();
         v.setVisibility(View.VISIBLE);
-        if (!animate) {
+        if (!animate || !mAnimationsEnabled) {
             v.setAlpha(1f);
             return;
         }
@@ -872,6 +909,8 @@
     @Override
     public void dump(PrintWriter printWriter, String[] args) {
         IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, /* singleIndent= */"  ");
+        pw.println("mHasOngoingActivity=" + mHasOngoingActivity);
+        pw.println("mAnimationsEnabled=" + mAnimationsEnabled);
         StatusBarFragmentComponent component = mStatusBarFragmentComponent;
         if (component == null) {
             pw.println("StatusBarFragmentComponent is null");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLogger.kt
index 7cdb9c0..0a19023 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLogger.kt
@@ -59,13 +59,13 @@
             {
                 bool1 = model.showClock
                 bool2 = model.showNotificationIcons
-                bool3 = model.showOngoingCallChip
+                bool3 = model.showOngoingActivityChip
                 bool4 = model.showSystemInfo
             },
             { "New visibilities calculated internally. " +
                     "showClock=$bool1 " +
                     "showNotificationIcons=$bool2 " +
-                    "showOngoingCallChip=$bool3 " +
+                    "showOngoingActivityChip=$bool3 " +
                     "showSystemInfo=$bool4"
             }
         )
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModel.kt
index cf54cb7..fe24fae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModel.kt
@@ -26,7 +26,7 @@
 data class StatusBarVisibilityModel(
     val showClock: Boolean,
     val showNotificationIcons: Boolean,
-    val showOngoingCallChip: Boolean,
+    val showOngoingActivityChip: Boolean,
     val showSystemInfo: Boolean,
 ) {
     companion object {
@@ -48,7 +48,7 @@
                 showNotificationIcons = (disabled1 and DISABLE_NOTIFICATION_ICONS) == 0,
                 // TODO(b/279899176): [CollapsedStatusBarFragment] always overwrites this with the
                 //  value of [OngoingCallController]. Do we need to process the flag here?
-                showOngoingCallChip = (disabled1 and DISABLE_ONGOING_CALL_CHIP) == 0,
+                showOngoingActivityChip = (disabled1 and DISABLE_ONGOING_CALL_CHIP) == 0,
                 showSystemInfo =
                     (disabled1 and DISABLE_SYSTEM_INFO) == 0 &&
                         (disabled2 and DISABLE2_SYSTEM_ICONS) == 0
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
index 7d7f49b..a2ec1f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
@@ -19,11 +19,17 @@
 import android.animation.Animator
 import android.animation.AnimatorListenerAdapter
 import android.view.View
+import android.widget.ImageView
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.Flags
+import com.android.systemui.common.ui.binder.IconViewBinder
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.res.R
+import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.chips.ui.binder.ChipChronometerBinder
+import com.android.systemui.statusbar.chips.ui.view.ChipChronometer
 import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
 import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel
 import javax.inject.Inject
@@ -75,6 +81,35 @@
                         }
                     }
                 }
+
+                if (Flags.statusBarScreenSharingChips()) {
+                    val chipView: View = view.requireViewById(R.id.ongoing_activity_chip)
+                    val chipIconView: ImageView =
+                        chipView.requireViewById(R.id.ongoing_activity_chip_icon)
+                    val chipTimeView: ChipChronometer =
+                        chipView.requireViewById(R.id.ongoing_activity_chip_time)
+                    launch {
+                        viewModel.ongoingActivityChip.collect { chipModel ->
+                            when (chipModel) {
+                                is OngoingActivityChipModel.Shown -> {
+                                    IconViewBinder.bind(chipModel.icon, chipIconView)
+                                    ChipChronometerBinder.bind(chipModel.startTimeMs, chipTimeView)
+                                    // TODO(b/332662551): Attach click listener to chip
+
+                                    listener.onOngoingActivityStatusChanged(
+                                        hasOngoingActivity = true
+                                    )
+                                }
+                                is OngoingActivityChipModel.Hidden -> {
+                                    chipTimeView.stop()
+                                    listener.onOngoingActivityStatusChanged(
+                                        hasOngoingActivity = false
+                                    )
+                                }
+                            }
+                        }
+                    }
+                }
             }
         }
     }
@@ -120,4 +155,7 @@
 
     /** Called when a transition from lockscreen to dream has started. */
     fun onTransitionFromLockscreenToDreamStarted()
+
+    /** Called when the status of the ongoing activity chip (active or not active) has changed. */
+    fun onOngoingActivityStatusChanged(hasOngoingActivity: Boolean)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt
index 0a6e95e..bb3a67e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt
@@ -24,6 +24,8 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
 import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModel
 import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
 import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
 import com.android.systemui.statusbar.phone.domain.interactor.LightsOutInteractor
@@ -59,6 +61,9 @@
     /** Emits whenever a transition from lockscreen to dream has started. */
     val transitionFromLockscreenToDreamStartedEvent: Flow<Unit>
 
+    /** The ongoing activity chip that should be shown on the left-hand side of the status bar. */
+    val ongoingActivityChip: StateFlow<OngoingActivityChipModel>
+
     /**
      * Apps can request a low profile mode [android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE] where
      * status bar and navigation icons dim. In this mode, a notification dot appears where the
@@ -78,6 +83,7 @@
     private val lightsOutInteractor: LightsOutInteractor,
     private val notificationsInteractor: ActiveNotificationsInteractor,
     keyguardTransitionInteractor: KeyguardTransitionInteractor,
+    ongoingActivityChipsViewModel: OngoingActivityChipsViewModel,
     @Application coroutineScope: CoroutineScope,
 ) : CollapsedStatusBarViewModel {
     override val isTransitioningFromLockscreenToOccluded: StateFlow<Boolean> =
@@ -91,6 +97,8 @@
             .filter { it.transitionState == TransitionState.STARTED }
             .map {}
 
+    override val ongoingActivityChip = ongoingActivityChipsViewModel.chip
+
     override fun areNotificationsLightsOut(displayId: Int): Flow<Boolean> =
         if (NotificationsLiveDataStoreRefactor.isUnexpectedlyInLegacyMode()) {
             emptyFlow()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt
index ef73e2e..e487780 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt
@@ -28,7 +28,6 @@
 import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createToken
 import com.android.systemui.mediaprojection.taskswitcher.fakeActivityTaskManager
 import com.android.systemui.mediaprojection.taskswitcher.fakeMediaProjectionManager
-import com.android.systemui.mediaprojection.taskswitcher.mediaProjectionManagerRepository
 import com.android.systemui.mediaprojection.taskswitcher.taskSwitcherKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
@@ -45,7 +44,7 @@
     private val fakeMediaProjectionManager = kosmos.fakeMediaProjectionManager
     private val fakeActivityTaskManager = kosmos.fakeActivityTaskManager
 
-    private val repo = kosmos.mediaProjectionManagerRepository
+    private val repo = kosmos.realMediaProjectionRepository
 
     @Test
     fun switchProjectedTask_stateIsUpdatedWithNewTask() =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt
new file mode 100644
index 0000000..0f33b9d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt
@@ -0,0 +1,100 @@
+/*
+ * 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.statusbar.chips.mediaprojection.domain.interactor
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.mediaprojection.data.model.MediaProjectionState
+import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
+import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.chips.ui.viewmodel.mediaProjectionChipInteractor
+import com.android.systemui.util.time.fakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.test.runTest
+
+@SmallTest
+class MediaProjectionChipInteractorTest : SysuiTestCase() {
+    private val kosmos = Kosmos()
+    private val testScope = kosmos.testScope
+    private val mediaProjectionRepo = kosmos.fakeMediaProjectionRepository
+    private val systemClock = kosmos.fakeSystemClock
+
+    private val underTest = kosmos.mediaProjectionChipInteractor
+
+    @Test
+    fun chip_notProjectingState_isHidden() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.NotProjecting
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+        }
+
+    @Test
+    fun chip_singleTaskState_isShownWithIcon() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            mediaProjectionRepo.mediaProjectionState.value =
+                MediaProjectionState.SingleTask(createTask(taskId = 1))
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+            val icon = (latest as OngoingActivityChipModel.Shown).icon
+            assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_cast_connected)
+        }
+
+    @Test
+    fun chip_entireScreenState_isShownWithIcon() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.EntireScreen
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+            val icon = (latest as OngoingActivityChipModel.Shown).icon
+            assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_cast_connected)
+        }
+
+    @Test
+    fun chip_timeResetsOnEachNewShare() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            systemClock.setElapsedRealtime(1234)
+            mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.EntireScreen
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+            assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(1234)
+
+            mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.NotProjecting
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+
+            systemClock.setElapsedRealtime(5678)
+            mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.EntireScreen
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+            assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(5678)
+        }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
index 1260f07..121229c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
@@ -23,6 +23,9 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.mediaprojection.data.model.MediaProjectionState
+import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
+import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
 import com.android.systemui.res.R
 import com.android.systemui.screenrecord.data.model.ScreenRecordModel
 import com.android.systemui.screenrecord.data.repository.screenRecordRepository
@@ -35,13 +38,20 @@
 class OngoingActivityChipsViewModelTest : SysuiTestCase() {
 
     private val kosmos = Kosmos()
+    private val testScope = kosmos.testScope
+
+    private val screenRecordState = kosmos.screenRecordRepository.screenRecordState
+    private val mediaProjectionState = kosmos.fakeMediaProjectionRepository.mediaProjectionState
+    private val callState = kosmos.callChipInteractor.chip
+
     private val underTest = kosmos.ongoingActivityChipsViewModel
 
     @Test
     fun chip_allHidden_hidden() =
-        kosmos.testScope.runTest {
-            kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.DoingNothing
-            kosmos.callChipInteractor.chip.value = OngoingActivityChipModel.Hidden
+        testScope.runTest {
+            screenRecordState.value = ScreenRecordModel.DoingNothing
+            mediaProjectionState.value = MediaProjectionState.NotProjecting
+            callState.value = OngoingActivityChipModel.Hidden
 
             val latest by collectLastValue(underTest.chip)
 
@@ -50,9 +60,10 @@
 
     @Test
     fun chip_screenRecordShow_restHidden_screenRecordShown() =
-        kosmos.testScope.runTest {
-            kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording
-            kosmos.callChipInteractor.chip.value = OngoingActivityChipModel.Hidden
+        testScope.runTest {
+            screenRecordState.value = ScreenRecordModel.Recording
+            mediaProjectionState.value = MediaProjectionState.NotProjecting
+            callState.value = OngoingActivityChipModel.Hidden
 
             val latest by collectLastValue(underTest.chip)
 
@@ -61,15 +72,15 @@
 
     @Test
     fun chip_screenRecordShowAndCallShow_screenRecordShown() =
-        kosmos.testScope.runTest {
-            kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording
+        testScope.runTest {
+            screenRecordState.value = ScreenRecordModel.Recording
 
             val callChip =
                 OngoingActivityChipModel.Shown(
                     Icon.Resource(R.drawable.ic_call, ContentDescription.Loaded("icon")),
                     startTimeMs = 600L,
                 ) {}
-            kosmos.callChipInteractor.chip.value = callChip
+            callState.value = callChip
 
             val latest by collectLastValue(underTest.chip)
 
@@ -77,16 +88,46 @@
         }
 
     @Test
-    fun chip_screenRecordHideAndCallShown_callShown() =
-        kosmos.testScope.runTest {
-            kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.DoingNothing
+    fun chip_screenRecordShowAndMediaProjectionShow_screenRecordShown() =
+        testScope.runTest {
+            screenRecordState.value = ScreenRecordModel.Recording
+            mediaProjectionState.value = MediaProjectionState.EntireScreen
+            callState.value = OngoingActivityChipModel.Hidden
+
+            val latest by collectLastValue(underTest.chip)
+
+            assertIsScreenRecordChip(latest)
+        }
+
+    @Test
+    fun chip_mediaProjectionShowAndCallShow_mediaProjectionShown() =
+        testScope.runTest {
+            screenRecordState.value = ScreenRecordModel.DoingNothing
+            mediaProjectionState.value = MediaProjectionState.EntireScreen
+            val callChip =
+                OngoingActivityChipModel.Shown(
+                    Icon.Resource(R.drawable.ic_call, ContentDescription.Loaded("icon")),
+                    startTimeMs = 600L,
+                ) {}
+            callState.value = callChip
+
+            val latest by collectLastValue(underTest.chip)
+
+            assertIsMediaProjectionChip(latest)
+        }
+
+    @Test
+    fun chip_screenRecordAndMediaProjectionHideAndCallShown_callShown() =
+        testScope.runTest {
+            screenRecordState.value = ScreenRecordModel.DoingNothing
+            mediaProjectionState.value = MediaProjectionState.NotProjecting
 
             val callChip =
                 OngoingActivityChipModel.Shown(
                     Icon.Resource(R.drawable.ic_call, ContentDescription.Loaded("icon")),
                     startTimeMs = 600L,
                 ) {}
-            kosmos.callChipInteractor.chip.value = callChip
+            callState.value = callChip
 
             val latest by collectLastValue(underTest.chip)
 
@@ -95,22 +136,29 @@
 
     @Test
     fun chip_higherPriorityChipAdded_lowerPriorityChipReplaced() =
-        kosmos.testScope.runTest {
+        testScope.runTest {
             // Start with just the lower priority call chip
             val callChip =
                 OngoingActivityChipModel.Shown(
                     Icon.Resource(R.drawable.ic_call, ContentDescription.Loaded("icon")),
                     startTimeMs = 600L,
                 ) {}
-            kosmos.callChipInteractor.chip.value = callChip
-            kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.DoingNothing
+            callState.value = callChip
+            mediaProjectionState.value = MediaProjectionState.NotProjecting
+            screenRecordState.value = ScreenRecordModel.DoingNothing
 
             val latest by collectLastValue(underTest.chip)
 
             assertThat(latest).isEqualTo(callChip)
 
+            // WHEN the higher priority media projection chip is added
+            mediaProjectionState.value = MediaProjectionState.SingleTask(createTask(taskId = 1))
+
+            // THEN the higher priority media projection chip is used
+            assertIsMediaProjectionChip(latest)
+
             // WHEN the higher priority screen record chip is added
-            kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording
+            screenRecordState.value = ScreenRecordModel.Recording
 
             // THEN the higher priority screen record chip is used
             assertIsScreenRecordChip(latest)
@@ -118,31 +166,47 @@
 
     @Test
     fun chip_highestPriorityChipRemoved_showsNextPriorityChip() =
-        kosmos.testScope.runTest {
-            // Start with both the higher priority screen record chip and lower priority call chip
-            kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording
+        testScope.runTest {
+            // WHEN all chips are active
+            screenRecordState.value = ScreenRecordModel.Recording
+            mediaProjectionState.value = MediaProjectionState.EntireScreen
 
             val callChip =
                 OngoingActivityChipModel.Shown(
                     Icon.Resource(R.drawable.ic_call, ContentDescription.Loaded("icon")),
                     startTimeMs = 600L,
                 ) {}
-            kosmos.callChipInteractor.chip.value = callChip
+            callState.value = callChip
 
             val latest by collectLastValue(underTest.chip)
 
+            // THEN the highest priority screen record is used
             assertIsScreenRecordChip(latest)
 
             // WHEN the higher priority screen record is removed
-            kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.DoingNothing
+            screenRecordState.value = ScreenRecordModel.DoingNothing
+
+            // THEN the lower priority media projection is used
+            assertIsMediaProjectionChip(latest)
+
+            // WHEN the higher priority media projection is removed
+            mediaProjectionState.value = MediaProjectionState.NotProjecting
 
             // THEN the lower priority call is used
             assertThat(latest).isEqualTo(callChip)
         }
 
-    private fun assertIsScreenRecordChip(latest: OngoingActivityChipModel?) {
-        assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
-        val icon = (latest as OngoingActivityChipModel.Shown).icon
-        assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.stat_sys_screen_record)
+    companion object {
+        fun assertIsScreenRecordChip(latest: OngoingActivityChipModel?) {
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+            val icon = (latest as OngoingActivityChipModel.Shown).icon
+            assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.stat_sys_screen_record)
+        }
+
+        fun assertIsMediaProjectionChip(latest: OngoingActivityChipModel?) {
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+            val icon = (latest as OngoingActivityChipModel.Shown).icon
+            assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_cast_connected)
+        }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLoggerTest.kt
index 87d813c..e0f1e1a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLoggerTest.kt
@@ -63,7 +63,7 @@
             StatusBarVisibilityModel(
                 showClock = false,
                 showNotificationIcons = true,
-                showOngoingCallChip = false,
+                showOngoingActivityChip = false,
                 showSystemInfo = true,
             )
         )
@@ -74,7 +74,7 @@
 
         assertThat(actualString).contains("showClock=false")
         assertThat(actualString).contains("showNotificationIcons=true")
-        assertThat(actualString).contains("showOngoingCallChip=false")
+        assertThat(actualString).contains("showOngoingActivityChip=false")
         assertThat(actualString).contains("showSystemInfo=true")
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index ff182ad..ee27cea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -16,6 +16,7 @@
 
 import static android.view.Display.DEFAULT_DISPLAY;
 
+import static com.android.systemui.Flags.FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS;
 import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_CLOSED;
 import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_OPEN;
 
@@ -33,6 +34,8 @@
 import android.content.Context;
 import android.os.Bundle;
 import android.os.UserHandle;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
 import android.provider.Settings;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
@@ -46,7 +49,6 @@
 import com.android.systemui.animation.AnimatorTestRule;
 import com.android.systemui.demomode.DemoModeController;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.kosmos.KosmosJavaAdapter;
 import com.android.systemui.log.LogBuffer;
 import com.android.systemui.log.LogcatEchoTracker;
 import com.android.systemui.plugins.DarkIconDispatcher;
@@ -56,7 +58,6 @@
 import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.OperatorNameViewController;
-import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModel;
 import com.android.systemui.statusbar.disableflags.DisableFlagsLogger;
 import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
 import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerStatusBarViewBinder;
@@ -92,11 +93,9 @@
 @RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
-    private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter();
     private NotificationIconAreaController mMockNotificationAreaController;
     private ShadeExpansionStateManager mShadeExpansionStateManager;
     private OngoingCallController mOngoingCallController;
-    private OngoingActivityChipsViewModel mOngoingActivityChipsViewModel;
     private SystemStatusAnimationScheduler mAnimationScheduler;
     private StatusBarLocationPublisher mLocationPublisher;
     // Set in instantiate()
@@ -421,6 +420,7 @@
     }
 
     @Test
+    @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
     public void disable_noOngoingCall_chipHidden() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -433,6 +433,7 @@
     }
 
     @Test
+    @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
     public void disable_hasOngoingCall_chipDisplayedAndNotificationIconsHidden() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -446,6 +447,7 @@
     }
 
     @Test
+    @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
     public void disable_hasOngoingCallButNotificationIconsDisabled_chipHidden() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -459,6 +461,7 @@
     }
 
     @Test
+    @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
     public void disable_hasOngoingCallButAlsoHun_chipHidden() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -472,6 +475,7 @@
     }
 
     @Test
+    @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
     public void disable_ongoingCallEnded_chipHidden() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -498,8 +502,11 @@
     }
 
     @Test
+    @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
     public void disable_hasOngoingCall_hidesNotifsWithoutAnimation() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+        // Enable animations for testing so that we can verify we still aren't animating
+        fragment.enableAnimationsForTesting();
         fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
 
         // Ongoing call started
@@ -512,6 +519,161 @@
     }
 
     @Test
+    @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+    public void screenSharingChipsDisabled_ignoresNewCallback() {
+        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+
+        // WHEN there *is* an ongoing call via old callback
+        when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
+        fragment.disable(DEFAULT_DISPLAY, 0, 0, true);
+
+        // WHEN there's *no* ongoing activity via new callback
+        mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
+                /* hasOngoingActivity= */ false);
+
+        // THEN the old callback value is used, so the view is shown
+        assertEquals(View.VISIBLE,
+                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+
+        // WHEN there's *no* ongoing call via old callback
+        when(mOngoingCallController.hasOngoingCall()).thenReturn(false);
+        fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
+
+        // WHEN there *is* an ongoing activity via new callback
+        mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
+                /* hasOngoingActivity= */ true);
+
+        // THEN the old callback value is used, so the view is hidden
+        assertEquals(View.GONE,
+                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+    }
+
+    @Test
+    @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+    public void noOngoingActivity_chipHidden() {
+        resumeAndGetFragment();
+
+        // TODO(b/332662551): We *should* be able to just set a value on
+        // mCollapsedStatusBarViewModel.getOngoingActivityChip() instead of manually invoking the
+        // listener, but I'm unable to get the fragment to get attached so that the binder starts
+        // listening to flows.
+        mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
+                /* hasOngoingActivity= */ false);
+
+        assertEquals(View.GONE,
+                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+    }
+
+    @Test
+    @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+    public void hasOngoingActivity_chipDisplayedAndNotificationIconsHidden() {
+        resumeAndGetFragment();
+
+        mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
+                /* hasOngoingActivity= */ true);
+
+        assertEquals(View.VISIBLE,
+                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+        assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
+    }
+
+    @Test
+    @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+    public void hasOngoingActivityButNotificationIconsDisabled_chipHidden() {
+        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+
+        mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
+                /* hasOngoingActivity= */ true);
+
+        fragment.disable(DEFAULT_DISPLAY,
+                StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
+
+        assertEquals(View.GONE,
+                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+    }
+
+    @Test
+    @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+    public void hasOngoingActivityButAlsoHun_chipHidden() {
+        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+
+        mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
+                /* hasOngoingActivity= */ true);
+        when(mHeadsUpAppearanceController.shouldBeVisible()).thenReturn(true);
+
+        fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
+
+        assertEquals(View.GONE,
+                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+    }
+
+    @Test
+    @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+    public void ongoingActivityEnded_chipHidden() {
+        resumeAndGetFragment();
+
+        // Ongoing activity started
+        mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
+                /* hasOngoingActivity= */ true);
+
+        assertEquals(View.VISIBLE,
+                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+
+        // Ongoing activity ended
+        mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
+                /* hasOngoingActivity= */ false);
+
+        assertEquals(View.GONE,
+                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+    }
+
+    @Test
+    @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+    public void hasOngoingActivity_hidesNotifsWithoutAnimation() {
+        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+        // Enable animations for testing so that we can verify we still aren't animating
+        fragment.enableAnimationsForTesting();
+
+        // Ongoing call started
+        mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
+                /* hasOngoingActivity= */ true);
+
+        // Notification area is hidden without delay
+        assertEquals(0f, getNotificationAreaView().getAlpha(), 0.01);
+        assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
+    }
+
+    @Test
+    @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+    public void screenSharingChipsEnabled_ignoresOngoingCallController() {
+        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+
+        // WHEN there *is* an ongoing call via old callback
+        when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
+        fragment.disable(DEFAULT_DISPLAY, 0, 0, true);
+
+        // WHEN there's *no* ongoing activity via new callback
+        mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
+                /* hasOngoingActivity= */ false);
+
+        // THEN the new callback value is used, so the view is hidden
+        assertEquals(View.GONE,
+                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+
+        // WHEN there's *no* ongoing call via old callback
+        when(mOngoingCallController.hasOngoingCall()).thenReturn(false);
+        fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
+
+        // WHEN there *is* an ongoing activity via new callback
+        mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
+                /* hasOngoingActivity= */ true);
+
+        // THEN the new callback value is used, so the view is shown
+        assertEquals(View.VISIBLE,
+                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+    }
+
+    @Test
     public void disable_isDozing_clockAndSystemInfoVisible() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
         when(mStatusBarStateController.isDozing()).thenReturn(true);
@@ -670,7 +832,6 @@
         MockitoAnnotations.initMocks(this);
         setUpDaggerComponent();
         mOngoingCallController = mock(OngoingCallController.class);
-        mOngoingActivityChipsViewModel = mKosmos.getOngoingActivityChipsViewModel();
         mAnimationScheduler = mock(SystemStatusAnimationScheduler.class);
         mLocationPublisher = mock(StatusBarLocationPublisher.class);
         mStatusBarIconController = mock(StatusBarIconController.class);
@@ -691,7 +852,6 @@
         return new CollapsedStatusBarFragment(
                 mStatusBarFragmentComponentFactory,
                 mOngoingCallController,
-                mOngoingActivityChipsViewModel,
                 mAnimationScheduler,
                 mLocationPublisher,
                 mMockNotificationAreaController,
@@ -773,7 +933,9 @@
     private CollapsedStatusBarFragment resumeAndGetFragment() {
         mFragments.dispatchResume();
         processAllMessages();
-        return (CollapsedStatusBarFragment) mFragment;
+        CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+        fragment.disableAnimationsForTesting();
+        return fragment;
     }
 
     private View getUserChipView() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModelTest.kt
index 8e789cb..022b5d2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModelTest.kt
@@ -36,7 +36,7 @@
             StatusBarVisibilityModel(
                 showClock = true,
                 showNotificationIcons = true,
-                showOngoingCallChip = true,
+                showOngoingActivityChip = true,
                 showSystemInfo = true,
             )
 
@@ -72,17 +72,17 @@
     }
 
     @Test
-    fun createModelFromFlags_ongoingCallChipNotDisabled_showOngoingCallChipTrue() {
+    fun createModelFromFlags_ongoingCallChipNotDisabled_showOngoingActivityChipTrue() {
         val result = createModelFromFlags(disabled1 = 0, disabled2 = 0)
 
-        assertThat(result.showOngoingCallChip).isTrue()
+        assertThat(result.showOngoingActivityChip).isTrue()
     }
 
     @Test
-    fun createModelFromFlags_ongoingCallChipDisabled_showOngoingCallChipFalse() {
+    fun createModelFromFlags_ongoingCallChipDisabled_showOngoingActivityChipFalse() {
         val result = createModelFromFlags(disabled1 = DISABLE_ONGOING_CALL_CHIP, disabled2 = 0)
 
-        assertThat(result.showOngoingCallChip).isFalse()
+        assertThat(result.showOngoingActivityChip).isFalse()
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
index 606feab..c9fe449 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
@@ -32,6 +32,14 @@
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.log.assertLogsWtf
+import com.android.systemui.mediaprojection.data.model.MediaProjectionState
+import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
+import com.android.systemui.screenrecord.data.model.ScreenRecordModel
+import com.android.systemui.screenrecord.data.repository.screenRecordRepository
+import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsMediaProjectionChip
+import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsScreenRecordChip
+import com.android.systemui.statusbar.chips.ui.viewmodel.ongoingActivityChipsViewModel
 import com.android.systemui.statusbar.data.model.StatusBarMode
 import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository.Companion.DISPLAY_ID
 import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository
@@ -65,6 +73,7 @@
             kosmos.lightsOutInteractor,
             kosmos.activeNotificationsInteractor,
             kosmos.keyguardTransitionInteractor,
+            kosmos.ongoingActivityChipsViewModel,
             kosmos.applicationCoroutineScope,
         )
 
@@ -382,6 +391,25 @@
             }
         }
 
+    @Test
+    fun ongoingActivityChip_matchesViewModel() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.ongoingActivityChip)
+
+            kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording
+
+            assertIsScreenRecordChip(latest)
+
+            kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.DoingNothing
+
+            assertThat(latest).isEqualTo(OngoingActivityChipModel.Hidden)
+
+            kosmos.fakeMediaProjectionRepository.mediaProjectionState.value =
+                MediaProjectionState.EntireScreen
+
+            assertIsMediaProjectionChip(latest)
+        }
+
     private fun activeNotificationsStore(notifications: List<ActiveNotificationModel>) =
         ActiveNotificationsStore.Builder()
             .apply { notifications.forEach(::addIndividualNotif) }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt
index bc50f79..c3c9907 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel
 
+import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -27,6 +28,9 @@
 
     override val transitionFromLockscreenToDreamStartedEvent = MutableSharedFlow<Unit>()
 
+    override val ongoingActivityChip: MutableStateFlow<OngoingActivityChipModel> =
+        MutableStateFlow(OngoingActivityChipModel.Hidden)
+
     override fun areNotificationsLightsOut(displayId: Int): Flow<Boolean> = areNotificationLightsOut
 
     fun setNotificationLightsOut(lightsOut: Boolean) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiBaseFragmentTest.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiBaseFragmentTest.java
index e470406..a581993 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiBaseFragmentTest.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiBaseFragmentTest.java
@@ -19,6 +19,7 @@
 
 import android.app.Fragment;
 import android.app.Instrumentation;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.testing.BaseFragmentTest;
 import android.testing.DexmakerShareClassLoaderRule;
 
@@ -31,6 +32,7 @@
 import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.Before;
+import org.junit.ClassRule;
 import org.junit.Rule;
 import org.mockito.Mockito;
 
@@ -43,6 +45,12 @@
     @Rule
     public final SysuiLeakCheck mLeakCheck = new SysuiLeakCheck();
 
+    @ClassRule
+    public static final SetFlagsRule.ClassRule mSetFlagsClassRule =
+            new SetFlagsRule.ClassRule(
+                    com.android.systemui.Flags.class);
+    @Rule public final SetFlagsRule mSetFlagsRule = mSetFlagsClassRule.createSetFlagsRule();
+
     @Rule
     public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
             new DexmakerShareClassLoaderRule();
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt
new file mode 100644
index 0000000..c4365c9
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt
@@ -0,0 +1,28 @@
+/*
+ * 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.mediaprojection.data.repository
+
+import android.app.ActivityManager
+import com.android.systemui.mediaprojection.data.model.MediaProjectionState
+import kotlinx.coroutines.flow.MutableStateFlow
+
+class FakeMediaProjectionRepository : MediaProjectionRepository {
+    override suspend fun switchProjectedTask(task: ActivityManager.RunningTaskInfo) {}
+
+    override val mediaProjectionState: MutableStateFlow<MediaProjectionState> =
+        MutableStateFlow(MediaProjectionState.NotProjecting)
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepositoryKosmos.kt
new file mode 100644
index 0000000..f253e94
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepositoryKosmos.kt
@@ -0,0 +1,39 @@
+/*
+ * 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.mediaprojection.data.repository
+
+import android.os.Handler
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.mediaprojection.taskswitcher.activityTaskManagerTasksRepository
+import com.android.systemui.mediaprojection.taskswitcher.fakeMediaProjectionManager
+
+val Kosmos.fakeMediaProjectionRepository: FakeMediaProjectionRepository by
+    Kosmos.Fixture { FakeMediaProjectionRepository() }
+
+val Kosmos.realMediaProjectionRepository by
+    Kosmos.Fixture {
+        MediaProjectionManagerRepository(
+            mediaProjectionManager = fakeMediaProjectionManager.mediaProjectionManager,
+            handler = Handler.getMain(),
+            applicationScope = applicationCoroutineScope,
+            tasksRepository = activityTaskManagerTasksRepository,
+            backgroundDispatcher = testDispatcher,
+            mediaProjectionServiceHelper = fakeMediaProjectionManager.helper,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/taskswitcher/TaskSwitcherKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/taskswitcher/TaskSwitcherKosmos.kt
index 5b1f95a..5acadd7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/taskswitcher/TaskSwitcherKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/taskswitcher/TaskSwitcherKosmos.kt
@@ -16,11 +16,10 @@
 
 package com.android.systemui.mediaprojection.taskswitcher
 
-import android.os.Handler
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testDispatcher
-import com.android.systemui.mediaprojection.data.repository.MediaProjectionManagerRepository
+import com.android.systemui.mediaprojection.data.repository.realMediaProjectionRepository
 import com.android.systemui.mediaprojection.taskswitcher.data.repository.ActivityTaskManagerTasksRepository
 import com.android.systemui.mediaprojection.taskswitcher.domain.interactor.TaskSwitchInteractor
 import com.android.systemui.mediaprojection.taskswitcher.ui.viewmodel.TaskSwitcherNotificationViewModel
@@ -40,21 +39,9 @@
         )
     }
 
-val Kosmos.mediaProjectionManagerRepository by
-    Kosmos.Fixture {
-        MediaProjectionManagerRepository(
-            mediaProjectionManager = fakeMediaProjectionManager.mediaProjectionManager,
-            handler = Handler.getMain(),
-            applicationScope = applicationCoroutineScope,
-            tasksRepository = activityTaskManagerTasksRepository,
-            backgroundDispatcher = testDispatcher,
-            mediaProjectionServiceHelper = fakeMediaProjectionManager.helper,
-        )
-    }
-
 val Kosmos.taskSwitcherInteractor by
     Kosmos.Fixture {
-        TaskSwitchInteractor(mediaProjectionManagerRepository, activityTaskManagerTasksRepository)
+        TaskSwitchInteractor(realMediaProjectionRepository, activityTaskManagerTasksRepository)
     }
 
 val Kosmos.taskSwitcherViewModel by
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt
index 9e02df9..88bde2e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt
@@ -19,7 +19,9 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
 import com.android.systemui.screenrecord.data.repository.screenRecordRepository
+import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractor
 import com.android.systemui.statusbar.chips.screenrecord.domain.interactor.ScreenRecordChipInteractor
 import com.android.systemui.util.time.fakeSystemClock
 
@@ -32,6 +34,15 @@
         )
     }
 
+val Kosmos.mediaProjectionChipInteractor: MediaProjectionChipInteractor by
+    Kosmos.Fixture {
+        MediaProjectionChipInteractor(
+            scope = applicationCoroutineScope,
+            mediaProjectionRepository = fakeMediaProjectionRepository,
+            systemClock = fakeSystemClock,
+        )
+    }
+
 val Kosmos.callChipInteractor: FakeCallChipInteractor by Kosmos.Fixture { FakeCallChipInteractor() }
 
 val Kosmos.ongoingActivityChipsViewModel: OngoingActivityChipsViewModel by
@@ -39,6 +50,7 @@
         OngoingActivityChipsViewModel(
             testScope.backgroundScope,
             screenRecordChipInteractor = screenRecordChipInteractor,
+            mediaProjectionChipInteractor = mediaProjectionChipInteractor,
             callChipInteractor = callChipInteractor,
         )
     }