• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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