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

Commit 15148029 authored by Danny Wang's avatar Danny Wang Committed by Android (Google) Code Review
Browse files

Merge "Screen Sharing: Add PreShareViewModel and State" into main

parents 102bcf22 1d2a5c00
Loading
Loading
Loading
Loading
+35 −19
Original line number Diff line number Diff line
@@ -25,42 +25,58 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon as IconModel
import com.android.systemui.res.R
import com.android.systemui.screencapture.common.ui.compose.PrimaryButton
import com.android.systemui.screencapture.common.ui.compose.RadioButtonGroup
import com.android.systemui.screencapture.common.ui.compose.RadioButtonGroupItem
import com.android.systemui.screencapture.common.ui.compose.Toolbar
import com.android.systemui.screencapture.common.ui.compose.loadIcon
import com.android.systemui.screencapture.sharescreen.largescreen.ui.viewmodel.PreShareViewModel
import com.android.systemui.screencapture.sharescreen.largescreen.ui.viewmodel.ScreenShareTarget

/** TODO(b/433836686): Inject ScreenShareViewModel */
@Composable
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
fun PreShareToolbar(expanded: Boolean, onCloseClick: () -> Unit, modifier: Modifier = Modifier) {
    // TODO(b/433836686): Preload icons in the view model to avoid loading icons in UI thread and
    // improve performance
    val screenShareTabIcon =
        IconModel.Resource(res = R.drawable.ic_screen_capture_tab, contentDescription = null)
    val screenShareWindowIcon =
        IconModel.Resource(res = R.drawable.ic_screen_capture_window, contentDescription = null)

    val captureShareButtonItems =
fun PreShareToolbar(
    viewModel: PreShareViewModel,
    expanded: Boolean,
    onCloseClick: () -> Unit,
    modifier: Modifier = Modifier,
) {
    val shareTargetButtonItems =
        listOf(
            RadioButtonGroupItem(icon = screenShareTabIcon, isSelected = true, onClick = {}),
            RadioButtonGroupItem(icon = screenShareWindowIcon, isSelected = false, onClick = {}),
            RadioButtonGroupItem(
                icon =
                    loadIcon(
                        viewModel = viewModel,
                        resId = R.drawable.ic_screen_capture_tab,
                        contentDescription = null,
                    ),
                isSelected = viewModel.selectedScreenShareTarget == ScreenShareTarget.TAB,
                onClick = { viewModel.onTargetSelected(ScreenShareTarget.TAB) },
            ),
            RadioButtonGroupItem(
                icon =
                    loadIcon(
                        viewModel = viewModel,
                        resId = R.drawable.ic_screen_capture_window,
                        contentDescription = null,
                    ),
                isSelected = viewModel.selectedScreenShareTarget == ScreenShareTarget.APP_WINDOW,
                onClick = { viewModel.onTargetSelected(ScreenShareTarget.APP_WINDOW) },
            ),
        )

    Toolbar(expanded = expanded, onCloseClick = onCloseClick, modifier = modifier) {
        Row {
            // TODO(b/433836686): Use state from ViewModel for selected index
            RadioButtonGroup(items = captureShareButtonItems)
            RadioButtonGroup(items = shareTargetButtonItems)

            Spacer(Modifier.size(16.dp))

            PrimaryButton(
                icon =
                    IconModel.Resource(
                        res = R.drawable.ic_present_to_all,
                        contentDescription =
                    loadIcon(
                        viewModel = viewModel,
                        resId = R.drawable.ic_present_to_all,
                        ContentDescription.Resource(R.string.screen_share_toolbar_share_button),
                    ),
                text = stringResource(R.string.screen_share_toolbar_share_button),
+32 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.screencapture.sharescreen.largescreen.ui.compose

import androidx.compose.runtime.Composable
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.screencapture.sharescreen.largescreen.ui.viewmodel.PreShareViewModel

/** Main component for the screen share UI. */
@Composable
fun PreShareUI(preShareViewModelFactory: PreShareViewModel.Factory) {
    val viewModel: PreShareViewModel =
        rememberViewModel("PreShareViewModel") { preShareViewModelFactory.create() }

    PreShareToolbar(viewModel = viewModel, expanded = true, onCloseClick = {})

    // TODO: Add PreShareSelector here.
}
+53 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.screencapture.sharescreen.largescreen.ui.viewmodel

import androidx.compose.runtime.getValue
import com.android.systemui.lifecycle.HydratedActivatable
import com.android.systemui.screencapture.common.ui.viewmodel.DrawableLoaderViewModel
import com.android.systemui.screencapture.common.ui.viewmodel.DrawableLoaderViewModelImpl
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.flow.MutableStateFlow

// TODO(b/423708493): Add FULLSCREEN Sharing target.
enum class ScreenShareTarget {
    APP_WINDOW,
    TAB,
}

/** Models UI state for the Screen Share feature. */
class PreShareViewModel
@AssistedInject
constructor(private val drawableLoaderViewModelImpl: DrawableLoaderViewModelImpl) :
    HydratedActivatable(), DrawableLoaderViewModel by drawableLoaderViewModelImpl {
    // The private, mutable source of truth for the selected target.
    private val selectedScreenShareTargetSource = MutableStateFlow(ScreenShareTarget.APP_WINDOW)

    val selectedScreenShareTarget by
        selectedScreenShareTargetSource.hydratedStateOf(traceName = "selectedScreenShareTarget")

    // Called by the UI when a new sharing target is selected by the user.
    fun onTargetSelected(target: ScreenShareTarget) {
        selectedScreenShareTargetSource.value = target
    }

    @AssistedFactory
    interface Factory {
        fun create(): PreShareViewModel
    }
}
+58 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.screencapture.sharescreen.largescreen.ui.viewmodel

import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.lifecycle.activateIn
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidJUnit4::class)
class PreShareViewModelTest : SysuiTestCase() {
    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
    private val testScope = kosmos.testScope

    private val viewModel: PreShareViewModel by lazy { kosmos.preShareViewModel }

    @Before
    fun setUp() {
        viewModel.activateIn(testScope)
    }

    @Test
    fun initialState() =
        kosmos.runTest {
            // Assert that the initial values are as expected upon creation and activation.
            assertThat(viewModel.selectedScreenShareTarget).isEqualTo(ScreenShareTarget.APP_WINDOW)
        }

    @Test
    fun onTargetSelected_updateScreenShareTarget() =
        kosmos.runTest {
            viewModel.onTargetSelected(ScreenShareTarget.TAB)
            assertThat(viewModel.selectedScreenShareTarget).isEqualTo(ScreenShareTarget.TAB)
        }
}
+25 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.screencapture.sharescreen.largescreen.ui.viewmodel

import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.screencapture.common.ui.viewmodel.drawableLoaderViewModelImpl

val Kosmos.preShareViewModel by Fixture {
    PreShareViewModel(drawableLoaderViewModelImpl = drawableLoaderViewModelImpl)
}