Loading packages/SystemUI/src/com/android/systemui/screencapture/sharescreen/largescreen/ui/compose/PreShareToolbar.kt +35 −19 Original line number Diff line number Diff line Loading @@ -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), Loading packages/SystemUI/src/com/android/systemui/screencapture/sharescreen/largescreen/ui/compose/PreShareUI.kt 0 → 100644 +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. } packages/SystemUI/src/com/android/systemui/screencapture/sharescreen/largescreen/ui/viewmodel/PreShareViewModel.kt 0 → 100644 +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 } } packages/SystemUI/tests/src/com/android/systemui/screencapture/sharescreen/largescreen/ui/viewmodel/PreShareViewModelTest.kt 0 → 100644 +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) } } packages/SystemUI/tests/utils/src/com/android/systemui/screencapture/sharescreen/largescreen/ui/viewmodel/PreShareViewModelKosmos.kt 0 → 100644 +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) } Loading
packages/SystemUI/src/com/android/systemui/screencapture/sharescreen/largescreen/ui/compose/PreShareToolbar.kt +35 −19 Original line number Diff line number Diff line Loading @@ -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), Loading
packages/SystemUI/src/com/android/systemui/screencapture/sharescreen/largescreen/ui/compose/PreShareUI.kt 0 → 100644 +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. }
packages/SystemUI/src/com/android/systemui/screencapture/sharescreen/largescreen/ui/viewmodel/PreShareViewModel.kt 0 → 100644 +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 } }
packages/SystemUI/tests/src/com/android/systemui/screencapture/sharescreen/largescreen/ui/viewmodel/PreShareViewModelTest.kt 0 → 100644 +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) } }
packages/SystemUI/tests/utils/src/com/android/systemui/screencapture/sharescreen/largescreen/ui/viewmodel/PreShareViewModelKosmos.kt 0 → 100644 +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) }