1 /*
2 * Copyright (C) 2024 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.shared.clocks
18
19 import android.graphics.Point
20 import android.view.animation.Interpolator
21 import com.android.app.animation.Interpolators
22 import com.android.internal.annotations.Keep
23 import com.android.systemui.monet.Style as MonetStyle
24 import com.android.systemui.shared.clocks.view.HorizontalAlignment
25 import com.android.systemui.shared.clocks.view.VerticalAlignment
26
27 /** Data format for a simple asset-defined clock */
28 @Keep
29 data class ClockDesign(
30 val id: String,
31 val name: String? = null,
32 val description: String? = null,
33 val thumbnail: String? = null,
34 val large: ClockFace? = null,
35 val small: ClockFace? = null,
36 @MonetStyle.Type val colorPalette: Int? = null,
37 )
38
39 /** Describes a clock using layers */
40 @Keep
41 data class ClockFace(
42 val layers: List<ClockLayer> = listOf<ClockLayer>(),
43 val layerBounds: LayerBounds = LayerBounds.FIT,
44 val wallpaper: String? = null,
45 val faceLayout: DigitalFaceLayout? = null,
46 val pickerScale: ClockFaceScaleInPicker? = ClockFaceScaleInPicker(1.0f, 1.0f),
47 )
48
49 @Keep data class ClockFaceScaleInPicker(val scaleX: Float, val scaleY: Float)
50
51 /** Base Type for a Clock Layer */
52 @Keep
53 interface ClockLayer {
54 /** Override of face LayerBounds setting for this layer */
55 val layerBounds: LayerBounds?
56 }
57
58 /** Clock layer that renders a static asset */
59 @Keep
60 data class AssetLayer(
61 /** Asset to render in this layer */
62 val asset: AssetReference,
63 override val layerBounds: LayerBounds? = null,
64 ) : ClockLayer
65
66 /** Clock layer that renders the time (or a component of it) using numerals */
67 @Keep
68 data class DigitalHandLayer(
69 /** See SimpleDateFormat for timespec format info */
70 val timespec: DigitalTimespec,
71 val style: TextStyle,
72 // adoStyle concrete type must match style,
73 // cause styles will transition between style and aodStyle
74 val aodStyle: TextStyle?,
75 val timer: Int? = null,
76 override val layerBounds: LayerBounds? = null,
77 var faceLayout: DigitalFaceLayout? = null,
78 // we pass 12-hour format from json, which will be converted to 24-hour format in codes
79 val dateTimeFormat: String,
80 val alignment: DigitalAlignment?,
81 // ratio of margins to measured size, currently used for handwritten clocks
82 val marginRatio: DigitalMarginRatio? = DigitalMarginRatio(),
83 ) : ClockLayer
84
85 /** Clock layer that renders the time (or a component of it) using numerals */
86 @Keep
87 data class ComposedDigitalHandLayer(
88 val customizedView: String? = null,
89 /** See SimpleDateFormat for timespec format info */
90 val digitalLayers: List<DigitalHandLayer> = listOf<DigitalHandLayer>(),
91 override val layerBounds: LayerBounds? = null,
92 ) : ClockLayer
93
94 @Keep
95 data class DigitalAlignment(
96 val horizontalAlignment: HorizontalAlignment?,
97 val verticalAlignment: VerticalAlignment?,
98 )
99
100 @Keep
101 data class DigitalMarginRatio(
102 val left: Float = 0F,
103 val top: Float = 0F,
104 val right: Float = 0F,
105 val bottom: Float = 0F,
106 )
107
108 /** Clock layer which renders a component of the time using an analog hand */
109 @Keep
110 data class AnalogHandLayer(
111 val timespec: AnalogTimespec,
112 val tickMode: AnalogTickMode,
113 val asset: AssetReference,
114 val timer: Int? = null,
115 val clock_pivot: Point = Point(0, 0),
116 val asset_pivot: Point? = null,
117 val length: Float = 1f,
118 override val layerBounds: LayerBounds? = null,
119 ) : ClockLayer
120
121 /** Clock layer which renders the time using an AVD */
122 @Keep
123 data class AnimatedHandLayer(
124 val timespec: AnalogTimespec,
125 val asset: AssetReference,
126 val timer: Int? = null,
127 override val layerBounds: LayerBounds? = null,
128 ) : ClockLayer
129
130 /** A collection of asset references for use in different device modes */
131 @Keep
132 data class AssetReference(
133 val light: String,
134 val dark: String,
135 val doze: String? = null,
136 val lightTint: String? = null,
137 val darkTint: String? = null,
138 val dozeTint: String? = null,
139 )
140
141 /**
142 * Core TextStyling attributes for text clocks. Both color and sizing information can be applied to
143 * either subtype.
144 */
145 @Keep
146 interface TextStyle {
147 // fontSizeScale is a scale factor applied to the default clock's font size.
148 val fontSizeScale: Float?
149 }
150
151 /**
152 * This specifies a font and styling parameters for that font. This is rendered using a text view
153 * and the text animation classes used by the default clock. To ensure default value take effects,
154 * all parameters MUST have a default value
155 */
156 @Keep
157 data class FontTextStyle(
158 // Font to load and use in the TextView
159 val fontFamily: String? = null,
160 val lineHeight: Float? = null,
161 val borderWidth: String? = null,
162 // ratio of borderWidth / fontSize
163 val borderWidthScale: Float? = null,
164 // A color literal like `#FF00FF` or a color resource like `@android:color/system_accent1_100`
165 val fillColorLight: String? = null,
166 // A color literal like `#FF00FF` or a color resource like `@android:color/system_accent1_100`
167 val fillColorDark: String? = null,
168 override val fontSizeScale: Float? = null,
169 // used when alternate in one font file is needed
170 var fontFeatureSettings: String? = null,
171 val renderType: RenderType = RenderType.STROKE_TEXT,
172 val outlineColor: String? = null,
173 val transitionDuration: Long = -1L,
174 val transitionInterpolator: InterpolatorEnum? = null,
175 ) : TextStyle
176
177 /**
178 * As an alternative to using a font, we can instead render a digital clock using a set of drawables
179 * for each numeral, and optionally a colon. These drawables will be rendered directly after sizing
180 * and placing them. This may be easier than generating a font file in some cases, and is provided
181 * for ease of use. Unlike fonts, these are not localizable to other numeric systems (like Burmese).
182 */
183 @Keep
184 data class LottieTextStyle(
185 val numbers: List<String> = listOf(),
186 // Spacing between numbers, dimension string
187 val spacing: String = "0dp",
188 // Colon drawable may be omitted if unused in format spec
189 val colon: String? = null,
190 // key is keypath name to get strokes from lottie, value is the color name to query color in
191 // palette, e.g. @android:color/system_accent1_100
192 val fillColorLightMap: Map<String, String>? = null,
193 val fillColorDarkMap: Map<String, String>? = null,
194 override val fontSizeScale: Float? = null,
195 val paddingVertical: String = "0dp",
196 val paddingHorizontal: String = "0dp",
197 ) : TextStyle
198
199 /** Layer sizing mode for the clockface or layer */
200 enum class LayerBounds {
201 /**
202 * Sized so the larger dimension matches the allocated space. This results in some of the
203 * allocated space being unused.
204 */
205 FIT,
206
207 /**
208 * Sized so the smaller dimension matches the allocated space. This will clip some content to
209 * the edges of the space.
210 */
211 FILL,
212
213 /** Fills the allocated space exactly by stretching the layer */
214 STRETCH,
215 }
216
217 /** Ticking mode for analog hands. */
218 enum class AnalogTickMode {
219 SWEEP,
220 TICK,
221 }
222
223 /** Timspec options for Analog Hands. Named for tick interval. */
224 enum class AnalogTimespec {
225 SECONDS,
226 MINUTES,
227 HOURS,
228 HOURS_OF_DAY,
229 DAY_OF_WEEK,
230 DAY_OF_MONTH,
231 DAY_OF_YEAR,
232 WEEK,
233 MONTH,
234 TIMER,
235 }
236
237 enum class DigitalTimespec {
238 TIME_FULL_FORMAT,
239 DIGIT_PAIR,
240 FIRST_DIGIT,
241 SECOND_DIGIT,
242 DATE_FORMAT,
243 }
244
245 enum class DigitalFaceLayout {
246 // can only use HH_PAIR, MM_PAIR from DigitalTimespec
247 TWO_PAIRS_VERTICAL,
248 TWO_PAIRS_HORIZONTAL,
249 // can only use HOUR_FIRST_DIGIT, HOUR_SECOND_DIGIT, MINUTE_FIRST_DIGIT, MINUTE_SECOND_DIGIT
250 // from DigitalTimespec, used for tabular layout when the font doesn't support tnum
251 FOUR_DIGITS_ALIGN_CENTER,
252 FOUR_DIGITS_HORIZONTAL,
253 }
254
255 enum class RenderType {
256 CHANGE_WEIGHT,
257 HOLLOW_TEXT,
258 STROKE_TEXT,
259 OUTER_OUTLINE_TEXT,
260 }
261
262 enum class InterpolatorEnum(factory: () -> Interpolator) {
<lambda>null263 STANDARD({ Interpolators.STANDARD }),
<lambda>null264 EMPHASIZED({ Interpolators.EMPHASIZED });
265
266 val interpolator: Interpolator by lazy(factory)
267 }
268
generateDigitalLayerIdStringnull269 fun generateDigitalLayerIdString(layer: DigitalHandLayer): String {
270 return if (
271 layer.timespec == DigitalTimespec.TIME_FULL_FORMAT ||
272 layer.timespec == DigitalTimespec.DATE_FORMAT
273 ) {
274 layer.timespec.toString()
275 } else {
276 if ("h" in layer.dateTimeFormat) {
277 "HOUR" + "_" + layer.timespec.toString()
278 } else {
279 "MINUTE" + "_" + layer.timespec.toString()
280 }
281 }
282 }
283