1 /*
<lambda>null2  * Copyright (C) 2023 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.statusbar.pipeline.shared.ui.binder
18 
19 import android.animation.Animator
20 import android.animation.AnimatorListenerAdapter
21 import android.view.View
22 import android.widget.ImageView
23 import androidx.lifecycle.Lifecycle
24 import androidx.lifecycle.repeatOnLifecycle
25 import com.android.systemui.Flags
26 import com.android.systemui.common.ui.binder.IconViewBinder
27 import com.android.systemui.dagger.SysUISingleton
28 import com.android.systemui.lifecycle.repeatWhenAttached
29 import com.android.systemui.res.R
30 import com.android.systemui.statusbar.chips.ui.binder.ChipChronometerBinder
31 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
32 import com.android.systemui.statusbar.chips.ui.view.ChipChronometer
33 import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
34 import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel
35 import javax.inject.Inject
36 import kotlinx.coroutines.launch
37 
38 /**
39  * Interface to assist with binding the [CollapsedStatusBarFragment] to
40  * [CollapsedStatusBarViewModel]. Used only to enable easy testing of [CollapsedStatusBarFragment].
41  */
42 interface CollapsedStatusBarViewBinder {
43     /**
44      * Binds the view to the view-model. [listener] will be notified whenever an event that may
45      * change the status bar visibility occurs.
46      */
47     fun bind(
48         view: View,
49         viewModel: CollapsedStatusBarViewModel,
50         listener: StatusBarVisibilityChangeListener,
51     )
52 }
53 
54 @SysUISingleton
55 class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBarViewBinder {
bindnull56     override fun bind(
57         view: View,
58         viewModel: CollapsedStatusBarViewModel,
59         listener: StatusBarVisibilityChangeListener,
60     ) {
61         view.repeatWhenAttached {
62             repeatOnLifecycle(Lifecycle.State.CREATED) {
63                 launch {
64                     viewModel.isTransitioningFromLockscreenToOccluded.collect {
65                         listener.onStatusBarVisibilityMaybeChanged()
66                     }
67                 }
68 
69                 launch {
70                     viewModel.transitionFromLockscreenToDreamStartedEvent.collect {
71                         listener.onTransitionFromLockscreenToDreamStarted()
72                     }
73                 }
74 
75                 if (NotificationsLiveDataStoreRefactor.isEnabled) {
76                     val displayId = view.display.displayId
77                     val lightsOutView: View = view.requireViewById(R.id.notification_lights_out)
78                     launch {
79                         viewModel.areNotificationsLightsOut(displayId).collect { show ->
80                             animateLightsOutView(lightsOutView, show)
81                         }
82                     }
83                 }
84 
85                 if (Flags.statusBarScreenSharingChips()) {
86                     val chipView: View = view.requireViewById(R.id.ongoing_activity_chip)
87                     val chipIconView: ImageView =
88                         chipView.requireViewById(R.id.ongoing_activity_chip_icon)
89                     val chipTimeView: ChipChronometer =
90                         chipView.requireViewById(R.id.ongoing_activity_chip_time)
91                     launch {
92                         viewModel.ongoingActivityChip.collect { chipModel ->
93                             when (chipModel) {
94                                 is OngoingActivityChipModel.Shown -> {
95                                     IconViewBinder.bind(chipModel.icon, chipIconView)
96                                     ChipChronometerBinder.bind(chipModel.startTimeMs, chipTimeView)
97                                     chipView.setOnClickListener(chipModel.onClickListener)
98 
99                                     listener.onOngoingActivityStatusChanged(
100                                         hasOngoingActivity = true
101                                     )
102                                 }
103                                 is OngoingActivityChipModel.Hidden -> {
104                                     chipTimeView.stop()
105                                     listener.onOngoingActivityStatusChanged(
106                                         hasOngoingActivity = false
107                                     )
108                                 }
109                             }
110                         }
111                     }
112                 }
113             }
114         }
115     }
116 
animateLightsOutViewnull117     private fun animateLightsOutView(view: View, visible: Boolean) {
118         view.animate().cancel()
119 
120         val alpha = if (visible) 1f else 0f
121         val duration = if (visible) 750L else 250L
122         val visibility = if (visible) View.VISIBLE else View.GONE
123 
124         if (visible) {
125             view.alpha = 0f
126             view.visibility = View.VISIBLE
127         }
128 
129         view
130             .animate()
131             .alpha(alpha)
132             .setDuration(duration)
133             .setListener(
134                 object : AnimatorListenerAdapter() {
135                     override fun onAnimationEnd(animation: Animator) {
136                         view.alpha = alpha
137                         view.visibility = visibility
138                         // Unset the listener, otherwise this may persist for
139                         // another view property animation
140                         view.animate().setListener(null)
141                     }
142                 }
143             )
144             .start()
145     }
146 }
147 
148 /** Listener for various events that may affect the status bar's visibility. */
149 interface StatusBarVisibilityChangeListener {
150     /**
151      * Called when the status bar visibility might have changed due to the device moving to a
152      * different state.
153      */
onStatusBarVisibilityMaybeChangednull154     fun onStatusBarVisibilityMaybeChanged()
155 
156     /** Called when a transition from lockscreen to dream has started. */
157     fun onTransitionFromLockscreenToDreamStarted()
158 
159     /** Called when the status of the ongoing activity chip (active or not active) has changed. */
160     fun onOngoingActivityStatusChanged(hasOngoingActivity: Boolean)
161 }
162