xref: /aosp_15_r20/frameworks/base/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceLayout.kt (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5  * except in compliance with the License. You may obtain a copy of the License at
6  *
7  *      http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the
10  * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11  * KIND, either express or implied. See the License for the specific language governing
12  * permissions and limitations under the License.
13  */
14 package com.android.systemui.plugins.clocks
15 
16 import android.content.Context
17 import android.util.DisplayMetrics
18 import android.view.View
19 import androidx.constraintlayout.widget.ConstraintSet
20 import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
21 import androidx.constraintlayout.widget.ConstraintSet.END
22 import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
23 import androidx.constraintlayout.widget.ConstraintSet.START
24 import androidx.constraintlayout.widget.ConstraintSet.TOP
25 import androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT
26 import com.android.internal.policy.SystemBarUtils
27 import com.android.systemui.plugins.annotations.GeneratedImport
28 import com.android.systemui.plugins.annotations.ProtectedInterface
29 import com.android.systemui.plugins.annotations.ProtectedReturn
30 
31 /** Specifies layout information for the clock face */
32 @ProtectedInterface
33 @GeneratedImport("java.util.ArrayList")
34 @GeneratedImport("android.view.View")
35 interface ClockFaceLayout {
36     @get:ProtectedReturn("return new ArrayList<View>();")
37     /** All clock views to add to the root constraint layout before applying constraints. */
38     val views: List<View>
39 
40     @ProtectedReturn("return constraints;")
41     /** Custom constraints to apply to Lockscreen ConstraintLayout. */
applyConstraintsnull42     fun applyConstraints(constraints: ConstraintSet): ConstraintSet
43 
44     @ProtectedReturn("return constraints;")
45     /** Custom constraints to apply to preview ConstraintLayout. */
46     fun applyPreviewConstraints(
47         clockPreviewConfig: ClockPreviewConfig,
48         constraints: ConstraintSet,
49     ): ConstraintSet
50 
51     /** Apply specified AOD BurnIn parameters to this layout */
52     fun applyAodBurnIn(aodBurnInModel: AodClockBurnInModel)
53 }
54 
55 /** Data class to contain AOD BurnIn information for correct aod rendering */
56 data class AodClockBurnInModel(
57     /** Scale that the clock should render at to mitigate burnin */
58     val scale: Float,
59 
60     /** X-Translation for the clock to mitigate burnin */
61     val translationX: Float,
62 
63     /** Y-Translation for the clock to mitigate burnin */
64     val translationY: Float,
65 )
66 
67 /** A ClockFaceLayout that applies the default lockscreen layout to a single view */
68 class DefaultClockFaceLayout(val view: View) : ClockFaceLayout {
69     override val views = listOf(view)
70 
71     override fun applyConstraints(constraints: ConstraintSet): ConstraintSet {
72         if (views.size != 1) {
73             throw IllegalArgumentException(
74                 "Should have only one container view when using DefaultClockFaceLayout"
75             )
76         }
77         return constraints
78     }
79 
80     override fun applyPreviewConstraints(
81         clockPreviewConfig: ClockPreviewConfig,
82         constraints: ConstraintSet,
83     ): ConstraintSet {
84         return applyDefaultPreviewConstraints(clockPreviewConfig, constraints)
85     }
86 
87     override fun applyAodBurnIn(aodBurnInModel: AodClockBurnInModel) {
88         // Default clock doesn't need detailed control of view
89     }
90 
91     companion object {
92         fun applyDefaultPreviewConstraints(
93             clockPreviewConfig: ClockPreviewConfig,
94             constraints: ConstraintSet,
95         ): ConstraintSet {
96             constraints.apply {
97                 val context = clockPreviewConfig.previewContext
98                 val lockscreenClockViewLargeId = getId(context, "lockscreen_clock_view_large")
99                 constrainWidth(lockscreenClockViewLargeId, WRAP_CONTENT)
100                 constrainHeight(lockscreenClockViewLargeId, WRAP_CONTENT)
101                 constrainMaxHeight(lockscreenClockViewLargeId, 0)
102 
103                 val largeClockTopMargin =
104                     SystemBarUtils.getStatusBarHeight(context) +
105                         getDimen(context, "small_clock_padding_top") +
106                         getDimen(context, "keyguard_smartspace_top_offset") +
107                         getDimen(context, "date_weather_view_height") +
108                         getDimen(context, "enhanced_smartspace_height")
109                 connect(lockscreenClockViewLargeId, TOP, PARENT_ID, TOP, largeClockTopMargin)
110                 connect(lockscreenClockViewLargeId, START, PARENT_ID, START)
111                 connect(lockscreenClockViewLargeId, END, PARENT_ID, END)
112 
113                 // In preview, we'll show UDFPS icon for UDFPS devices
114                 // and nothing for non-UDFPS devices,
115                 // and we're not planning to add this vide in clockHostView
116                 // so we only need position of device entry icon to constrain clock
117                 // Copied calculation codes from applyConstraints in DefaultDeviceEntrySection
118                 val bottomPaddingPx = getDimen(context, "lock_icon_margin_bottom")
119                 val defaultDensity =
120                     DisplayMetrics.DENSITY_DEVICE_STABLE.toFloat() /
121                         DisplayMetrics.DENSITY_DEFAULT.toFloat()
122                 val lockIconRadiusPx = (defaultDensity * 36).toInt()
123                 val clockBottomMargin = bottomPaddingPx + 2 * lockIconRadiusPx
124 
125                 connect(lockscreenClockViewLargeId, BOTTOM, PARENT_ID, BOTTOM, clockBottomMargin)
126                 val smallClockViewId = getId(context, "lockscreen_clock_view")
127                 constrainWidth(smallClockViewId, WRAP_CONTENT)
128                 constrainHeight(smallClockViewId, getDimen(context, "small_clock_height"))
129                 connect(
130                     smallClockViewId,
131                     START,
132                     PARENT_ID,
133                     START,
134                     getDimen(context, "clock_padding_start") +
135                         getDimen(context, "status_view_margin_horizontal"),
136                 )
137                 val smallClockTopMargin =
138                     getSmallClockTopPadding(
139                         clockPreviewConfig = clockPreviewConfig,
140                         SystemBarUtils.getStatusBarHeight(context),
141                     )
142                 connect(smallClockViewId, TOP, PARENT_ID, TOP, smallClockTopMargin)
143             }
144             return constraints
145         }
146 
147         fun getId(context: Context, name: String): Int {
148             val packageName = context.packageName
149             val res = context.packageManager.getResourcesForApplication(packageName)
150             val id = res.getIdentifier(name, "id", packageName)
151             return id
152         }
153 
154         fun getDimen(context: Context, name: String): Int {
155             val packageName = context.packageName
156             val res = context.resources
157             val id = res.getIdentifier(name, "dimen", packageName)
158             return if (id == 0) 0 else res.getDimensionPixelSize(id)
159         }
160 
161         fun getSmallClockTopPadding(
162             clockPreviewConfig: ClockPreviewConfig,
163             statusBarHeight: Int,
164         ): Int {
165             return if (clockPreviewConfig.isShadeLayoutWide) {
166                 getDimen(clockPreviewConfig.previewContext, "keyguard_split_shade_top_margin") -
167                     if (clockPreviewConfig.isSceneContainerFlagEnabled) statusBarHeight else 0
168             } else {
169                 getDimen(clockPreviewConfig.previewContext, "keyguard_clock_top_margin") +
170                     if (!clockPreviewConfig.isSceneContainerFlagEnabled) statusBarHeight else 0
171             }
172         }
173     }
174 }
175