1 /* <lambda>null2 * 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 */ 17 18 package com.android.systemui.keyguard.ui.preview 19 20 import android.app.WallpaperColors 21 import android.content.BroadcastReceiver 22 import android.content.Context 23 import android.content.Intent 24 import android.content.IntentFilter 25 import android.content.res.Resources 26 import android.graphics.Rect 27 import android.hardware.display.DisplayManager 28 import android.os.Bundle 29 import android.os.Handler 30 import android.os.IBinder 31 import android.provider.Settings 32 import android.util.Log 33 import android.view.ContextThemeWrapper 34 import android.view.Display 35 import android.view.Display.DEFAULT_DISPLAY 36 import android.view.DisplayInfo 37 import android.view.LayoutInflater 38 import android.view.SurfaceControlViewHost 39 import android.view.View 40 import android.view.ViewGroup 41 import android.view.WindowManager 42 import android.widget.FrameLayout 43 import android.window.InputTransferToken 44 import androidx.constraintlayout.widget.ConstraintLayout 45 import androidx.constraintlayout.widget.ConstraintSet 46 import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID 47 import androidx.constraintlayout.widget.ConstraintSet.START 48 import androidx.constraintlayout.widget.ConstraintSet.TOP 49 import androidx.core.view.isInvisible 50 import com.android.keyguard.ClockEventController 51 import com.android.systemui.animation.view.LaunchableImageView 52 import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor 53 import com.android.systemui.broadcast.BroadcastDispatcher 54 import com.android.systemui.customization.R as customR 55 import com.android.systemui.dagger.qualifiers.Application 56 import com.android.systemui.dagger.qualifiers.Background 57 import com.android.systemui.dagger.qualifiers.Main 58 import com.android.systemui.keyguard.shared.model.ClockSizeSetting 59 import com.android.systemui.keyguard.ui.binder.KeyguardPreviewClockViewBinder 60 import com.android.systemui.keyguard.ui.binder.KeyguardPreviewSmartspaceViewBinder 61 import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder 62 import com.android.systemui.keyguard.ui.view.KeyguardRootView 63 import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection 64 import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewClockViewModel 65 import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewSmartspaceViewModel 66 import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModel 67 import com.android.systemui.monet.ColorScheme 68 import com.android.systemui.monet.Style 69 import com.android.systemui.plugins.clocks.ClockController 70 import com.android.systemui.plugins.clocks.ClockPreviewConfig 71 import com.android.systemui.plugins.clocks.ContextExt.getId 72 import com.android.systemui.plugins.clocks.ThemeConfig 73 import com.android.systemui.plugins.clocks.WeatherData 74 import com.android.systemui.res.R 75 import com.android.systemui.scene.shared.flag.SceneContainerFlag 76 import com.android.systemui.shade.domain.interactor.ShadeModeInteractor 77 import com.android.systemui.shared.clocks.ClockRegistry 78 import com.android.systemui.shared.clocks.shared.model.ClockPreviewConstants 79 import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots 80 import com.android.systemui.shared.quickaffordance.shared.model.KeyguardPreviewConstants 81 import com.android.systemui.statusbar.KeyguardIndicationController 82 import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController 83 import com.android.systemui.util.kotlin.DisposableHandles 84 import com.android.systemui.util.settings.SecureSettings 85 import com.android.systemui.wallpapers.domain.interactor.WallpaperFocalAreaInteractor 86 import dagger.assisted.Assisted 87 import dagger.assisted.AssistedInject 88 import kotlinx.coroutines.CoroutineDispatcher 89 import kotlinx.coroutines.DisposableHandle 90 import kotlinx.coroutines.flow.flowOf 91 import kotlinx.coroutines.runBlocking 92 import kotlinx.coroutines.withContext 93 import org.json.JSONException 94 import org.json.JSONObject 95 96 /** Renders the preview of the lock screen. */ 97 class KeyguardPreviewRenderer 98 @AssistedInject 99 constructor( 100 @Application private val context: Context, 101 @Main private val mainDispatcher: CoroutineDispatcher, 102 @Main private val mainHandler: Handler, 103 @Background private val backgroundDispatcher: CoroutineDispatcher, 104 private val clockViewModel: KeyguardPreviewClockViewModel, 105 private val smartspaceViewModel: KeyguardPreviewSmartspaceViewModel, 106 private val quickAffordancesCombinedViewModel: KeyguardQuickAffordancesCombinedViewModel, 107 displayManager: DisplayManager, 108 private val windowManager: WindowManager, 109 private val clockController: ClockEventController, 110 private val clockRegistry: ClockRegistry, 111 private val broadcastDispatcher: BroadcastDispatcher, 112 private val lockscreenSmartspaceController: LockscreenSmartspaceController, 113 private val udfpsOverlayInteractor: UdfpsOverlayInteractor, 114 private val indicationController: KeyguardIndicationController, 115 @Assisted bundle: Bundle, 116 private val shadeModeInteractor: ShadeModeInteractor, 117 private val secureSettings: SecureSettings, 118 private val defaultShortcutsSection: DefaultShortcutsSection, 119 private val keyguardQuickAffordanceViewBinder: KeyguardQuickAffordanceViewBinder, 120 private val wallpaperFocalAreaInteractor: WallpaperFocalAreaInteractor, 121 ) { 122 val hostToken: IBinder? = bundle.getBinder(KEY_HOST_TOKEN) 123 private val width: Int = bundle.getInt(KEY_VIEW_WIDTH) 124 private val height: Int = bundle.getInt(KEY_VIEW_HEIGHT) 125 private val shouldHighlightSelectedAffordance: Boolean = 126 bundle.getBoolean(KeyguardPreviewConstants.KEY_HIGHLIGHT_QUICK_AFFORDANCES, false) 127 128 private val displayId = bundle.getInt(KEY_DISPLAY_ID, DEFAULT_DISPLAY) 129 private val display: Display? = displayManager.getDisplay(displayId) 130 131 /** 132 * Returns a key that should make the KeyguardPreviewRenderer unique and if two of them have the 133 * same key they will be treated as the same KeyguardPreviewRenderer. Primary this is used to 134 * prevent memory leaks by allowing removal of the old KeyguardPreviewRenderer. 135 */ 136 val id = Pair(hostToken, displayId) 137 138 /** [shouldHideClock] here means that we never create and bind the clock views */ 139 private val shouldHideClock: Boolean = 140 bundle.getBoolean(ClockPreviewConstants.KEY_HIDE_CLOCK, false) 141 private val wallpaperColors: WallpaperColors? = bundle.getParcelable(KEY_COLORS) 142 143 private var host: SurfaceControlViewHost 144 145 val surfacePackage: SurfaceControlViewHost.SurfacePackage 146 get() = checkNotNull(host.surfacePackage) 147 148 private var smartSpaceView: View? = null 149 private var largeDateView: View? = null 150 private var smallDateView: View? = null 151 152 private val disposables = DisposableHandles() 153 private var isDestroyed = false 154 155 private val shortcutsBindings = mutableSetOf<KeyguardQuickAffordanceViewBinder.Binding>() 156 157 @Style.Type private var themeStyle: Int? = null 158 159 init { 160 clockController.setFallbackWeatherData(WeatherData.getPlaceholderWeatherData()) 161 quickAffordancesCombinedViewModel.enablePreviewMode( 162 initiallySelectedSlotId = 163 bundle.getString(KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID) 164 ?: KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START, 165 shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance, 166 ) 167 168 clockViewModel.shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance 169 runBlocking(mainDispatcher) { 170 host = 171 SurfaceControlViewHost( 172 context, 173 displayManager.getDisplay(DEFAULT_DISPLAY), 174 if (hostToken == null) null else InputTransferToken(hostToken), 175 "KeyguardPreviewRenderer", 176 ) 177 disposables += DisposableHandle { host.release() } 178 } 179 } 180 181 fun render() { 182 mainHandler.post { 183 val previewContext = 184 display?.let { 185 ContextThemeWrapper(context.createDisplayContext(it), context.getTheme()) 186 } ?: context 187 188 val rootView = ConstraintLayout(previewContext) 189 190 setupKeyguardRootView(previewContext, rootView) 191 192 var displayInfo: DisplayInfo? = null 193 display?.let { 194 displayInfo = DisplayInfo() 195 it.getDisplayInfo(displayInfo) 196 } 197 rootView.measure( 198 View.MeasureSpec.makeMeasureSpec( 199 displayInfo?.logicalWidth ?: windowManager.currentWindowMetrics.bounds.width(), 200 View.MeasureSpec.EXACTLY, 201 ), 202 View.MeasureSpec.makeMeasureSpec( 203 displayInfo?.logicalHeight 204 ?: windowManager.currentWindowMetrics.bounds.height(), 205 View.MeasureSpec.EXACTLY, 206 ), 207 ) 208 rootView.layout(0, 0, rootView.measuredWidth, rootView.measuredHeight) 209 210 // This aspect scales the view to fit in the surface and centers it 211 val scale: Float = 212 (width / rootView.measuredWidth.toFloat()).coerceAtMost( 213 height / rootView.measuredHeight.toFloat() 214 ) 215 216 rootView.scaleX = scale 217 rootView.scaleY = scale 218 rootView.pivotX = 0f 219 rootView.pivotY = 0f 220 rootView.translationX = (width - scale * rootView.width) / 2 221 rootView.translationY = (height - scale * rootView.height) / 2 222 223 if (isDestroyed) { 224 return@post 225 } 226 227 host.setView(rootView, rootView.measuredWidth, rootView.measuredHeight) 228 } 229 } 230 231 fun onStartCustomizingQuickAffordances(initiallySelectedSlotId: String?) { 232 quickAffordancesCombinedViewModel.enablePreviewMode( 233 initiallySelectedSlotId = initiallySelectedSlotId, 234 shouldHighlightSelectedAffordance = true, 235 ) 236 } 237 238 fun onSlotSelected(slotId: String) { 239 quickAffordancesCombinedViewModel.onPreviewSlotSelected(slotId = slotId) 240 } 241 242 fun onPreviewQuickAffordanceSelected(slotId: String, quickAffordanceId: String) { 243 quickAffordancesCombinedViewModel.onPreviewQuickAffordanceSelected( 244 slotId, 245 quickAffordanceId, 246 ) 247 } 248 249 fun onDefaultPreview() { 250 quickAffordancesCombinedViewModel.onClearPreviewQuickAffordances() 251 quickAffordancesCombinedViewModel.enablePreviewMode( 252 initiallySelectedSlotId = null, 253 shouldHighlightSelectedAffordance = false, 254 ) 255 } 256 257 fun onClockSizeSelected(clockSize: ClockSizeSetting) { 258 smartspaceViewModel.setOverrideClockSize(clockSize) 259 if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) { 260 when (clockSize) { 261 ClockSizeSetting.DYNAMIC -> { 262 largeDateView?.post { 263 smallDateView?.visibility = View.GONE 264 largeDateView?.visibility = View.VISIBLE 265 } 266 } 267 268 ClockSizeSetting.SMALL -> { 269 largeDateView?.post { 270 smallDateView?.visibility = View.VISIBLE 271 largeDateView?.visibility = View.GONE 272 } 273 } 274 } 275 smartSpaceView?.post { smartSpaceView?.visibility = View.GONE } 276 } 277 } 278 279 fun destroy() { 280 isDestroyed = true 281 lockscreenSmartspaceController.disconnect() 282 disposables.dispose() 283 shortcutsBindings.forEach { it.destroy() } 284 } 285 286 /** 287 * Hides or shows smartspace 288 * 289 * @param hide TRUE hides smartspace, FALSE shows smartspace 290 */ 291 fun hideSmartspace(hide: Boolean) { 292 mainHandler.post { smartSpaceView?.visibility = if (hide) View.INVISIBLE else View.VISIBLE } 293 } 294 295 /** 296 * This sets up and shows a non-interactive smart space 297 * 298 * The top padding is as follows: Status bar height + clock top margin + keyguard smart space 299 * top offset 300 * 301 * The start padding is as follows: Clock padding start + Below clock padding start 302 * 303 * The end padding is as follows: Below clock padding end 304 */ 305 private fun setUpSmartspace(previewContext: Context, parentView: ConstraintLayout) { 306 if ( 307 !lockscreenSmartspaceController.isEnabled || 308 !lockscreenSmartspaceController.isDateWeatherDecoupled 309 ) { 310 return 311 } 312 313 if (smartSpaceView != null) { 314 parentView.removeView(smartSpaceView) 315 } 316 317 if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) { 318 val cs = ConstraintSet() 319 cs.clone(parentView) 320 cs.apply { 321 val largeClockViewId = previewContext.getId("lockscreen_clock_view_large") 322 val smallClockViewId = previewContext.getId("lockscreen_clock_view") 323 largeDateView = 324 lockscreenSmartspaceController 325 .buildAndConnectDateView(parentView, true) 326 ?.also { view -> 327 constrainWidth(view.id, ConstraintSet.WRAP_CONTENT) 328 constrainHeight(view.id, ConstraintSet.WRAP_CONTENT) 329 connect(view.id, START, largeClockViewId, START) 330 connect(view.id, ConstraintSet.END, largeClockViewId, ConstraintSet.END) 331 connect( 332 view.id, 333 TOP, 334 largeClockViewId, 335 ConstraintSet.BOTTOM, 336 smartspaceViewModel.getDateWeatherEndPadding(previewContext), 337 ) 338 } 339 smallDateView = 340 lockscreenSmartspaceController 341 .buildAndConnectDateView(parentView, false) 342 ?.also { view -> 343 constrainWidth(view.id, ConstraintSet.WRAP_CONTENT) 344 constrainHeight(view.id, ConstraintSet.WRAP_CONTENT) 345 connect( 346 view.id, 347 START, 348 smallClockViewId, 349 ConstraintSet.END, 350 context.resources.getDimensionPixelSize( 351 R.dimen.smartspace_padding_horizontal 352 ), 353 ) 354 connect(view.id, TOP, smallClockViewId, TOP) 355 connect( 356 view.id, 357 ConstraintSet.BOTTOM, 358 smallClockViewId, 359 ConstraintSet.BOTTOM, 360 ) 361 } 362 parentView.addView(largeDateView) 363 parentView.addView(smallDateView) 364 } 365 cs.applyTo(parentView) 366 } else { 367 smartSpaceView = 368 lockscreenSmartspaceController.buildAndConnectDateView( 369 parent = parentView, 370 isLargeClock = false, 371 ) 372 373 val topPadding: Int = 374 smartspaceViewModel.getLargeClockSmartspaceTopPadding( 375 ClockPreviewConfig( 376 previewContext, 377 getPreviewShadeLayoutWide(display!!), 378 SceneContainerFlag.isEnabled, 379 ) 380 ) 381 val startPadding: Int = smartspaceViewModel.getDateWeatherStartPadding(previewContext) 382 val endPadding: Int = smartspaceViewModel.getDateWeatherEndPadding(previewContext) 383 384 smartSpaceView?.let { 385 it.setPaddingRelative(startPadding, topPadding, endPadding, 0) 386 it.isClickable = false 387 it.isInvisible = true 388 parentView.addView( 389 it, 390 FrameLayout.LayoutParams( 391 FrameLayout.LayoutParams.MATCH_PARENT, 392 FrameLayout.LayoutParams.WRAP_CONTENT, 393 ), 394 ) 395 } 396 smartSpaceView?.alpha = if (shouldHighlightSelectedAffordance) DIM_ALPHA else 1.0f 397 } 398 } 399 400 private fun setupKeyguardRootView(previewContext: Context, rootView: ConstraintLayout) { 401 val keyguardRootView = KeyguardRootView(previewContext, null) 402 rootView.addView( 403 keyguardRootView, 404 FrameLayout.LayoutParams( 405 FrameLayout.LayoutParams.MATCH_PARENT, 406 FrameLayout.LayoutParams.MATCH_PARENT, 407 ), 408 ) 409 410 setUpUdfps(previewContext, keyguardRootView) 411 412 setupShortcuts(keyguardRootView) 413 414 if (!shouldHideClock) { 415 setUpClock(previewContext, rootView) 416 if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) { 417 setUpSmartspace(previewContext, keyguardRootView) 418 KeyguardPreviewSmartspaceViewBinder.bind(keyguardRootView, smartspaceViewModel) 419 } 420 KeyguardPreviewClockViewBinder.bind( 421 keyguardRootView, 422 clockViewModel, 423 clockRegistry, 424 ::updateClockAppearance, 425 ClockPreviewConfig( 426 previewContext, 427 getPreviewShadeLayoutWide(display!!), 428 SceneContainerFlag.isEnabled, 429 ), 430 ) 431 } 432 433 if (!com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) { 434 setUpSmartspace(previewContext, keyguardRootView) 435 smartSpaceView?.let { 436 KeyguardPreviewSmartspaceViewBinder.bind( 437 it, 438 smartspaceViewModel, 439 clockPreviewConfig = 440 ClockPreviewConfig( 441 previewContext, 442 getPreviewShadeLayoutWide(display!!), 443 SceneContainerFlag.isEnabled, 444 lockId = null, 445 udfpsTop = null, 446 ), 447 ) 448 } 449 } 450 } 451 452 private fun setupShortcuts(keyguardRootView: ConstraintLayout) { 453 // Add shortcuts 454 val cs = ConstraintSet() 455 cs.clone(keyguardRootView) 456 defaultShortcutsSection.addViews(keyguardRootView) 457 defaultShortcutsSection.applyConstraints(cs) 458 cs.applyTo(keyguardRootView) 459 460 keyguardRootView.findViewById<LaunchableImageView?>(R.id.start_button)?.let { imageView -> 461 shortcutsBindings.add( 462 keyguardQuickAffordanceViewBinder.bind( 463 view = imageView, 464 viewModel = quickAffordancesCombinedViewModel.startButton, 465 alpha = flowOf(1f), 466 ) { message -> 467 indicationController.showTransientIndication(message) 468 } 469 ) 470 } 471 472 keyguardRootView.findViewById<LaunchableImageView?>(R.id.end_button)?.let { imageView -> 473 shortcutsBindings.add( 474 keyguardQuickAffordanceViewBinder.bind( 475 view = imageView, 476 viewModel = quickAffordancesCombinedViewModel.endButton, 477 alpha = flowOf(1f), 478 ) { message -> 479 indicationController.showTransientIndication(message) 480 } 481 ) 482 } 483 } 484 485 private fun setUpUdfps(previewContext: Context, parentView: ViewGroup) { 486 val sensorBounds = udfpsOverlayInteractor.udfpsOverlayParams.value.sensorBounds 487 488 // If sensorBounds are default rect, then there is no UDFPS 489 if (sensorBounds == Rect()) { 490 return 491 } 492 493 val finger = 494 LayoutInflater.from(previewContext) 495 .inflate(R.layout.udfps_keyguard_preview, parentView, false) as View 496 497 // Place the UDFPS view in the proper sensor location 498 val lockId = KeyguardPreviewClockViewBinder.lockId 499 finger.id = lockId 500 parentView.addView(finger) 501 val cs = ConstraintSet() 502 cs.clone(parentView as ConstraintLayout) 503 cs.apply { 504 constrainWidth(lockId, sensorBounds.width()) 505 constrainHeight(lockId, sensorBounds.height()) 506 connect(lockId, TOP, PARENT_ID, TOP, sensorBounds.top) 507 connect(lockId, START, PARENT_ID, START, sensorBounds.left) 508 } 509 cs.applyTo(parentView) 510 } 511 512 private fun setUpClock(previewContext: Context, parentView: ViewGroup) { 513 val resources = parentView.resources 514 val receiver = 515 object : BroadcastReceiver() { 516 override fun onReceive(context: Context?, intent: Intent?) { 517 clockController.clock?.run { 518 smallClock.events.onTimeTick() 519 largeClock.events.onTimeTick() 520 } 521 } 522 } 523 broadcastDispatcher.registerReceiver( 524 receiver, 525 IntentFilter().apply { 526 addAction(Intent.ACTION_TIME_TICK) 527 addAction(Intent.ACTION_TIME_CHANGED) 528 }, 529 ) 530 disposables += DisposableHandle { broadcastDispatcher.unregisterReceiver(receiver) } 531 } 532 533 private suspend fun updateClockAppearance(clock: ClockController, resources: Resources) { 534 val colors = wallpaperColors 535 if (clockRegistry.seedColor == null && colors != null) { 536 // Seed color null means users do not override any color on the clock. The default 537 // color will need to use wallpaper's extracted color and consider if the 538 // wallpaper's color is dark or light. 539 @Style.Type 540 val style = themeStyle ?: fetchThemeStyleFromSetting().also { themeStyle = it } 541 val wallpaperColorScheme = ColorScheme(colors, false, style) 542 val lightClockColor = wallpaperColorScheme.accent1.s100 543 val darkClockColor = wallpaperColorScheme.accent2.s600 544 545 // Note that when [wallpaperColors] is null, isWallpaperDark is true. 546 val isWallpaperDark: Boolean = 547 (colors.colorHints.and(WallpaperColors.HINT_SUPPORTS_DARK_TEXT)) == 0 548 val theme = 549 ThemeConfig( 550 isWallpaperDark, 551 if (isWallpaperDark) lightClockColor else darkClockColor, 552 ) 553 clock.smallClock.events.onThemeChanged(theme) 554 clock.largeClock.events.onThemeChanged(theme) 555 } 556 // In clock preview, we should have a seed color for clock 557 // before setting clock to clockEventController to avoid updateColor with seedColor == null 558 // So in update colors, it should already have the correct theme in clockFaceController 559 clockController.clock = clock 560 // When set clock to clockController,it will reset fontsize based on context.resources 561 // We need to override it with overlaid resources 562 clock.largeClock.events.onFontSettingChanged( 563 resources.getDimensionPixelSize(customR.dimen.large_clock_text_size).toFloat() 564 ) 565 } 566 567 @Style.Type 568 private suspend fun fetchThemeStyleFromSetting(): Int { 569 val overlayPackageJson = 570 withContext(backgroundDispatcher) { 571 secureSettings.getString(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES) 572 } 573 return if (!overlayPackageJson.isNullOrEmpty()) { 574 try { 575 val jsonObject = JSONObject(overlayPackageJson) 576 Style.valueOf(jsonObject.getString(OVERLAY_CATEGORY_THEME_STYLE)) 577 } catch (e: (JSONException)) { 578 Log.i(TAG, "Failed to parse THEME_CUSTOMIZATION_OVERLAY_PACKAGES.", e) 579 Style.TONAL_SPOT 580 } catch (e: IllegalArgumentException) { 581 Log.i(TAG, "Failed to parse THEME_CUSTOMIZATION_OVERLAY_PACKAGES.", e) 582 Style.TONAL_SPOT 583 } 584 } else { 585 Style.TONAL_SPOT 586 } 587 } 588 589 private fun getPreviewShadeLayoutWide(display: Display): Boolean { 590 return if (display.displayId == 0) { 591 shadeModeInteractor.isShadeLayoutWide.value 592 } else { 593 // For the unfolded preview in a folded screen; it's landscape by default 594 // For the folded preview in an unfolded screen; it's portrait by default 595 display.name == "Inner Display" 596 } 597 } 598 599 companion object { 600 private const val TAG = "KeyguardPreviewRenderer" 601 private const val OVERLAY_CATEGORY_THEME_STYLE = "android.theme.customization.theme_style" 602 private const val KEY_HOST_TOKEN = "host_token" 603 private const val KEY_VIEW_WIDTH = "width" 604 private const val KEY_VIEW_HEIGHT = "height" 605 private const val KEY_DISPLAY_ID = "display_id" 606 private const val KEY_COLORS = "wallpaper_colors" 607 608 const val DIM_ALPHA = 0.3f 609 } 610 } 611