1 /*
2  * Copyright (C) 2022 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 package com.android.systemui.smartspace.filters
17 
18 import android.app.smartspace.SmartspaceTarget
19 import android.content.ContentResolver
20 import android.content.Context
21 import android.database.ContentObserver
22 import android.net.Uri
23 import android.os.Handler
24 import android.os.UserHandle
25 import android.provider.Settings
26 import com.android.systemui.dagger.qualifiers.Main
27 import com.android.systemui.settings.UserTracker
28 import com.android.systemui.smartspace.SmartspaceTargetFilter
29 import com.android.systemui.util.concurrency.Execution
30 import com.android.systemui.util.settings.SecureSettings
31 import java.util.concurrent.Executor
32 import javax.inject.Inject
33 
34 /** {@link SmartspaceTargetFilter} for smartspace targets that show above the lockscreen. */
35 class LockscreenTargetFilter
36 @Inject
37 constructor(
38     private val secureSettings: SecureSettings,
39     private val userTracker: UserTracker,
40     private val execution: Execution,
41     @Main private val handler: Handler,
42     private val contentResolver: ContentResolver,
43     @Main private val uiExecutor: Executor
44 ) : SmartspaceTargetFilter {
45     private var listeners: MutableSet<SmartspaceTargetFilter.Listener> = mutableSetOf()
46     private var showSensitiveContentForCurrentUser = false
47         set(value) {
48             val existing = field
49             field = value
50             if (existing != field) {
<lambda>null51                 listeners.forEach { it.onCriteriaChanged() }
52             }
53         }
54     private var showSensitiveContentForManagedUser = false
55         set(value) {
56             val existing = field
57             field = value
58             if (existing != field) {
<lambda>null59                 listeners.forEach { it.onCriteriaChanged() }
60             }
61         }
62 
63     private val settingsObserver =
64         object : ContentObserver(handler) {
onChangenull65             override fun onChange(selfChange: Boolean, uri: Uri?) {
66                 execution.assertIsMainThread()
67                 updateUserContentSettings()
68             }
69         }
70 
71     private var managedUserHandle: UserHandle? = null
72 
addListenernull73     override fun addListener(listener: SmartspaceTargetFilter.Listener) {
74         listeners.add(listener)
75 
76         if (listeners.size != 1) {
77             return
78         }
79 
80         userTracker.addCallback(userTrackerCallback, uiExecutor)
81 
82         contentResolver.registerContentObserver(
83             secureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
84             true,
85             settingsObserver,
86             UserHandle.USER_ALL
87         )
88 
89         updateUserContentSettings()
90     }
91 
removeListenernull92     override fun removeListener(listener: SmartspaceTargetFilter.Listener) {
93         listeners.remove(listener)
94 
95         if (listeners.isNotEmpty()) {
96             return
97         }
98 
99         userTracker.removeCallback(userTrackerCallback)
100         contentResolver.unregisterContentObserver(settingsObserver)
101     }
102 
filterSmartspaceTargetnull103     override fun filterSmartspaceTarget(t: SmartspaceTarget): Boolean {
104         return when (t.userHandle) {
105             userTracker.userHandle -> {
106                 !t.isSensitive || showSensitiveContentForCurrentUser
107             }
108             managedUserHandle -> {
109                 // Really, this should be "if this managed profile is associated with the current
110                 // active user", but we don't have a good way to check that, so instead we cheat:
111                 // Only the primary user can have an associated managed profile, so only show
112                 // content for the managed profile if the primary user is active
113                 userTracker.userHandle.identifier == UserHandle.USER_SYSTEM &&
114                     (!t.isSensitive || showSensitiveContentForManagedUser)
115             }
116             else -> {
117                 false
118             }
119         }
120     }
121 
122     private val userTrackerCallback =
123         object : UserTracker.Callback {
onUserChangednull124             override fun onUserChanged(newUser: Int, userContext: Context) {
125                 execution.assertIsMainThread()
126                 updateUserContentSettings()
127             }
128         }
129 
getWorkProfileUsernull130     private fun getWorkProfileUser(): UserHandle? {
131         for (userInfo in userTracker.userProfiles) {
132             if (userInfo.isManagedProfile) {
133                 return userInfo.userHandle
134             }
135         }
136         return null
137     }
138 
updateUserContentSettingsnull139     private fun updateUserContentSettings() {
140         val setting = Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS
141 
142         showSensitiveContentForCurrentUser =
143             secureSettings.getIntForUser(setting, 0, userTracker.userId) == 1
144 
145         managedUserHandle = getWorkProfileUser()
146         val managedId = managedUserHandle?.identifier
147         if (managedId != null) {
148             showSensitiveContentForManagedUser =
149                 secureSettings.getIntForUser(setting, 0, managedId) == 1
150         }
151 
152         listeners.forEach { it.onCriteriaChanged() }
153     }
154 }
155