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