Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1423aeec authored by Caitlin Shkuratov's avatar Caitlin Shkuratov Committed by Android (Google) Code Review
Browse files

Merge "[SB][Screen Chips] Show cast chip even if audio-only casting." into main

parents 16d4a5ec 44f67875
Loading
Loading
Loading
Loading
+8 −4
Original line number Diff line number Diff line
@@ -341,13 +341,17 @@
    <string name="share_to_app_stop_dialog_button">Stop sharing</string>

    <!-- Content description for the status bar chip shown to the user when they're casting their screen to a different device [CHAR LIMIT=NONE] -->
    <string name="cast_to_other_device_chip_accessibility_label">Casting screen</string>
    <string name="cast_screen_to_other_device_chip_accessibility_label">Casting screen</string>
    <!-- Title for a dialog shown to the user that will let them stop casting their screen to a different device [CHAR LIMIT=50] -->
    <string name="cast_to_other_device_stop_dialog_title">Stop casting screen?</string>
    <string name="cast_screen_to_other_device_stop_dialog_title">Stop casting screen?</string>
    <!-- Title for a dialog shown to the user that will let them stop casting to a different device [CHAR LIMIT=50] -->
    <string name="cast_to_other_device_stop_dialog_title">Stop casting?</string>
    <!-- Text telling a user that they will stop casting their screen to a different device if they click the "Stop casting" button [CHAR LIMIT=100] -->
    <string name="cast_to_other_device_stop_dialog_message">You will stop casting your screen</string>
    <string name="cast_screen_to_other_device_stop_dialog_message">You will stop casting your screen</string>
    <!-- Text telling a user that they will stop casting the contents of the specified [app_name] to a different device if they click the "Stop casting" button. Note that the app name will appear in bold.  [CHAR LIMIT=100] -->
    <string name="cast_to_other_device_stop_dialog_message_specific_app">You will stop casting &lt;b><xliff:g id="app_name" example="Photos App">%1$s</xliff:g>&lt;/b></string>
    <string name="cast_screen_to_other_device_stop_dialog_message_specific_app">You will stop casting &lt;b><xliff:g id="app_name" example="Photos App">%1$s</xliff:g>&lt;/b></string>
    <!-- Text telling a user that they're currently casting to a different device [CHAR LIMIT=100] -->
    <string name="cast_to_other_device_stop_dialog_message">You\'re currently casting</string>
    <!-- Button to stop screen casting to a different device [CHAR LIMIT=35] -->
    <string name="cast_to_other_device_stop_dialog_button">Stop casting</string>

+2 −0
Original line number Diff line number Diff line
@@ -74,6 +74,7 @@ import com.android.systemui.log.table.TableLogBuffer;
import com.android.systemui.mediaprojection.MediaProjectionModule;
import com.android.systemui.mediaprojection.appselector.MediaProjectionActivitiesModule;
import com.android.systemui.mediaprojection.taskswitcher.MediaProjectionTaskSwitcherModule;
import com.android.systemui.mediarouter.MediaRouterModule;
import com.android.systemui.model.SceneContainerPlugin;
import com.android.systemui.model.SysUiState;
import com.android.systemui.motiontool.MotionToolModule;
@@ -220,6 +221,7 @@ import javax.inject.Named;
        MediaProjectionActivitiesModule.class,
        MediaProjectionModule.class,
        MediaProjectionTaskSwitcherModule.class,
        MediaRouterModule.class,
        MotionToolModule.class,
        NotificationIconAreaControllerModule.class,
        PeopleHubModule.class,
+27 −0
Original line number Diff line number Diff line
/*
 * 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.mediarouter

import com.android.systemui.mediarouter.data.repository.MediaRouterRepository
import com.android.systemui.mediarouter.data.repository.MediaRouterRepositoryImpl
import dagger.Binds
import dagger.Module

@Module
interface MediaRouterModule {
    @Binds fun mediaRouterRepository(impl: MediaRouterRepositoryImpl): MediaRouterRepository
}
+65 −0
Original line number Diff line number Diff line
/*
 * 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.mediarouter.data.repository

import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.statusbar.policy.CastController
import com.android.systemui.statusbar.policy.CastDevice
import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.stateIn

/** A repository for data coming from MediaRouter APIs. */
interface MediaRouterRepository {
    /** A list of the cast devices that MediaRouter is currently aware of. */
    val castDevices: StateFlow<List<CastDevice>>

    /** Stops the cast to the given device. */
    fun stopCasting(device: CastDevice)
}

@SysUISingleton
class MediaRouterRepositoryImpl
@Inject
constructor(
    @Application private val scope: CoroutineScope,
    private val castController: CastController,
) : MediaRouterRepository {
    override val castDevices: StateFlow<List<CastDevice>> =
        conflatedCallbackFlow {
                val callback =
                    CastController.Callback {
                        val mediaRouterCastDevices =
                            castController.castDevices.filter {
                                it.origin == CastDevice.CastOrigin.MediaRouter
                            }
                        trySend(mediaRouterCastDevices)
                    }
                castController.addCallback(callback)
                awaitClose { castController.removeCallback(callback) }
            }
            .stateIn(scope, SharingStarted.WhileSubscribed(), emptyList())

    override fun stopCasting(device: CastDevice) {
        castController.stopCasting(device)
    }
}
+63 −0
Original line number Diff line number Diff line
/*
 * 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.casttootherdevice.domain.interactor

import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.mediarouter.data.repository.MediaRouterRepository
import com.android.systemui.statusbar.chips.casttootherdevice.domain.model.MediaRouterCastModel
import com.android.systemui.statusbar.policy.CastDevice
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 MediaRouter events, used to show the cast-audio-to-other-device chip in the status
 * bar.
 */
@SysUISingleton
class MediaRouterChipInteractor
@Inject
constructor(
    @Application private val scope: CoroutineScope,
    private val mediaRouterRepository: MediaRouterRepository,
) {
    private val activeCastDevice: StateFlow<CastDevice?> =
        mediaRouterRepository.castDevices
            .map { allDevices -> allDevices.firstOrNull { it.isCasting } }
            .stateIn(scope, SharingStarted.WhileSubscribed(), null)

    /** The current casting state, according to MediaRouter APIs. */
    val mediaRouterCastingState: StateFlow<MediaRouterCastModel> =
        activeCastDevice
            .map {
                if (it != null) {
                    MediaRouterCastModel.Casting
                } else {
                    MediaRouterCastModel.DoingNothing
                }
            }
            .stateIn(scope, SharingStarted.WhileSubscribed(), MediaRouterCastModel.DoingNothing)

    /** Stops the currently active MediaRouter cast. */
    fun stopCasting() {
        activeCastDevice.value?.let { mediaRouterRepository.stopCasting(it) }
    }
}
Loading