1 /* <lambda>null2 * 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.lockscreen 18 19 import android.app.ActivityOptions 20 import android.app.PendingIntent 21 import android.app.smartspace.SmartspaceConfig 22 import android.app.smartspace.SmartspaceManager 23 import android.app.smartspace.SmartspaceSession 24 import android.app.smartspace.SmartspaceTarget 25 import android.content.ContentResolver 26 import android.content.Context 27 import android.content.Intent 28 import android.database.ContentObserver 29 import android.net.Uri 30 import android.os.Handler 31 import android.os.UserHandle 32 import android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS 33 import android.provider.Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS 34 import android.provider.Settings.Secure.LOCK_SCREEN_WEATHER_ENABLED 35 import android.util.Log 36 import android.view.ContextThemeWrapper 37 import android.view.View 38 import android.view.ViewGroup 39 import androidx.annotation.VisibleForTesting 40 import com.android.keyguard.KeyguardUpdateMonitor 41 import com.android.keyguard.KeyguardUpdateMonitorCallback 42 import com.android.settingslib.Utils 43 import com.android.systemui.Dumpable 44 import com.android.systemui.Flags.smartspaceLockscreenViewmodel 45 import com.android.systemui.dagger.SysUISingleton 46 import com.android.systemui.dagger.qualifiers.Background 47 import com.android.systemui.dagger.qualifiers.Main 48 import com.android.systemui.dump.DumpManager 49 import com.android.systemui.flags.FeatureFlags 50 import com.android.systemui.flags.Flags 51 import com.android.systemui.keyguard.WakefulnessLifecycle 52 import com.android.systemui.plugins.ActivityStarter 53 import com.android.systemui.plugins.BcSmartspaceConfigPlugin 54 import com.android.systemui.plugins.BcSmartspaceDataPlugin 55 import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener 56 import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView 57 import com.android.systemui.plugins.BcSmartspaceDataPlugin.TimeChangedDelegate 58 import com.android.systemui.plugins.FalsingManager 59 import com.android.systemui.plugins.clocks.WeatherData 60 import com.android.systemui.plugins.statusbar.StatusBarStateController 61 import com.android.systemui.res.R 62 import com.android.systemui.settings.UserTracker 63 import com.android.systemui.shared.regionsampling.RegionSampler 64 import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DATE_SMARTSPACE_DATA_PLUGIN 65 import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.WEATHER_SMARTSPACE_DATA_PLUGIN 66 import com.android.systemui.smartspace.ui.binder.SmartspaceViewBinder 67 import com.android.systemui.smartspace.ui.viewmodel.SmartspaceViewModel 68 import com.android.systemui.statusbar.phone.KeyguardBypassController 69 import com.android.systemui.statusbar.policy.ConfigurationController 70 import com.android.systemui.statusbar.policy.DeviceProvisionedController 71 import com.android.systemui.util.asIndenting 72 import com.android.systemui.util.concurrency.Execution 73 import com.android.systemui.util.printCollection 74 import com.android.systemui.util.settings.SecureSettings 75 import com.android.systemui.util.time.SystemClock 76 import java.io.PrintWriter 77 import java.time.Instant 78 import java.util.Deque 79 import java.util.LinkedList 80 import java.util.Optional 81 import java.util.concurrent.Executor 82 import javax.inject.Inject 83 import javax.inject.Named 84 85 86 /** Controller for managing the smartspace view on the lockscreen */ 87 @SysUISingleton 88 class LockscreenSmartspaceController 89 @Inject 90 constructor( 91 private val context: Context, 92 private val featureFlags: FeatureFlags, 93 private val smartspaceManager: SmartspaceManager?, 94 private val activityStarter: ActivityStarter, 95 private val falsingManager: FalsingManager, 96 private val systemClock: SystemClock, 97 private val secureSettings: SecureSettings, 98 private val userTracker: UserTracker, 99 private val contentResolver: ContentResolver, 100 private val configurationController: ConfigurationController, 101 private val statusBarStateController: StatusBarStateController, 102 private val deviceProvisionedController: DeviceProvisionedController, 103 private val bypassController: KeyguardBypassController, 104 private val keyguardUpdateMonitor: KeyguardUpdateMonitor, 105 private val wakefulnessLifecycle: WakefulnessLifecycle, 106 private val smartspaceViewModelFactory: SmartspaceViewModel.Factory, 107 private val dumpManager: DumpManager, 108 private val execution: Execution, 109 @Main private val uiExecutor: Executor, 110 @Background private val bgExecutor: Executor, 111 @Main private val handler: Handler, 112 @Named(DATE_SMARTSPACE_DATA_PLUGIN) 113 optionalDatePlugin: Optional<BcSmartspaceDataPlugin>, 114 @Named(WEATHER_SMARTSPACE_DATA_PLUGIN) 115 optionalWeatherPlugin: Optional<BcSmartspaceDataPlugin>, 116 optionalPlugin: Optional<BcSmartspaceDataPlugin>, 117 optionalConfigPlugin: Optional<BcSmartspaceConfigPlugin>, 118 ) : Dumpable { 119 companion object { 120 private const val TAG = "LockscreenSmartspaceController" 121 122 private const val MAX_RECENT_SMARTSPACE_DATA_FOR_DUMP = 5 123 } 124 125 private var session: SmartspaceSession? = null 126 private val datePlugin: BcSmartspaceDataPlugin? = optionalDatePlugin.orElse(null) 127 private val weatherPlugin: BcSmartspaceDataPlugin? = optionalWeatherPlugin.orElse(null) 128 private val plugin: BcSmartspaceDataPlugin? = optionalPlugin.orElse(null) 129 private val configPlugin: BcSmartspaceConfigPlugin? = optionalConfigPlugin.orElse(null) 130 131 // This stores recently received Smartspace pushes to be included in dumpsys. 132 private val recentSmartspaceData: Deque<List<SmartspaceTarget>> = LinkedList() 133 134 // Smartspace can be used on multiple displays, such as when the user casts their screen 135 @VisibleForTesting var smartspaceViews = mutableSetOf<SmartspaceView>() 136 private var regionSamplers = 137 mutableMapOf<SmartspaceView, RegionSampler>() 138 139 private val regionSamplingEnabled = 140 featureFlags.isEnabled(Flags.REGION_SAMPLING) 141 private var isRegionSamplersCreated = false 142 private var showNotifications = false 143 private var showSensitiveContentForCurrentUser = false 144 private var showSensitiveContentForManagedUser = false 145 private var managedUserHandle: UserHandle? = null 146 private var mSplitShadeEnabled = false 147 148 var suppressDisconnects = false 149 set(value) { 150 field = value 151 disconnect() 152 } 153 154 // TODO(b/202758428): refactor so that we can test color updates via region samping, similar to 155 // how we test color updates when theme changes (See testThemeChangeUpdatesTextColor). 156 157 // TODO: Move logic into SmartspaceView 158 var stateChangeListener = object : View.OnAttachStateChangeListener { 159 override fun onViewAttachedToWindow(v: View) { 160 (v as SmartspaceView).setSplitShadeEnabled(mSplitShadeEnabled) 161 smartspaceViews.add(v as SmartspaceView) 162 163 connectSession() 164 165 updateTextColorFromWallpaper() 166 statusBarStateListener.onDozeAmountChanged(0f, statusBarStateController.dozeAmount) 167 168 if (regionSamplingEnabled && (!regionSamplers.containsKey(v))) { 169 var regionSampler = RegionSampler( 170 v as View, 171 uiExecutor, 172 bgExecutor, 173 regionSamplingEnabled, 174 isLockscreen = true, 175 ) { updateTextColorFromRegionSampler() } 176 initializeTextColors(regionSampler) 177 regionSamplers[v] = regionSampler 178 regionSampler.startRegionSampler() 179 } 180 } 181 182 override fun onViewDetachedFromWindow(v: View) { 183 smartspaceViews.remove(v as SmartspaceView) 184 185 regionSamplers[v]?.stopRegionSampler() 186 regionSamplers.remove(v as SmartspaceView) 187 188 if (smartspaceViews.isEmpty()) { 189 disconnect() 190 } 191 } 192 } 193 194 private val sessionListener = SmartspaceSession.OnTargetsAvailableListener { targets -> 195 execution.assertIsMainThread() 196 197 // The weather data plugin takes unfiltered targets and performs the filtering internally. 198 weatherPlugin?.onTargetsAvailable(targets) 199 200 val now = Instant.ofEpochMilli(systemClock.currentTimeMillis()) 201 val weatherTarget = targets.find { t -> 202 t.featureType == SmartspaceTarget.FEATURE_WEATHER && 203 now.isAfter(Instant.ofEpochMilli(t.creationTimeMillis)) && 204 now.isBefore(Instant.ofEpochMilli(t.expiryTimeMillis)) 205 } 206 if (weatherTarget != null) { 207 val clickIntent = weatherTarget.headerAction?.intent 208 val weatherData = weatherTarget.baseAction?.extras?.let { extras -> 209 WeatherData.fromBundle( 210 extras, 211 ) { _ -> 212 if (!falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { 213 activityStarter.startActivity( 214 clickIntent, 215 true, /* dismissShade */ 216 null, 217 false) 218 } 219 } 220 } 221 222 if (weatherData != null) { 223 keyguardUpdateMonitor.sendWeatherData(weatherData) 224 } 225 } 226 227 val filteredTargets = targets.filter(::filterSmartspaceTarget) 228 229 synchronized(recentSmartspaceData) { 230 recentSmartspaceData.offerLast(filteredTargets) 231 if (recentSmartspaceData.size > MAX_RECENT_SMARTSPACE_DATA_FOR_DUMP) { 232 recentSmartspaceData.pollFirst() 233 } 234 } 235 236 plugin?.onTargetsAvailable(filteredTargets) 237 } 238 239 private val userTrackerCallback = object : UserTracker.Callback { 240 override fun onUserChanged(newUser: Int, userContext: Context) { 241 execution.assertIsMainThread() 242 reloadSmartspace() 243 } 244 } 245 246 private val settingsObserver = object : ContentObserver(handler) { 247 override fun onChange(selfChange: Boolean, uri: Uri?) { 248 execution.assertIsMainThread() 249 reloadSmartspace() 250 } 251 } 252 253 private val configChangeListener = object : ConfigurationController.ConfigurationListener { 254 override fun onThemeChanged() { 255 execution.assertIsMainThread() 256 updateTextColorFromWallpaper() 257 } 258 } 259 260 private val statusBarStateListener = object : StatusBarStateController.StateListener { 261 override fun onDozeAmountChanged(linear: Float, eased: Float) { 262 execution.assertIsMainThread() 263 smartspaceViews.forEach { it.setDozeAmount(eased) } 264 } 265 266 override fun onDozingChanged(isDozing: Boolean) { 267 execution.assertIsMainThread() 268 smartspaceViews.forEach { it.setDozing(isDozing) } 269 } 270 } 271 272 private val deviceProvisionedListener = 273 object : DeviceProvisionedController.DeviceProvisionedListener { 274 override fun onDeviceProvisionedChanged() { 275 connectSession() 276 } 277 278 override fun onUserSetupChanged() { 279 connectSession() 280 } 281 } 282 283 private val bypassStateChangedListener = 284 object : KeyguardBypassController.OnBypassStateChangedListener { 285 override fun onBypassStateChanged(isEnabled: Boolean) { 286 updateBypassEnabled() 287 } 288 } 289 290 // TODO(b/331451011): Refactor to viewmodel and use interactor pattern. 291 private val wakefulnessLifecycleObserver = 292 object : WakefulnessLifecycle.Observer { 293 override fun onStartedWakingUp() { 294 smartspaceViews.forEach { it.setScreenOn(true) } 295 } 296 297 override fun onFinishedGoingToSleep() { 298 smartspaceViews.forEach { it.setScreenOn(false) } 299 } 300 } 301 302 init { 303 deviceProvisionedController.addCallback(deviceProvisionedListener) 304 dumpManager.registerDumpable(this) 305 } 306 307 fun isEnabled(): Boolean { 308 execution.assertIsMainThread() 309 310 return plugin != null 311 } 312 313 fun isDateWeatherDecoupled(): Boolean { 314 execution.assertIsMainThread() 315 316 return datePlugin != null && weatherPlugin != null 317 } 318 319 fun isWeatherEnabled(): Boolean { 320 execution.assertIsMainThread() 321 val showWeather = secureSettings.getIntForUser( 322 LOCK_SCREEN_WEATHER_ENABLED, 323 1, 324 userTracker.userId) == 1 325 return showWeather 326 } 327 328 private fun updateBypassEnabled() { 329 val bypassEnabled = bypassController.bypassEnabled 330 smartspaceViews.forEach { it.setKeyguardBypassEnabled(bypassEnabled) } 331 } 332 333 /** 334 * Constructs the date view and connects it to the smartspace service. 335 */ 336 fun buildAndConnectDateView(parent: ViewGroup): View? { 337 execution.assertIsMainThread() 338 339 if (!isEnabled()) { 340 throw RuntimeException("Cannot build view when not enabled") 341 } 342 if (!isDateWeatherDecoupled()) { 343 throw RuntimeException("Cannot build date view when not decoupled") 344 } 345 346 val view = 347 buildView( 348 surfaceName = SmartspaceViewModel.SURFACE_DATE_VIEW, 349 parent = parent, 350 plugin = datePlugin 351 ) 352 connectSession() 353 354 return view 355 } 356 357 /** 358 * Constructs the weather view and connects it to the smartspace service. 359 */ 360 fun buildAndConnectWeatherView(parent: ViewGroup): View? { 361 execution.assertIsMainThread() 362 363 if (!isEnabled()) { 364 throw RuntimeException("Cannot build view when not enabled") 365 } 366 if (!isDateWeatherDecoupled()) { 367 throw RuntimeException("Cannot build weather view when not decoupled") 368 } 369 370 val view = 371 buildView( 372 surfaceName = SmartspaceViewModel.SURFACE_WEATHER_VIEW, 373 parent = parent, 374 plugin = weatherPlugin 375 ) 376 connectSession() 377 378 return view 379 } 380 381 /** 382 * Constructs the smartspace view and connects it to the smartspace service. 383 */ 384 fun buildAndConnectView(parent: ViewGroup): View? { 385 execution.assertIsMainThread() 386 387 if (!isEnabled()) { 388 throw RuntimeException("Cannot build view when not enabled") 389 } 390 391 val view = 392 buildView( 393 surfaceName = SmartspaceViewModel.SURFACE_GENERAL_VIEW, 394 parent = parent, 395 plugin = plugin, 396 configPlugin = configPlugin 397 ) 398 connectSession() 399 400 return view 401 } 402 403 private fun buildView( 404 surfaceName: String, 405 parent: ViewGroup, 406 plugin: BcSmartspaceDataPlugin?, 407 configPlugin: BcSmartspaceConfigPlugin? = null 408 ): View? { 409 if (plugin == null) { 410 return null 411 } 412 413 val ssView = plugin.getView(parent) 414 configPlugin?.let { ssView.registerConfigProvider(it) } 415 ssView.setUiSurface(BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD) 416 ssView.setTimeChangedDelegate(SmartspaceTimeChangedDelegate(keyguardUpdateMonitor)) 417 ssView.registerDataProvider(plugin) 418 419 ssView.setIntentStarter(object : BcSmartspaceDataPlugin.IntentStarter { 420 override fun startIntent(view: View, intent: Intent, showOnLockscreen: Boolean) { 421 if (showOnLockscreen) { 422 activityStarter.startActivity( 423 intent, 424 true, /* dismissShade */ 425 // launch animator - looks bad with the transparent smartspace bg 426 null, 427 true 428 ) 429 } else { 430 activityStarter.postStartActivityDismissingKeyguard(intent, 0) 431 } 432 } 433 434 override fun startPendingIntent( 435 view: View, 436 pi: PendingIntent, 437 showOnLockscreen: Boolean 438 ) { 439 if (showOnLockscreen) { 440 val options = ActivityOptions.makeBasic() 441 .setPendingIntentBackgroundActivityStartMode( 442 ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED) 443 .toBundle() 444 pi.send(options) 445 } else { 446 activityStarter.postStartActivityDismissingKeyguard(pi) 447 } 448 } 449 }) 450 ssView.setFalsingManager(falsingManager) 451 ssView.setKeyguardBypassEnabled(bypassController.bypassEnabled) 452 return (ssView as View).apply { 453 setTag(R.id.tag_smartspace_view, Any()) 454 addOnAttachStateChangeListener(stateChangeListener) 455 456 if (smartspaceLockscreenViewmodel()) { 457 val viewModel = smartspaceViewModelFactory.create(surfaceName) 458 SmartspaceViewBinder.bind( 459 smartspaceView = ssView, 460 viewModel = viewModel, 461 ) 462 } 463 } 464 } 465 466 private fun connectSession() { 467 if (smartspaceManager == null) return 468 if (datePlugin == null && weatherPlugin == null && plugin == null) return 469 if (session != null || smartspaceViews.isEmpty()) { 470 return 471 } 472 473 // Only connect after the device is fully provisioned to avoid connection caching 474 // issues 475 if (!deviceProvisionedController.isDeviceProvisioned() || 476 !deviceProvisionedController.isCurrentUserSetup()) { 477 return 478 } 479 480 val newSession = smartspaceManager.createSmartspaceSession( 481 SmartspaceConfig.Builder( 482 context, BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD).build()) 483 Log.d(TAG, "Starting smartspace session for " + 484 BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD) 485 newSession.addOnTargetsAvailableListener(uiExecutor, sessionListener) 486 this.session = newSession 487 488 deviceProvisionedController.removeCallback(deviceProvisionedListener) 489 userTracker.addCallback(userTrackerCallback, uiExecutor) 490 contentResolver.registerContentObserver( 491 secureSettings.getUriFor(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS), 492 true, 493 settingsObserver, 494 UserHandle.USER_ALL 495 ) 496 contentResolver.registerContentObserver( 497 secureSettings.getUriFor(LOCK_SCREEN_SHOW_NOTIFICATIONS), 498 true, 499 settingsObserver, 500 UserHandle.USER_ALL 501 ) 502 configurationController.addCallback(configChangeListener) 503 statusBarStateController.addCallback(statusBarStateListener) 504 bypassController.registerOnBypassStateChangedListener(bypassStateChangedListener) 505 if (!smartspaceLockscreenViewmodel()) { 506 wakefulnessLifecycle.addObserver(wakefulnessLifecycleObserver) 507 } 508 509 datePlugin?.registerSmartspaceEventNotifier { e -> session?.notifySmartspaceEvent(e) } 510 weatherPlugin?.registerSmartspaceEventNotifier { e -> session?.notifySmartspaceEvent(e) } 511 plugin?.registerSmartspaceEventNotifier { e -> session?.notifySmartspaceEvent(e) } 512 513 updateBypassEnabled() 514 reloadSmartspace() 515 } 516 517 fun setSplitShadeEnabled(enabled: Boolean) { 518 mSplitShadeEnabled = enabled 519 smartspaceViews.forEach { it.setSplitShadeEnabled(enabled) } 520 } 521 522 /** 523 * Requests the smartspace session for an update. 524 */ 525 fun requestSmartspaceUpdate() { 526 session?.requestSmartspaceUpdate() 527 } 528 529 /** 530 * Disconnects the smartspace view from the smartspace service and cleans up any resources. 531 */ 532 fun disconnect() { 533 if (!smartspaceViews.isEmpty()) return 534 if (suppressDisconnects) return 535 536 execution.assertIsMainThread() 537 538 if (session == null) { 539 return 540 } 541 542 session?.let { 543 it.removeOnTargetsAvailableListener(sessionListener) 544 it.close() 545 } 546 userTracker.removeCallback(userTrackerCallback) 547 contentResolver.unregisterContentObserver(settingsObserver) 548 configurationController.removeCallback(configChangeListener) 549 statusBarStateController.removeCallback(statusBarStateListener) 550 bypassController.unregisterOnBypassStateChangedListener(bypassStateChangedListener) 551 if (!smartspaceLockscreenViewmodel()) { 552 wakefulnessLifecycle.removeObserver(wakefulnessLifecycleObserver) 553 } 554 session = null 555 556 datePlugin?.registerSmartspaceEventNotifier(null) 557 558 weatherPlugin?.registerSmartspaceEventNotifier(null) 559 weatherPlugin?.onTargetsAvailable(emptyList()) 560 561 plugin?.registerSmartspaceEventNotifier(null) 562 plugin?.onTargetsAvailable(emptyList()) 563 564 Log.d(TAG, "Ended smartspace session for lockscreen") 565 } 566 567 fun addListener(listener: SmartspaceTargetListener) { 568 execution.assertIsMainThread() 569 plugin?.registerListener(listener) 570 } 571 572 fun removeListener(listener: SmartspaceTargetListener) { 573 execution.assertIsMainThread() 574 plugin?.unregisterListener(listener) 575 } 576 577 private fun filterSmartspaceTarget(t: SmartspaceTarget): Boolean { 578 if (isDateWeatherDecoupled() && t.featureType == SmartspaceTarget.FEATURE_WEATHER) { 579 return false 580 } 581 if (!showNotifications) { 582 return t.featureType == SmartspaceTarget.FEATURE_WEATHER 583 } 584 return when (t.userHandle) { 585 userTracker.userHandle -> { 586 !t.isSensitive || showSensitiveContentForCurrentUser 587 } 588 managedUserHandle -> { 589 // Really, this should be "if this managed profile is associated with the current 590 // active user", but we don't have a good way to check that, so instead we cheat: 591 // Only the primary user can have an associated managed profile, so only show 592 // content for the managed profile if the primary user is active 593 userTracker.userHandle.identifier == UserHandle.USER_SYSTEM && 594 (!t.isSensitive || showSensitiveContentForManagedUser) 595 } 596 else -> { 597 false 598 } 599 } 600 } 601 602 private fun initializeTextColors(regionSampler: RegionSampler) { 603 val lightThemeContext = ContextThemeWrapper(context, R.style.Theme_SystemUI_LightWallpaper) 604 val darkColor = Utils.getColorAttrDefaultColor(lightThemeContext, R.attr.wallpaperTextColor) 605 606 val darkThemeContext = ContextThemeWrapper(context, R.style.Theme_SystemUI) 607 val lightColor = Utils.getColorAttrDefaultColor(darkThemeContext, R.attr.wallpaperTextColor) 608 609 regionSampler.setForegroundColors(lightColor, darkColor) 610 } 611 612 private fun updateTextColorFromRegionSampler() { 613 regionSamplers.forEach { (view, region) -> 614 val textColor = region.currentForegroundColor() 615 if (textColor != null) { 616 view.setPrimaryTextColor(textColor) 617 } 618 } 619 } 620 621 private fun updateTextColorFromWallpaper() { 622 if (!regionSamplingEnabled || regionSamplers.isEmpty()) { 623 val wallpaperTextColor = 624 Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColor) 625 smartspaceViews.forEach { it.setPrimaryTextColor(wallpaperTextColor) } 626 } else { 627 updateTextColorFromRegionSampler() 628 } 629 } 630 631 private fun reloadSmartspace() { 632 showNotifications = secureSettings.getIntForUser( 633 LOCK_SCREEN_SHOW_NOTIFICATIONS, 634 0, 635 userTracker.userId 636 ) == 1 637 638 showSensitiveContentForCurrentUser = secureSettings.getIntForUser( 639 LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 640 0, 641 userTracker.userId 642 ) == 1 643 644 managedUserHandle = getWorkProfileUser() 645 val managedId = managedUserHandle?.identifier 646 if (managedId != null) { 647 showSensitiveContentForManagedUser = secureSettings.getIntForUser( 648 LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 649 0, 650 managedId 651 ) == 1 652 } 653 654 session?.requestSmartspaceUpdate() 655 } 656 657 private fun getWorkProfileUser(): UserHandle? { 658 for (userInfo in userTracker.userProfiles) { 659 if (userInfo.isManagedProfile) { 660 return userInfo.userHandle 661 } 662 } 663 return null 664 } 665 666 override fun dump(pw: PrintWriter, args: Array<out String>) { 667 pw.asIndenting().run { 668 printCollection("Region Samplers", regionSamplers.values) { 669 it.dump(this) 670 } 671 } 672 673 pw.println("Recent BC Smartspace Targets (most recent first)") 674 synchronized(recentSmartspaceData) { 675 if (recentSmartspaceData.size === 0) { 676 pw.println(" No data\n") 677 return 678 } 679 recentSmartspaceData.descendingIterator().forEachRemaining { smartspaceTargets -> 680 pw.println(" Number of targets: ${smartspaceTargets.size}") 681 for (target in smartspaceTargets) { 682 pw.println(" $target") 683 } 684 pw.println() 685 } 686 } 687 } 688 689 private class SmartspaceTimeChangedDelegate( 690 private val keyguardUpdateMonitor: KeyguardUpdateMonitor 691 ) : TimeChangedDelegate { 692 private var keyguardUpdateMonitorCallback: KeyguardUpdateMonitorCallback? = null 693 override fun register(callback: Runnable) { 694 if (keyguardUpdateMonitorCallback != null) { 695 unregister() 696 } 697 keyguardUpdateMonitorCallback = object : KeyguardUpdateMonitorCallback() { 698 override fun onTimeChanged() { 699 callback.run() 700 } 701 } 702 keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback) 703 callback.run() 704 } 705 706 override fun unregister() { 707 keyguardUpdateMonitor.removeCallback(keyguardUpdateMonitorCallback) 708 keyguardUpdateMonitorCallback = null 709 } 710 } 711 } 712 713