• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.notifications.ui.composable
18 
19 import androidx.compose.foundation.layout.Box
20 import androidx.compose.foundation.layout.Column
21 import androidx.compose.foundation.layout.fillMaxWidth
22 import androidx.compose.foundation.layout.padding
23 import androidx.compose.runtime.Composable
24 import androidx.compose.ui.Alignment
25 import androidx.compose.ui.Modifier
26 import androidx.compose.ui.platform.LocalResources
27 import androidx.compose.ui.res.dimensionResource
28 import com.android.compose.animation.scene.ContentScope
29 import com.android.compose.animation.scene.ElementKey
30 import com.android.compose.animation.scene.UserAction
31 import com.android.compose.animation.scene.UserActionResult
32 import com.android.internal.jank.InteractionJankMonitor
33 import com.android.systemui.dagger.SysUISingleton
34 import com.android.systemui.keyguard.ui.composable.blueprint.rememberBurnIn
35 import com.android.systemui.keyguard.ui.composable.section.DefaultClockSection
36 import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
37 import com.android.systemui.lifecycle.rememberViewModel
38 import com.android.systemui.media.controls.ui.composable.MediaCarousel
39 import com.android.systemui.media.controls.ui.composable.isLandscape
40 import com.android.systemui.media.controls.ui.controller.MediaCarouselController
41 import com.android.systemui.media.controls.ui.view.MediaHost
42 import com.android.systemui.media.controls.ui.view.MediaHostState.Companion.COLLAPSED
43 import com.android.systemui.media.controls.ui.view.MediaHostState.Companion.EXPANDED
44 import com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL
45 import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeOverlayActionsViewModel
46 import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeOverlayContentViewModel
47 import com.android.systemui.res.R
48 import com.android.systemui.scene.session.ui.composable.SaveableSession
49 import com.android.systemui.scene.shared.model.Overlays
50 import com.android.systemui.scene.ui.composable.Overlay
51 import com.android.systemui.shade.ui.composable.OverlayShade
52 import com.android.systemui.shade.ui.composable.OverlayShadeHeader
53 import com.android.systemui.shade.ui.composable.isFullWidthShade
54 import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
55 import com.android.systemui.util.Utils
56 import dagger.Lazy
57 import javax.inject.Inject
58 import javax.inject.Named
59 import kotlinx.coroutines.flow.Flow
60 
61 @SysUISingleton
62 class NotificationsShadeOverlay
63 @Inject
64 constructor(
65     private val actionsViewModelFactory: NotificationsShadeOverlayActionsViewModel.Factory,
66     private val contentViewModelFactory: NotificationsShadeOverlayContentViewModel.Factory,
67     private val shadeSession: SaveableSession,
68     private val stackScrollView: Lazy<NotificationScrollView>,
69     private val clockSection: DefaultClockSection,
70     private val keyguardClockViewModel: KeyguardClockViewModel,
71     private val mediaCarouselController: MediaCarouselController,
72     @Named(QUICK_QS_PANEL) private val mediaHost: Lazy<MediaHost>,
73     private val jankMonitor: InteractionJankMonitor,
74 ) : Overlay {
75     override val key = Overlays.NotificationsShade
76 
<lambda>null77     private val actionsViewModel: NotificationsShadeOverlayActionsViewModel by lazy {
78         actionsViewModelFactory.create()
79     }
80 
81     override val userActions: Flow<Map<UserAction, UserActionResult>> = actionsViewModel.actions
82 
activatenull83     override suspend fun activate(): Nothing {
84         actionsViewModel.activate()
85     }
86 
87     @Composable
Contentnull88     override fun ContentScope.Content(modifier: Modifier) {
89         val notificationStackPadding = dimensionResource(id = R.dimen.notification_side_paddings)
90 
91         val viewModel =
92             rememberViewModel("NotificationsShadeOverlay-viewModel") {
93                 contentViewModelFactory.create()
94             }
95         val placeholderViewModel =
96             rememberViewModel("NotificationsShadeOverlay-notifPlaceholderViewModel") {
97                 viewModel.notificationsPlaceholderViewModelFactory.create()
98             }
99 
100         val usingCollapsedLandscapeMedia =
101             Utils.useCollapsedMediaInLandscape(LocalResources.current)
102         mediaHost.get().expansion =
103             if (usingCollapsedLandscapeMedia && isLandscape()) COLLAPSED else EXPANDED
104 
105         OverlayShade(
106             panelElement = NotificationsShade.Elements.Panel,
107             alignmentOnWideScreens = Alignment.TopStart,
108             modifier = modifier,
109             onScrimClicked = viewModel::onScrimClicked,
110             header = {
111                 val headerViewModel =
112                     rememberViewModel("NotificationsShadeOverlayHeader") {
113                         viewModel.shadeHeaderViewModelFactory.create()
114                     }
115                 OverlayShadeHeader(
116                     viewModel = headerViewModel,
117                     modifier = Modifier.element(NotificationsShade.Elements.StatusBar),
118                 )
119             },
120         ) {
121             Box {
122                 Column {
123                     if (isFullWidthShade()) {
124                         val burnIn = rememberBurnIn(keyguardClockViewModel)
125 
126                         with(clockSection) {
127                             SmallClock(
128                                 burnInParams = burnIn.parameters,
129                                 onTopChanged = burnIn.onSmallClockTopChanged,
130                             )
131                         }
132                     }
133 
134                     MediaCarousel(
135                         isVisible = viewModel.showMedia,
136                         mediaHost = mediaHost.get(),
137                         carouselController = mediaCarouselController,
138                         usingCollapsedLandscapeMedia = usingCollapsedLandscapeMedia,
139                         modifier =
140                             Modifier.padding(
141                                 top = notificationStackPadding,
142                                 start = notificationStackPadding,
143                                 end = notificationStackPadding,
144                             ),
145                     )
146 
147                     NotificationScrollingStack(
148                         shadeSession = shadeSession,
149                         stackScrollView = stackScrollView.get(),
150                         viewModel = placeholderViewModel,
151                         jankMonitor = jankMonitor,
152                         maxScrimTop = { 0f },
153                         shouldPunchHoleBehindScrim = false,
154                         stackTopPadding = notificationStackPadding,
155                         stackBottomPadding = notificationStackPadding,
156                         shouldFillMaxSize = false,
157                         shouldShowScrim = false,
158                         supportNestedScrolling = false,
159                         modifier = Modifier.fillMaxWidth(),
160                     )
161                 }
162                 // Communicates the bottom position of the drawable area within the shade to NSSL.
163                 NotificationStackCutoffGuideline(
164                     stackScrollView = stackScrollView.get(),
165                     viewModel = placeholderViewModel,
166                     modifier =
167                         Modifier.align(Alignment.BottomCenter)
168                             .padding(bottom = notificationStackPadding),
169                 )
170             }
171         }
172     }
173 }
174 
175 object NotificationsShade {
176     object Elements {
177         val Panel = ElementKey("NotificationsShadeOverlayPanel")
178         val StatusBar = ElementKey("NotificationsShadeOverlayStatusBar")
179     }
180 }
181