1 /*
2  * Copyright (C) 2021 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.notification.collection.coordinator
18 
19 import android.util.Log
20 import com.android.internal.widget.MessagingGroup
21 import com.android.internal.widget.MessagingMessage
22 import com.android.keyguard.KeyguardUpdateMonitor
23 import com.android.keyguard.KeyguardUpdateMonitorCallback
24 import com.android.systemui.statusbar.NotificationLockscreenUserManager
25 import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener
26 import com.android.systemui.statusbar.notification.collection.NotifPipeline
27 import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
28 import com.android.systemui.statusbar.notification.row.NotificationGutsManager
29 import com.android.systemui.statusbar.policy.ConfigurationController
30 import com.android.systemui.util.Compile
31 import javax.inject.Inject
32 
33 /**
34  * A coordinator which ensures that notifications within the new pipeline are correctly inflated
35  * for the current uiMode and screen properties; additionally deferring those changes when a user
36  * change is in progress until that process has completed.
37  */
38 @CoordinatorScope
39 class ViewConfigCoordinator @Inject internal constructor(
40     private val mConfigurationController: ConfigurationController,
41     private val mLockscreenUserManager: NotificationLockscreenUserManager,
42     private val mGutsManager: NotificationGutsManager,
43     private val mKeyguardUpdateMonitor: KeyguardUpdateMonitor
44 ) : Coordinator, ConfigurationController.ConfigurationListener {
45 
46     private var mIsSwitchingUser = false
47     private var mReinflateNotificationsOnUserSwitched = false
48     private var mDispatchUiModeChangeOnUserSwitched = false
49     private var mPipeline: NotifPipeline? = null
50 
51     private val mKeyguardUpdateCallback = object : KeyguardUpdateMonitorCallback() {
52         override fun onUserSwitching(userId: Int) {
53             log { "ViewConfigCoordinator.onUserSwitching(userId=$userId)" }
54             mIsSwitchingUser = true
55         }
56 
57         override fun onUserSwitchComplete(userId: Int) {
58             log { "ViewConfigCoordinator.onUserSwitchComplete(userId=$userId)" }
59             mIsSwitchingUser = false
60             applyChangesOnUserSwitched()
61         }
62     }
63 
64     private val mUserChangedListener = object : UserChangedListener {
65         override fun onUserChanged(userId: Int) {
66             log { "ViewConfigCoordinator.onUserChanged(userId=$userId)" }
67             applyChangesOnUserSwitched()
68         }
69     }
70 
71     override fun attach(pipeline: NotifPipeline) {
72         mPipeline = pipeline
73         mLockscreenUserManager.addUserChangedListener(mUserChangedListener)
74         mConfigurationController.addCallback(this)
75         mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback)
76     }
77 
78     override fun onDensityOrFontScaleChanged() {
79         log {
80             val keyguardIsSwitchingUser = mKeyguardUpdateMonitor.isSwitchingUser
81             "ViewConfigCoordinator.onDensityOrFontScaleChanged()" +
82                     " isSwitchingUser=$mIsSwitchingUser" +
83                     " keyguardUpdateMonitor.isSwitchingUser=$keyguardIsSwitchingUser"
84         }
85         MessagingMessage.dropCache()
86         MessagingGroup.dropCache()
87         if (!mIsSwitchingUser) {
88             updateNotificationsOnDensityOrFontScaleChanged()
89         } else {
90             mReinflateNotificationsOnUserSwitched = true
91         }
92     }
93 
94     override fun onUiModeChanged() {
95         log {
96             val keyguardIsSwitchingUser = mKeyguardUpdateMonitor.isSwitchingUser
97             "ViewConfigCoordinator.onUiModeChanged()" +
98                     " isSwitchingUser=$mIsSwitchingUser" +
99                     " keyguardUpdateMonitor.isSwitchingUser=$keyguardIsSwitchingUser"
100         }
101         if (!mIsSwitchingUser) {
102             updateNotificationsOnUiModeChanged()
103         } else {
104             mDispatchUiModeChangeOnUserSwitched = true
105         }
106     }
107 
108     override fun onThemeChanged() {
109         onDensityOrFontScaleChanged()
110     }
111 
112     private fun applyChangesOnUserSwitched() {
113         if (mReinflateNotificationsOnUserSwitched) {
114             updateNotificationsOnDensityOrFontScaleChanged()
115             mReinflateNotificationsOnUserSwitched = false
116         }
117         if (mDispatchUiModeChangeOnUserSwitched) {
118             updateNotificationsOnUiModeChanged()
119             mDispatchUiModeChangeOnUserSwitched = false
120         }
121     }
122 
123     private fun updateNotificationsOnUiModeChanged() {
124         log { "ViewConfigCoordinator.updateNotificationsOnUiModeChanged()" }
125         mPipeline?.allNotifs?.forEach { entry ->
126             entry.row?.onUiModeChanged()
127         }
128     }
129 
130     private fun updateNotificationsOnDensityOrFontScaleChanged() {
131         mPipeline?.allNotifs?.forEach { entry ->
132             entry.onDensityOrFontScaleChanged()
133             val exposedGuts = entry.areGutsExposed()
134             if (exposedGuts) {
135                 mGutsManager.onDensityOrFontScaleChanged(entry)
136             }
137         }
138     }
139 
140     private inline fun log(message: () -> String) {
141         if (DEBUG) Log.d(TAG, message())
142     }
143 
144     companion object {
145         private const val TAG = "ViewConfigCoordinator"
146         private val DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG)
147     }
148 }
149