diff options
| author | 2024-02-28 16:17:50 +0100 | |
|---|---|---|
| committer | 2024-03-01 14:59:36 +0100 | |
| commit | e8c26a68f55a36c7cbecc42fff1b3a5f7648661f (patch) | |
| tree | f54edbeb8bddf63d8592fe1085adcf5d23b32231 | |
| parent | 016b4dae2fdddcf8604bb6979c6540e99cf22b19 (diff) | |
Remove (Base)ComposeFacade
Bug: 295304993
Test: mp sysuig
Flag: N/A
Change-Id: I4f70577387ae3e76a807d7c4ee4b04eca481a5a0
25 files changed, 301 insertions, 616 deletions
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt deleted file mode 100644 index 76931a2b4d41..000000000000 --- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright (C) 2022 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.compose - -import android.content.Context -import android.graphics.Point -import android.view.View -import android.view.WindowInsets -import androidx.activity.ComponentActivity -import androidx.activity.compose.setContent -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.ComposeView -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp -import androidx.lifecycle.LifecycleOwner -import com.android.compose.theme.LocalAndroidColorScheme -import com.android.compose.theme.PlatformTheme -import com.android.compose.ui.platform.DensityAwareComposeView -import com.android.internal.policy.ScreenDecorationsUtils -import com.android.systemui.bouncer.ui.BouncerDialogFactory -import com.android.systemui.bouncer.ui.composable.BouncerContent -import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel -import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation -import com.android.systemui.common.ui.compose.windowinsets.DisplayCutout -import com.android.systemui.common.ui.compose.windowinsets.ScreenDecorProvider -import com.android.systemui.communal.ui.compose.CommunalContainer -import com.android.systemui.communal.ui.compose.CommunalHub -import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel -import com.android.systemui.communal.ui.viewmodel.CommunalViewModel -import com.android.systemui.communal.widgets.WidgetConfigurator -import com.android.systemui.keyboard.stickykeys.ui.view.createStickyKeyIndicatorView -import com.android.systemui.keyboard.stickykeys.ui.viewmodel.StickyKeysIndicatorViewModel -import com.android.systemui.keyguard.shared.model.LockscreenSceneBlueprint -import com.android.systemui.keyguard.ui.composable.LockscreenContent -import com.android.systemui.keyguard.ui.composable.blueprint.ComposableLockscreenSceneBlueprint -import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel -import com.android.systemui.people.ui.compose.PeopleScreen -import com.android.systemui.people.ui.viewmodel.PeopleViewModel -import com.android.systemui.qs.footer.ui.compose.FooterActions -import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel -import com.android.systemui.scene.shared.model.Scene -import com.android.systemui.scene.shared.model.SceneDataSourceDelegator -import com.android.systemui.scene.shared.model.SceneKey -import com.android.systemui.scene.ui.composable.ComposableScene -import com.android.systemui.scene.ui.composable.SceneContainer -import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel -import com.android.systemui.volume.panel.ui.composable.VolumePanelRoot -import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelViewModel -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.stateIn - -/** The Compose facade, when Compose is available. */ -object ComposeFacade : BaseComposeFacade { - override fun isComposeAvailable(): Boolean = true - - override fun composeInitializer(): ComposeInitializer = ComposeInitializerImpl - - override fun setPeopleSpaceActivityContent( - activity: ComponentActivity, - viewModel: PeopleViewModel, - onResult: (PeopleViewModel.Result) -> Unit, - ) { - activity.setContent { PlatformTheme { PeopleScreen(viewModel, onResult) } } - } - - override fun setCommunalEditWidgetActivityContent( - activity: ComponentActivity, - viewModel: BaseCommunalViewModel, - widgetConfigurator: WidgetConfigurator, - onOpenWidgetPicker: () -> Unit, - onEditDone: () -> Unit, - ) { - activity.setContent { - PlatformTheme { - Box( - modifier = - Modifier.fillMaxSize() - .background(LocalAndroidColorScheme.current.outlineVariant), - ) { - CommunalHub( - viewModel = viewModel, - onOpenWidgetPicker = onOpenWidgetPicker, - widgetConfigurator = widgetConfigurator, - onEditDone = onEditDone, - ) - } - } - } - } - - override fun setVolumePanelActivityContent( - activity: ComponentActivity, - viewModel: VolumePanelViewModel, - onDismiss: () -> Unit, - ) { - activity.setContent { - VolumePanelRoot( - viewModel = viewModel, - onDismiss = onDismiss, - ) - } - } - - override fun createFooterActionsView( - context: Context, - viewModel: FooterActionsViewModel, - qsVisibilityLifecycleOwner: LifecycleOwner, - ): View { - return DensityAwareComposeView(context).apply { - setContent { PlatformTheme { FooterActions(viewModel, qsVisibilityLifecycleOwner) } } - } - } - - override fun createSceneContainerView( - scope: CoroutineScope, - context: Context, - viewModel: SceneContainerViewModel, - windowInsets: StateFlow<WindowInsets?>, - sceneByKey: Map<SceneKey, Scene>, - dataSourceDelegator: SceneDataSourceDelegator, - ): View { - return ComposeView(context).apply { - setContent { - PlatformTheme { - ScreenDecorProvider( - displayCutout = displayCutoutFromWindowInsets(scope, context, windowInsets), - screenCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context) - ) { - SceneContainer( - viewModel = viewModel, - sceneByKey = - sceneByKey.mapValues { (_, scene) -> scene as ComposableScene }, - dataSourceDelegator = dataSourceDelegator, - ) - } - } - } - } - } - - override fun createStickyKeysIndicatorContent( - context: Context, - viewModel: StickyKeysIndicatorViewModel - ): View { - return createStickyKeyIndicatorView(context, viewModel) - } - - override fun createCommunalView( - context: Context, - viewModel: BaseCommunalViewModel, - ): View { - return ComposeView(context).apply { - setContent { PlatformTheme { CommunalHub(viewModel = viewModel) } } - } - } - - override fun createCommunalContainer(context: Context, viewModel: CommunalViewModel): View { - return ComposeView(context).apply { - setContent { PlatformTheme { CommunalContainer(viewModel = viewModel) } } - } - } - - // TODO(b/298525212): remove once Compose exposes window inset bounds. - private fun displayCutoutFromWindowInsets( - scope: CoroutineScope, - context: Context, - windowInsets: StateFlow<WindowInsets?>, - ): StateFlow<DisplayCutout> = - windowInsets - .map { - val boundingRect = it?.displayCutout?.boundingRectTop - val width = boundingRect?.let { boundingRect.right - boundingRect.left } ?: 0 - val left = boundingRect?.left?.toDp(context) ?: 0.dp - val top = boundingRect?.top?.toDp(context) ?: 0.dp - val right = boundingRect?.right?.toDp(context) ?: 0.dp - val bottom = boundingRect?.bottom?.toDp(context) ?: 0.dp - val location = - when { - width <= 0f -> CutoutLocation.NONE - left <= 0.dp -> CutoutLocation.LEFT - right >= getDisplayWidth(context) -> CutoutLocation.RIGHT - else -> CutoutLocation.CENTER - } - DisplayCutout( - left, - top, - right, - bottom, - location, - ) - } - .stateIn(scope, SharingStarted.WhileSubscribed(), DisplayCutout()) - - // TODO(b/298525212): remove once Compose exposes window inset bounds. - private fun getDisplayWidth(context: Context): Dp { - val point = Point() - checkNotNull(context.display).getRealSize(point) - return point.x.dp - } - - // TODO(b/298525212): remove once Compose exposes window inset bounds. - private fun Int.toDp(context: Context): Dp { - return (this.toFloat() / context.resources.displayMetrics.density).dp - } - - override fun createBouncer( - context: Context, - viewModel: BouncerViewModel, - dialogFactory: BouncerDialogFactory, - ): View { - return ComposeView(context).apply { - setContent { PlatformTheme { BouncerContent(viewModel, dialogFactory) } } - } - } - - override fun createLockscreen( - context: Context, - viewModel: LockscreenContentViewModel, - blueprints: Set<@JvmSuppressWildcards LockscreenSceneBlueprint>, - ): View { - val sceneBlueprints = - blueprints.mapNotNull { it as? ComposableLockscreenSceneBlueprint }.toSet() - return ComposeView(context).apply { - setContent { - LockscreenContent(viewModel = viewModel, blueprints = sceneBlueprints) - .Content(modifier = Modifier.fillMaxSize()) - } - } - } -} diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeInitializerImpl.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeInitializerImpl.kt deleted file mode 100644 index 1674591c30b5..000000000000 --- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeInitializerImpl.kt +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2023 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.compose - -import android.view.View -import androidx.lifecycle.findViewTreeLifecycleOwner -import androidx.lifecycle.setViewTreeLifecycleOwner -import androidx.lifecycle.Lifecycle -import androidx.savedstate.SavedStateRegistryController -import androidx.savedstate.SavedStateRegistryOwner -import com.android.compose.animation.ViewTreeSavedStateRegistryOwner -import com.android.systemui.lifecycle.ViewLifecycleOwner - -internal object ComposeInitializerImpl : ComposeInitializer { - override fun onAttachedToWindow(root: View) { - if (root.findViewTreeLifecycleOwner() != null) { - error("root $root already has a LifecycleOwner") - } - - val parent = root.parent - if (parent is View && parent.id != android.R.id.content) { - error( - "ComposeInitializer.onAttachedToWindow(View) must be called on the content child." + - "Outside of activities and dialogs, this is usually the top-most View of a " + - "window." - ) - } - - // The lifecycle owner, which is STARTED when [root] is visible and RESUMED when [root] is - // both visible and focused. - val lifecycleOwner = ViewLifecycleOwner(root) - - // We create a trivial implementation of [SavedStateRegistryOwner] that does not do any save - // or restore because SystemUI process is always running and top-level windows using this - // initializer are created once, when the process is started. - val savedStateRegistryOwner = - object : SavedStateRegistryOwner { - private val savedStateRegistryController = - SavedStateRegistryController.create(this).apply { performRestore(null) } - - override val savedStateRegistry = savedStateRegistryController.savedStateRegistry - - override val lifecycle: Lifecycle - get() = lifecycleOwner.lifecycle - } - - // We must call [ViewLifecycleOwner.onCreate] after creating the [SavedStateRegistryOwner] - // because `onCreate` might move the lifecycle state to STARTED which will make - // [SavedStateRegistryController.performRestore] throw. - lifecycleOwner.onCreate() - - // Set the owners on the root. They will be reused by any ComposeView inside the root - // hierarchy. - root.setViewTreeLifecycleOwner(lifecycleOwner) - ViewTreeSavedStateRegistryOwner.set(root, savedStateRegistryOwner) - } - - override fun onDetachedFromWindow(root: View) { - (root.findViewTreeLifecycleOwner() as ViewLifecycleOwner).onDestroy() - root.setViewTreeLifecycleOwner(null) - ViewTreeSavedStateRegistryOwner.set(root, null) - } -} diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt index 36d3ed52b655..f1a0e5e3539c 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt @@ -11,7 +11,6 @@ import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags import com.android.systemui.bouncer.ui.BouncerDialogFactory import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel -import com.android.systemui.compose.ComposeFacade import com.android.systemui.dagger.SysUISingleton import com.android.systemui.flags.Flags.COMPOSE_BOUNCER_ENABLED import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel @@ -60,11 +59,7 @@ constructor( private val composeBouncerDependencies: Lazy<ComposeBouncerDependencies>, ) { fun bind(view: ViewGroup) { - if ( - COMPOSE_BOUNCER_ENABLED && - composeBouncerFlags.isOnlyComposeBouncerEnabled() && - ComposeFacade.isComposeAvailable() - ) { + if (COMPOSE_BOUNCER_ENABLED && composeBouncerFlags.isOnlyComposeBouncerEnabled()) { val deps = composeBouncerDependencies.get() ComposeBouncerViewBinder.bind( view, diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt index 7b053956091e..179fa874db79 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt @@ -1,15 +1,17 @@ package com.android.systemui.bouncer.ui.binder import android.view.ViewGroup +import androidx.compose.ui.platform.ComposeView import androidx.core.view.isVisible import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle +import com.android.compose.theme.PlatformTheme import com.android.keyguard.ViewMediatorCallback import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.bouncer.ui.BouncerDialogFactory +import com.android.systemui.bouncer.ui.composable.BouncerContent import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel -import com.android.systemui.compose.ComposeFacade import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.user.domain.interactor.SelectedUserInteractor import kotlinx.coroutines.flow.collectLatest @@ -27,12 +29,11 @@ object ComposeBouncerViewBinder { viewMediatorCallback: ViewMediatorCallback?, ) { view.addView( - ComposeFacade.createBouncer( - view.context, - viewModel, - dialogFactory, - ) + ComposeView(view.context).apply { + setContent { PlatformTheme { BouncerContent(viewModel, dialogFactory) } } + } ) + view.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.CREATED) { launch { diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt index a5a390d7683b..48b3e4c3616d 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt @@ -25,14 +25,21 @@ import android.util.Log import android.view.IWindowManager import android.view.WindowInsets import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.ui.Modifier +import com.android.compose.theme.LocalAndroidColorScheme +import com.android.compose.theme.PlatformTheme import com.android.internal.logging.UiEventLogger import com.android.systemui.communal.shared.log.CommunalUiEvent import com.android.systemui.communal.shared.model.CommunalSceneKey +import com.android.systemui.communal.ui.compose.CommunalHub import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel import com.android.systemui.communal.util.WidgetPickerIntentUtils.getWidgetExtraFromIntent -import com.android.systemui.compose.ComposeFacade.setCommunalEditWidgetActivityContent import com.android.systemui.log.LogBuffer import com.android.systemui.log.core.Logger import com.android.systemui.log.dagger.CommunalLog @@ -110,56 +117,68 @@ constructor( val preselectedKey = intent.getStringExtra(EXTRA_PRESELECTED_KEY) communalViewModel.setSelectedKey(preselectedKey) - setCommunalEditWidgetActivityContent( - activity = this, - viewModel = communalViewModel, - widgetConfigurator = widgetConfigurator, - onOpenWidgetPicker = { - val intent = - Intent(Intent.ACTION_MAIN).also { it.addCategory(Intent.CATEGORY_HOME) } - packageManager - .resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) - ?.activityInfo - ?.packageName - ?.let { packageName -> - try { - addWidgetActivityLauncher.launch( - Intent(Intent.ACTION_PICK).apply { - setPackage(packageName) - putExtra( - EXTRA_DESIRED_WIDGET_WIDTH, - resources.getDimensionPixelSize( - R.dimen.communal_widget_picker_desired_width - ) - ) - putExtra( - EXTRA_DESIRED_WIDGET_HEIGHT, - resources.getDimensionPixelSize( - R.dimen.communal_widget_picker_desired_height - ) - ) - putExtra( - AppWidgetManager.EXTRA_CATEGORY_FILTER, - communalViewModel.getCommunalWidgetCategories - ) - } + setContent { + PlatformTheme { + Box( + modifier = + Modifier.fillMaxSize() + .background(LocalAndroidColorScheme.current.outlineVariant), + ) { + CommunalHub( + viewModel = communalViewModel, + onOpenWidgetPicker = ::onOpenWidgetPicker, + widgetConfigurator = widgetConfigurator, + onEditDone = ::onEditDone, + ) + } + } + } + } + + private fun onOpenWidgetPicker() { + val intent = Intent(Intent.ACTION_MAIN).also { it.addCategory(Intent.CATEGORY_HOME) } + packageManager + .resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) + ?.activityInfo + ?.packageName + ?.let { packageName -> + try { + addWidgetActivityLauncher.launch( + Intent(Intent.ACTION_PICK).apply { + setPackage(packageName) + putExtra( + EXTRA_DESIRED_WIDGET_WIDTH, + resources.getDimensionPixelSize( + R.dimen.communal_widget_picker_desired_width + ) + ) + putExtra( + EXTRA_DESIRED_WIDGET_HEIGHT, + resources.getDimensionPixelSize( + R.dimen.communal_widget_picker_desired_height + ) + ) + putExtra( + AppWidgetManager.EXTRA_CATEGORY_FILTER, + communalViewModel.getCommunalWidgetCategories ) - } catch (e: Exception) { - Log.e(TAG, "Failed to launch widget picker activity", e) } - } - ?: run { Log.e(TAG, "Couldn't resolve launcher package name") } - }, - onEditDone = { - try { - communalViewModel.onSceneChanged(CommunalSceneKey.Communal) - checkNotNull(windowManagerService).lockNow(/* options */ null) - finish() - } catch (e: RemoteException) { - Log.e(TAG, "Couldn't lock the device as WindowManager is dead.") + ) + } catch (e: Exception) { + Log.e(TAG, "Failed to launch widget picker activity", e) } } - ) + ?: run { Log.e(TAG, "Couldn't resolve launcher package name") } + } + + private fun onEditDone() { + try { + communalViewModel.onSceneChanged(CommunalSceneKey.Communal) + checkNotNull(windowManagerService).lockNow(/* options */ null) + finish() + } catch (e: RemoteException) { + Log.e(TAG, "Couldn't lock the device as WindowManager is dead.") + } } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { diff --git a/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt b/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt deleted file mode 100644 index a0aaa906802a..000000000000 --- a/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (C) 2022 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.compose - -import android.content.Context -import android.view.View -import android.view.WindowInsets -import androidx.activity.ComponentActivity -import androidx.lifecycle.LifecycleOwner -import com.android.systemui.bouncer.ui.BouncerDialogFactory -import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel -import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel -import com.android.systemui.communal.ui.viewmodel.CommunalViewModel -import com.android.systemui.communal.widgets.WidgetConfigurator -import com.android.systemui.keyboard.stickykeys.ui.viewmodel.StickyKeysIndicatorViewModel -import com.android.systemui.keyguard.shared.model.LockscreenSceneBlueprint -import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel -import com.android.systemui.people.ui.viewmodel.PeopleViewModel -import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel -import com.android.systemui.scene.shared.model.Scene -import com.android.systemui.scene.shared.model.SceneDataSourceDelegator -import com.android.systemui.scene.shared.model.SceneKey -import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel -import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelViewModel -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.StateFlow - -/** - * A facade to interact with Compose, when it is available. - * - * You should access this facade by calling the static methods on - * [com.android.systemui.compose.ComposeFacade] directly. - */ -interface BaseComposeFacade { - /** - * Whether Compose is currently available. This function should be checked before calling any - * other functions on this facade. - * - * This value will never change at runtime. - */ - fun isComposeAvailable(): Boolean - - /** - * Return the [ComposeInitializer] to make Compose usable in windows outside normal activities. - */ - fun composeInitializer(): ComposeInitializer - - /** Bind the content of [activity] to [viewModel]. */ - fun setPeopleSpaceActivityContent( - activity: ComponentActivity, - viewModel: PeopleViewModel, - onResult: (PeopleViewModel.Result) -> Unit, - ) - - /** Bind the content of [activity] to [viewModel]. */ - fun setCommunalEditWidgetActivityContent( - activity: ComponentActivity, - viewModel: BaseCommunalViewModel, - widgetConfigurator: WidgetConfigurator, - onOpenWidgetPicker: () -> Unit, - onEditDone: () -> Unit, - ) - - fun setVolumePanelActivityContent( - activity: ComponentActivity, - viewModel: VolumePanelViewModel, - onDismiss: () -> Unit, - ) - - /** Create a [View] to represent [viewModel] on screen. */ - fun createFooterActionsView( - context: Context, - viewModel: FooterActionsViewModel, - qsVisibilityLifecycleOwner: LifecycleOwner, - ): View - - /** Create a [View] to represent [viewModel] on screen. */ - fun createSceneContainerView( - scope: CoroutineScope, - context: Context, - viewModel: SceneContainerViewModel, - windowInsets: StateFlow<WindowInsets?>, - sceneByKey: Map<SceneKey, Scene>, - dataSourceDelegator: SceneDataSourceDelegator, - ): View - - /** Creates sticky key indicator content presenting provided [viewModel] */ - fun createStickyKeysIndicatorContent( - context: Context, - viewModel: StickyKeysIndicatorViewModel - ): View - - /** Create a [View] to represent [viewModel] on screen. */ - fun createCommunalView( - context: Context, - viewModel: BaseCommunalViewModel, - ): View - - /** Create a [View] to represent the [BouncerViewModel]. */ - fun createBouncer( - context: Context, - viewModel: BouncerViewModel, - dialogFactory: BouncerDialogFactory, - ): View - - /** Creates a container that hosts the communal UI and handles gesture transitions. */ - fun createCommunalContainer(context: Context, viewModel: CommunalViewModel): View - - /** Creates a [View] that represents the Lockscreen. */ - fun createLockscreen( - context: Context, - viewModel: LockscreenContentViewModel, - blueprints: Set<@JvmSuppressWildcards LockscreenSceneBlueprint>, - ): View -} diff --git a/packages/SystemUI/src/com/android/systemui/compose/ComposeInitializer.kt b/packages/SystemUI/src/com/android/systemui/compose/ComposeInitializer.kt index 90dc3a00daa2..813e0e025bf5 100644 --- a/packages/SystemUI/src/com/android/systemui/compose/ComposeInitializer.kt +++ b/packages/SystemUI/src/com/android/systemui/compose/ComposeInitializer.kt @@ -17,6 +17,13 @@ package com.android.systemui.compose import android.view.View +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.findViewTreeLifecycleOwner +import androidx.lifecycle.setViewTreeLifecycleOwner +import androidx.savedstate.SavedStateRegistryController +import androidx.savedstate.SavedStateRegistryOwner +import com.android.compose.animation.ViewTreeSavedStateRegistryOwner +import com.android.systemui.lifecycle.ViewLifecycleOwner /** * An initializer to use Compose outside of an Activity, e.g. inside a window added directly using @@ -39,10 +46,55 @@ import android.view.View * } * ``` */ -interface ComposeInitializer { +object ComposeInitializer { /** Function to be called on your window root view's [View.onAttachedToWindow] function. */ - fun onAttachedToWindow(root: View) + fun onAttachedToWindow(root: View) { + if (root.findViewTreeLifecycleOwner() != null) { + error("root $root already has a LifecycleOwner") + } + + val parent = root.parent + if (parent is View && parent.id != android.R.id.content) { + error( + "ComposeInitializer.onAttachedToWindow(View) must be called on the content child." + + "Outside of activities and dialogs, this is usually the top-most View of a " + + "window." + ) + } + + // The lifecycle owner, which is STARTED when [root] is visible and RESUMED when [root] is + // both visible and focused. + val lifecycleOwner = ViewLifecycleOwner(root) + + // We create a trivial implementation of [SavedStateRegistryOwner] that does not do any save + // or restore because SystemUI process is always running and top-level windows using this + // initializer are created once, when the process is started. + val savedStateRegistryOwner = + object : SavedStateRegistryOwner { + private val savedStateRegistryController = + SavedStateRegistryController.create(this).apply { performRestore(null) } + + override val savedStateRegistry = savedStateRegistryController.savedStateRegistry + + override val lifecycle: Lifecycle + get() = lifecycleOwner.lifecycle + } + + // We must call [ViewLifecycleOwner.onCreate] after creating the [SavedStateRegistryOwner] + // because `onCreate` might move the lifecycle state to STARTED which will make + // [SavedStateRegistryController.performRestore] throw. + lifecycleOwner.onCreate() + + // Set the owners on the root. They will be reused by any ComposeView inside the root + // hierarchy. + root.setViewTreeLifecycleOwner(lifecycleOwner) + ViewTreeSavedStateRegistryOwner.set(root, savedStateRegistryOwner) + } /** Function to be called on your window root view's [View.onDetachedFromWindow] function. */ - fun onDetachedFromWindow(root: View) + fun onDetachedFromWindow(root: View) { + (root.findViewTreeLifecycleOwner() as ViewLifecycleOwner).onDestroy() + root.setViewTreeLifecycleOwner(null) + ViewTreeSavedStateRegistryOwner.set(root, null) + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeyDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeyDialogFactory.kt index 3ed58a7fe5ae..f9084e5a6191 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeyDialogFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeyDialogFactory.kt @@ -26,9 +26,9 @@ import android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE import android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE import android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL import androidx.activity.ComponentDialog -import com.android.systemui.compose.ComposeFacade import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.keyboard.stickykeys.ui.view.createStickyKeyIndicatorView import com.android.systemui.keyboard.stickykeys.ui.viewmodel.StickyKeysIndicatorViewModel import com.android.systemui.res.R import javax.inject.Inject @@ -48,7 +48,7 @@ constructor( return ComponentDialog(context, R.style.Theme_SystemUI_Dialog).apply { // because we're requesting window feature it must be called before setting content window?.setStickyKeyWindowAttributes() - setContentView(ComposeFacade.createStickyKeysIndicatorContent(context, viewModel)) + setContentView(createStickyKeyIndicatorView(context, viewModel)) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinator.kt b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinator.kt index 842fd04bfcc5..78c4e77cad74 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinator.kt @@ -17,15 +17,13 @@ package com.android.systemui.keyboard.stickykeys.ui import android.app.Dialog -import android.util.Log -import com.android.systemui.compose.ComposeFacade import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyboard.stickykeys.StickyKeysLogger import com.android.systemui.keyboard.stickykeys.ui.viewmodel.StickyKeysIndicatorViewModel +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch -import javax.inject.Inject @SysUISingleton class StickyKeysIndicatorCoordinator @@ -40,11 +38,6 @@ constructor( private var dialog: Dialog? = null fun startListening() { - // this check needs to be moved to PhysicalKeyboardCoreStartable - if (!ComposeFacade.isComposeAvailable()) { - Log.e("StickyKeysIndicatorCoordinator", "Compose is required for this UI") - return - } applicationScope.launch { viewModel.indicatorContent.collect { stickyKeys -> stickyKeysLogger.logNewUiState(stickyKeys) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt index 301942f6242b..106fdf1fbcbe 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt @@ -20,6 +20,9 @@ package com.android.systemui.keyguard import android.content.Context import android.view.LayoutInflater import android.view.View +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.ComposeView import androidx.constraintlayout.widget.ConstraintSet import androidx.constraintlayout.widget.ConstraintSet.BOTTOM import androidx.constraintlayout.widget.ConstraintSet.END @@ -35,7 +38,6 @@ import com.android.keyguard.dagger.KeyguardStatusViewComponent import com.android.systemui.CoreStartable import com.android.systemui.Flags.keyguardBottomAreaRefactor import com.android.systemui.common.ui.ConfigurationState -import com.android.systemui.compose.ComposeFacade import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor @@ -45,6 +47,8 @@ import com.android.systemui.keyguard.shared.model.LockscreenSceneBlueprint import com.android.systemui.keyguard.ui.binder.KeyguardBlueprintViewBinder import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder +import com.android.systemui.keyguard.ui.composable.LockscreenContent +import com.android.systemui.keyguard.ui.composable.blueprint.ComposableLockscreenSceneBlueprint import com.android.systemui.keyguard.ui.view.KeyguardIndicationArea import com.android.systemui.keyguard.ui.view.KeyguardRootView import com.android.systemui.keyguard.ui.view.layout.KeyguardBlueprintCommandListener @@ -132,7 +136,7 @@ constructor( if (!SceneContainerFlag.isEnabled) { if (ComposeLockscreen.isEnabled) { val composeView = - ComposeFacade.createLockscreen( + createLockscreen( context = context, viewModel = lockscreenContentViewModel, blueprints = lockscreenSceneBlueprintsLazy.get(), @@ -207,6 +211,21 @@ constructor( ) } + private fun createLockscreen( + context: Context, + viewModel: LockscreenContentViewModel, + blueprints: Set<@JvmSuppressWildcards LockscreenSceneBlueprint>, + ): View { + val sceneBlueprints = + blueprints.mapNotNull { it as? ComposableLockscreenSceneBlueprint }.toSet() + return ComposeView(context).apply { + setContent { + LockscreenContent(viewModel = viewModel, blueprints = sceneBlueprints) + .Content(modifier = Modifier.fillMaxSize()) + } + } + } + /** * Temporary, to allow NotificationPanelViewController to use the same instance while code is * migrated: b/288242803 diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/ComposeLockscreen.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/ComposeLockscreen.kt index 7f0b483919b3..601fbfaf1b64 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/ComposeLockscreen.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/ComposeLockscreen.kt @@ -17,7 +17,6 @@ package com.android.systemui.keyguard.shared import com.android.systemui.Flags -import com.android.systemui.compose.ComposeFacade import com.android.systemui.flags.FlagToken import com.android.systemui.flags.RefactorFlagUtils @@ -34,7 +33,7 @@ object ComposeLockscreen { /** Is the refactor enabled */ @JvmStatic inline val isEnabled - get() = Flags.composeLockscreen() && ComposeFacade.isComposeAvailable() + get() = Flags.composeLockscreen() /** * Called to ensure code is only run when the flag is enabled. This protects users from the diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.kt b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.kt index 5b7eb454597c..deb0fed0ffc8 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.kt @@ -20,14 +20,15 @@ import android.appwidget.AppWidgetManager import android.os.Bundle import android.util.Log import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent import androidx.lifecycle.Lifecycle import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle -import com.android.systemui.compose.ComposeFacade.isComposeAvailable -import com.android.systemui.compose.ComposeFacade.setPeopleSpaceActivityContent +import com.android.compose.theme.PlatformTheme import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags +import com.android.systemui.people.ui.compose.PeopleScreen import com.android.systemui.people.ui.view.PeopleViewBinder import com.android.systemui.people.ui.view.PeopleViewBinder.bind import com.android.systemui.people.ui.viewmodel.PeopleViewModel @@ -65,13 +66,11 @@ constructor( } // Set the content of the activity, using either the View or Compose implementation. - if (featureFlags.isEnabled(Flags.COMPOSE_PEOPLE_SPACE) && isComposeAvailable()) { + if (featureFlags.isEnabled(Flags.COMPOSE_PEOPLE_SPACE)) { Log.d(TAG, "Using the Compose implementation of the PeopleSpaceActivity") - setPeopleSpaceActivityContent( - activity = this, - viewModel, - onResult = { finishActivity(it) }, - ) + setContent { + PlatformTheme { PeopleScreen(viewModel, onResult = { finishActivity(it) }) } + } } else { Log.d(TAG, "Using the View implementation of the PeopleSpaceActivity") val view = PeopleViewBinder.create(this) diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java index 741336277119..a000d63a2ee3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java @@ -47,7 +47,6 @@ import com.android.app.animation.Interpolators; import com.android.keyguard.BouncerPanelExpansionCalculator; import com.android.systemui.Dumpable; import com.android.systemui.animation.ShadeInterpolation; -import com.android.systemui.compose.ComposeFacade; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; @@ -301,8 +300,7 @@ public class QSImpl implements QS, CommandQueue.Callbacks, StatusBarStateControl private void bindFooterActionsView(View root) { LinearLayout footerActionsView = root.findViewById(R.id.qs_footer_actions); - if (!mFeatureFlags.isEnabled(Flags.COMPOSE_QS_FOOTER_ACTIONS) - || !ComposeFacade.INSTANCE.isComposeAvailable()) { + if (!mFeatureFlags.isEnabled(Flags.COMPOSE_QS_FOOTER_ACTIONS)) { Log.d(TAG, "Binding the View implementation of the QS footer actions"); mFooterActionsView = footerActionsView; mFooterActionsViewBinder.bind(footerActionsView, mQSFooterActionsViewModel, @@ -312,7 +310,7 @@ public class QSImpl implements QS, CommandQueue.Callbacks, StatusBarStateControl // Compose is available, so let's use the Compose implementation of the footer actions. Log.d(TAG, "Binding the Compose implementation of the QS footer actions"); - View composeView = ComposeFacade.INSTANCE.createFooterActionsView(root.getContext(), + View composeView = QSUtils.createFooterActionsView(root.getContext(), mQSFooterActionsViewModel, mListeningAndVisibilityLifecycleOwner); mFooterActionsView = composeView; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSUtils.kt b/packages/SystemUI/src/com/android/systemui/qs/QSUtils.kt index e42264f24e92..15c3f271469d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSUtils.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/QSUtils.kt @@ -1,7 +1,13 @@ package com.android.systemui.qs import android.content.Context +import android.view.View +import androidx.lifecycle.LifecycleOwner +import com.android.compose.theme.PlatformTheme +import com.android.compose.ui.platform.DensityAwareComposeView import com.android.internal.policy.SystemBarUtils +import com.android.systemui.qs.footer.ui.compose.FooterActions +import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel import com.android.systemui.util.LargeScreenUtils.shouldUseLargeScreenShadeHeader object QSUtils { @@ -21,4 +27,15 @@ object QSUtils { SystemBarUtils.getQuickQsOffsetHeight(context) } } -}
\ No newline at end of file + + @JvmStatic + fun createFooterActionsView( + context: Context, + viewModel: FooterActionsViewModel, + qsVisibilityLifecycleOwner: LifecycleOwner, + ): View { + return DensityAwareComposeView(context).apply { + setContent { PlatformTheme { FooterActions(viewModel, qsVisibilityLifecycleOwner) } } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt index 8408c51c86dc..1808d98cd692 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt @@ -24,7 +24,6 @@ import com.android.systemui.Flags.FLAG_SCENE_CONTAINER import com.android.systemui.Flags.keyguardBottomAreaRefactor import com.android.systemui.Flags.migrateClocksToBlueprint import com.android.systemui.Flags.sceneContainer -import com.android.systemui.compose.ComposeFacade import com.android.systemui.dagger.SysUISingleton import com.android.systemui.flags.FlagToken import com.android.systemui.flags.Flags.SCENE_CONTAINER_ENABLED @@ -47,9 +46,8 @@ object SceneContainerFlag { keyguardBottomAreaRefactor() && migrateClocksToBlueprint() && ComposeLockscreen.isEnabled && - MediaInSceneContainerFlag.isEnabled && - // NOTE: Changes should also be made in getSecondaryFlags and @EnableSceneContainer - ComposeFacade.isComposeAvailable() + MediaInSceneContainerFlag.isEnabled + // NOTE: Changes should also be made in getSecondaryFlags and @EnableSceneContainer /** * The main static flag, SCENE_CONTAINER_ENABLED. This is an explicit static flag check that @@ -74,11 +72,7 @@ object SceneContainerFlag { /** The full set of requirements for SceneContainer */ inline fun getAllRequirements(): Sequence<FlagToken> { - val composeRequirement = - FlagToken("ComposeFacade.isComposeAvailable()", ComposeFacade.isComposeAvailable()) - return sequenceOf(getMainStaticFlag(), getMainAconfigFlag()) + - getSecondaryFlags() + - composeRequirement + return sequenceOf(getMainStaticFlag(), getMainAconfigFlag()) + getSecondaryFlags() } /** Return all dependencies of this flag in pairs where [Pair.first] depends on [Pair.second] */ diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt index ee76c0582b9d..f2697b4e1c1e 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt @@ -16,17 +16,26 @@ package com.android.systemui.scene.ui.view +import android.content.Context +import android.graphics.Point import android.view.View import android.view.ViewGroup import android.view.WindowInsets import androidx.activity.OnBackPressedDispatcher import androidx.activity.OnBackPressedDispatcherOwner import androidx.activity.setViewTreeOnBackPressedDispatcherOwner +import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp import androidx.core.view.isVisible import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle -import com.android.systemui.compose.ComposeFacade +import com.android.compose.theme.PlatformTheme +import com.android.internal.policy.ScreenDecorationsUtils +import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation +import com.android.systemui.common.ui.compose.windowinsets.DisplayCutout +import com.android.systemui.common.ui.compose.windowinsets.ScreenDecorProvider import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.res.R import com.android.systemui.scene.shared.flag.SceneContainerFlags @@ -34,10 +43,16 @@ import com.android.systemui.scene.shared.model.Scene import com.android.systemui.scene.shared.model.SceneContainerConfig import com.android.systemui.scene.shared.model.SceneDataSourceDelegator import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.ui.composable.ComposableScene +import com.android.systemui.scene.ui.composable.SceneContainer import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel import com.android.systemui.statusbar.notification.stack.shared.flexiNotifsEnabled import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch object SceneWindowRootViewBinder { @@ -83,7 +98,7 @@ object SceneWindowRootViewBinder { ) view.addView( - ComposeFacade.createSceneContainerView( + createSceneContainerView( scope = this, context = view.context, viewModel = viewModel, @@ -120,4 +135,74 @@ object SceneWindowRootViewBinder { } } } + + private fun createSceneContainerView( + scope: CoroutineScope, + context: Context, + viewModel: SceneContainerViewModel, + windowInsets: StateFlow<WindowInsets?>, + sceneByKey: Map<SceneKey, Scene>, + dataSourceDelegator: SceneDataSourceDelegator, + ): View { + return ComposeView(context).apply { + setContent { + PlatformTheme { + ScreenDecorProvider( + displayCutout = displayCutoutFromWindowInsets(scope, context, windowInsets), + screenCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context) + ) { + SceneContainer( + viewModel = viewModel, + sceneByKey = + sceneByKey.mapValues { (_, scene) -> scene as ComposableScene }, + dataSourceDelegator = dataSourceDelegator, + ) + } + } + } + } + } + + // TODO(b/298525212): remove once Compose exposes window inset bounds. + private fun displayCutoutFromWindowInsets( + scope: CoroutineScope, + context: Context, + windowInsets: StateFlow<WindowInsets?>, + ): StateFlow<DisplayCutout> = + windowInsets + .map { + val boundingRect = it?.displayCutout?.boundingRectTop + val width = boundingRect?.let { boundingRect.right - boundingRect.left } ?: 0 + val left = boundingRect?.left?.toDp(context) ?: 0.dp + val top = boundingRect?.top?.toDp(context) ?: 0.dp + val right = boundingRect?.right?.toDp(context) ?: 0.dp + val bottom = boundingRect?.bottom?.toDp(context) ?: 0.dp + val location = + when { + width <= 0f -> CutoutLocation.NONE + left <= 0.dp -> CutoutLocation.LEFT + right >= getDisplayWidth(context) -> CutoutLocation.RIGHT + else -> CutoutLocation.CENTER + } + DisplayCutout( + left, + top, + right, + bottom, + location, + ) + } + .stateIn(scope, SharingStarted.WhileSubscribed(), DisplayCutout()) + + // TODO(b/298525212): remove once Compose exposes window inset bounds. + private fun getDisplayWidth(context: Context): Dp { + val point = Point() + checkNotNull(context.display).getRealSize(point) + return point.x.dp + } + + // TODO(b/298525212): remove once Compose exposes window inset bounds. + private fun Int.toDp(context: Context): Dp { + return (this.toFloat() / context.resources.displayMetrics.density).dp + } } diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt index 4c2c97981702..22645c4532f6 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt @@ -25,7 +25,7 @@ import android.view.View import android.view.WindowInsets import android.widget.FrameLayout import androidx.core.view.updateMargins -import com.android.systemui.compose.ComposeFacade +import com.android.systemui.compose.ComposeInitializer import com.android.systemui.res.R /** A view that can serve as the root of the main SysUI window. */ @@ -45,16 +45,16 @@ open class WindowRootView( override fun onAttachedToWindow() { super.onAttachedToWindow() - if (ComposeFacade.isComposeAvailable() && isRoot()) { - ComposeFacade.composeInitializer().onAttachedToWindow(this) + if (isRoot()) { + ComposeInitializer.onAttachedToWindow(this) } } override fun onDetachedFromWindow() { super.onDetachedFromWindow() - if (ComposeFacade.isComposeAvailable() && isRoot()) { - ComposeFacade.composeInitializer().onDetachedFromWindow(this) + if (isRoot()) { + ComposeInitializer.onDetachedFromWindow(this) } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt index df845f559f2e..d3869baf16a2 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt @@ -23,11 +23,12 @@ import android.view.GestureDetector import android.view.MotionEvent import android.view.View import android.view.ViewGroup +import androidx.compose.ui.platform.ComposeView +import com.android.compose.theme.PlatformTheme import com.android.internal.annotations.VisibleForTesting import com.android.systemui.communal.domain.interactor.CommunalInteractor +import com.android.systemui.communal.ui.compose.CommunalContainer import com.android.systemui.communal.ui.viewmodel.CommunalViewModel -import com.android.systemui.compose.ComposeFacade.createCommunalContainer -import com.android.systemui.compose.ComposeFacade.isComposeAvailable import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.res.R @@ -35,7 +36,6 @@ import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.util.kotlin.collectFlow import javax.inject.Inject import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flowOf /** * Controller that's responsible for the glanceable hub container view and its touch handling. @@ -107,8 +107,7 @@ constructor( private var shadeShowing = false /** Returns a flow that tracks whether communal hub is available. */ - fun communalAvailable(): Flow<Boolean> = - if (isComposeAvailable()) communalInteractor.isCommunalAvailable else flowOf(false) + fun communalAvailable(): Flow<Boolean> = communalInteractor.isCommunalAvailable /** * Creates the container view containing the glanceable hub UI. @@ -118,7 +117,11 @@ constructor( fun initView( context: Context, ): View { - return initView(createCommunalContainer(context, communalViewModel)) + return initView( + ComposeView(context).apply { + setContent { PlatformTheme { CommunalContainer(viewModel = communalViewModel) } } + } + ) } /** Override for testing. */ diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/shared/flag/VolumePanelFlag.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/shared/flag/VolumePanelFlag.kt index d90a9c75deec..485f4b5cbfd7 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/shared/flag/VolumePanelFlag.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/shared/flag/VolumePanelFlag.kt @@ -17,23 +17,18 @@ package com.android.systemui.volume.panel.shared.flag import com.android.systemui.Flags -import com.android.systemui.compose.ComposeFacade import com.android.systemui.flags.RefactorFlagUtils import javax.inject.Inject /** Provides a flag to check for the new Compose based Volume Panel availability. */ class VolumePanelFlag @Inject constructor() { - /** - * Returns true when the new Volume Panel is available and false the otherwise. The new panel - * can only be available when [ComposeFacade.isComposeAvailable] is true. - */ + /** Returns true when the new Volume Panel is available and false the otherwise. */ fun canUseNewVolumePanel(): Boolean { - return ComposeFacade.isComposeAvailable() && Flags.newVolumePanel() + return Flags.newVolumePanel() } fun assertNewVolumePanel() { - require(ComposeFacade.isComposeAvailable()) RefactorFlagUtils.assertInNewMode(Flags.newVolumePanel(), Flags.FLAG_NEW_VOLUME_PANEL) } } diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/activity/VolumePanelActivity.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/activity/VolumePanelActivity.kt index 53e1b8b5bb70..d430e65770fd 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/activity/VolumePanelActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/activity/VolumePanelActivity.kt @@ -18,11 +18,12 @@ package com.android.systemui.volume.panel.ui.activity import android.os.Bundle import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.activity.viewModels -import com.android.systemui.compose.ComposeFacade import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.volume.panel.shared.flag.VolumePanelFlag +import com.android.systemui.volume.panel.ui.composable.VolumePanelRoot import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelViewModel import javax.inject.Inject import javax.inject.Provider @@ -44,7 +45,7 @@ constructor( volumePanelFlag.assertNewVolumePanel() - ComposeFacade.setVolumePanelActivityContent(this, viewModel) { finish() } + setContent { VolumePanelRoot(viewModel = viewModel, onDismiss = ::finish) } } override fun onContentChanged() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/compose/ComposeInitializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/compose/ComposeInitializerTest.kt index 3e6cc3bb4f6b..03e4f9ae1685 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/compose/ComposeInitializerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/compose/ComposeInitializerTest.kt @@ -32,10 +32,6 @@ import org.junit.runner.RunWith class ComposeInitializerTest : SysuiTestCase() { @Test fun testCanAddComposeViewInInitializedWindow() { - if (!ComposeFacade.isComposeAvailable()) { - return - } - val root = TestWindowRoot(context) try { runOnMainThreadAndWaitForIdleSync { ViewUtils.attachView(root) } @@ -55,12 +51,12 @@ class ComposeInitializerTest : SysuiTestCase() { class TestWindowRoot(context: Context) : FrameLayout(context) { override fun onAttachedToWindow() { super.onAttachedToWindow() - ComposeFacade.composeInitializer().onAttachedToWindow(this) + ComposeInitializer.onAttachedToWindow(this) } override fun onDetachedFromWindow() { super.onDetachedFromWindow() - ComposeFacade.composeInitializer().onDetachedFromWindow(this) + ComposeInitializer.onDetachedFromWindow(this) } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt index a992956f5121..59d8fc3d3fd0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt @@ -19,7 +19,6 @@ package com.android.systemui.keyboard.stickykeys.ui import android.app.Dialog import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.compose.ComposeFacade import com.android.systemui.keyboard.data.repository.FakeStickyKeysRepository import com.android.systemui.keyboard.data.repository.keyboardRepository import com.android.systemui.keyboard.stickykeys.StickyKeysLogger @@ -34,7 +33,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent -import org.junit.Assume import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -54,19 +52,22 @@ class StickyKeysIndicatorCoordinatorTest : SysuiTestCase() { @Before fun setup() { - Assume.assumeTrue(ComposeFacade.isComposeAvailable()) val dialogFactory = mock<StickyKeyDialogFactory>() whenever(dialogFactory.create(any())).thenReturn(dialog) val keyboardRepository = Kosmos().keyboardRepository - val viewModel = StickyKeysIndicatorViewModel( + val viewModel = + StickyKeysIndicatorViewModel( stickyKeysRepository, keyboardRepository, - testScope.backgroundScope) - coordinator = StickyKeysIndicatorCoordinator( + testScope.backgroundScope + ) + coordinator = + StickyKeysIndicatorCoordinator( testScope.backgroundScope, dialogFactory, viewModel, - mock<StickyKeysLogger>()) + mock<StickyKeysLogger>() + ) coordinator.startListening() keyboardRepository.setIsAnyKeyboardConnected(true) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt index 665fc750c742..96574e245d2d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt @@ -35,7 +35,6 @@ import com.android.systemui.communal.domain.interactor.communalInteractor import com.android.systemui.communal.domain.interactor.setCommunalAvailable import com.android.systemui.communal.shared.model.CommunalSceneKey import com.android.systemui.communal.ui.viewmodel.CommunalViewModel -import com.android.systemui.compose.ComposeFacade import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testDispatcher @@ -52,9 +51,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.test.UnconfinedTestDispatcher import org.junit.After import org.junit.Assert.assertThrows -import org.junit.Assume.assumeTrue import org.junit.Before -import org.junit.BeforeClass import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock @@ -305,13 +302,5 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, CONTAINER_HEIGHT.toFloat(), 0) private val MOVE_EVENT = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0) private val UP_EVENT = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0) - - @BeforeClass - @JvmStatic - fun beforeClass() { - // Glanceable hub requires Compose, no point running any of these tests if compose isn't - // enabled. - assumeTrue(ComposeFacade.isComposeAvailable()) - } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt index b426d1de0b00..960fd59b4f10 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.shade +import org.mockito.Mockito.`when` as whenever import android.content.Context import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper @@ -33,7 +34,6 @@ import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.bouncer.ui.binder.BouncerViewBinder import com.android.systemui.classifier.FalsingCollectorFake -import com.android.systemui.compose.ComposeFacade.isComposeAvailable import com.android.systemui.dock.DockManager import com.android.systemui.dump.DumpManager import com.android.systemui.flags.FakeFeatureFlagsClassic @@ -88,7 +88,6 @@ import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.times import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @@ -511,10 +510,6 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { @Test @Ignore("b/321332798") fun setsUpCommunalHubLayout_whenFlagEnabled() { - if (!isComposeAvailable()) { - return - } - whenever(mGlanceableHubContainerController.communalAvailable()) .thenReturn(MutableStateFlow(true)) @@ -537,10 +532,6 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { @Test fun doesNotSetupCommunalHubLayout_whenFlagDisabled() { - if (!isComposeAvailable()) { - return - } - whenever(mGlanceableHubContainerController.communalAvailable()) .thenReturn(MutableStateFlow(false)) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/SceneContainerRule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/SceneContainerRule.kt index 3faa6eb8f5f2..4e05de27bb33 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/SceneContainerRule.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/SceneContainerRule.kt @@ -17,7 +17,6 @@ package com.android.systemui.flags import android.util.Log -import com.android.systemui.compose.ComposeFacade import com.android.systemui.scene.shared.flag.SceneContainerFlag import org.junit.Assert import org.junit.Assume @@ -42,10 +41,6 @@ class SceneContainerRule : TestRule { null || description?.getAnnotation(EnableSceneContainer::class.java) != null if (hasAnnotation) { Assume.assumeTrue( - "Compose must be available for @EnableSceneContainer test", - ComposeFacade.isComposeAvailable() - ) - Assume.assumeTrue( "Couldn't set Flags.SCENE_CONTAINER_ENABLED for @EnableSceneContainer test", trySetSceneContainerEnabled(true) ) |