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 /** 35 * {@link SmartspaceTargetFilter} for smartspace targets that show above the lockscreen and dreams. 36 */ 37 class LockscreenAndDreamTargetFilter @Inject 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) { 51 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) { 59 listeners.forEach { it.onCriteriaChanged() } 60 } 61 } 62 63 private val settingsObserver = object : ContentObserver(handler) { 64 override fun onChange(selfChange: Boolean, uri: Uri?) { 65 execution.assertIsMainThread() 66 updateUserContentSettings() 67 } 68 } 69 70 private var managedUserHandle: UserHandle? = null 71 72 override fun addListener(listener: SmartspaceTargetFilter.Listener) { 73 listeners.add(listener) 74 75 if (listeners.size != 1) { 76 return 77 } 78 79 userTracker.addCallback(userTrackerCallback, uiExecutor) 80 81 contentResolver.registerContentObserver( 82 secureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS), 83 true, 84 settingsObserver, 85 UserHandle.USER_ALL 86 ) 87 88 updateUserContentSettings() 89 } 90 91 override fun removeListener(listener: SmartspaceTargetFilter.Listener) { 92 listeners.remove(listener) 93 94 if (listeners.isNotEmpty()) { 95 return 96 } 97 98 userTracker.removeCallback(userTrackerCallback) 99 contentResolver.unregisterContentObserver(settingsObserver) 100 } 101 102 override fun filterSmartspaceTarget(t: SmartspaceTarget): Boolean { 103 return when (t.userHandle) { 104 userTracker.userHandle -> { 105 !t.isSensitive || showSensitiveContentForCurrentUser 106 } 107 managedUserHandle -> { 108 // Really, this should be "if this managed profile is associated with the current 109 // active user", but we don't have a good way to check that, so instead we cheat: 110 // Only the primary user can have an associated managed profile, so only show 111 // content for the managed profile if the primary user is active 112 userTracker.userHandle.identifier == UserHandle.USER_SYSTEM && 113 (!t.isSensitive || showSensitiveContentForManagedUser) 114 } 115 else -> { 116 false 117 } 118 } 119 } 120 121 private val userTrackerCallback = object : UserTracker.Callback { 122 override fun onUserChanged(newUser: Int, userContext: Context) { 123 execution.assertIsMainThread() 124 updateUserContentSettings() 125 } 126 } 127 128 private fun getWorkProfileUser(): UserHandle? { 129 for (userInfo in userTracker.userProfiles) { 130 if (userInfo.isManagedProfile) { 131 return userInfo.userHandle 132 } 133 } 134 return null 135 } 136 137 private fun updateUserContentSettings() { 138 val setting = Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS 139 140 showSensitiveContentForCurrentUser = 141 secureSettings.getIntForUser(setting, 0, userTracker.userId) == 1 142 143 managedUserHandle = getWorkProfileUser() 144 val managedId = managedUserHandle?.identifier 145 if (managedId != null) { 146 showSensitiveContentForManagedUser = 147 secureSettings.getIntForUser(setting, 0, managedId) == 1 148 } 149 150 listeners.forEach { it.onCriteriaChanged() } 151 } 152 } 153