1 /*
2  * Copyright (C) 2022 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.shared.clocks
15 
16 import android.content.Context
17 import android.content.res.Resources
18 import android.graphics.Typeface
19 import android.view.LayoutInflater
20 import com.android.systemui.customization.R
21 import com.android.systemui.log.core.MessageBuffer
22 import com.android.systemui.plugins.clocks.ClockController
23 import com.android.systemui.plugins.clocks.ClockFontAxis
24 import com.android.systemui.plugins.clocks.ClockFontAxisSetting
25 import com.android.systemui.plugins.clocks.ClockMessageBuffers
26 import com.android.systemui.plugins.clocks.ClockMetadata
27 import com.android.systemui.plugins.clocks.ClockPickerConfig
28 import com.android.systemui.plugins.clocks.ClockProvider
29 import com.android.systemui.plugins.clocks.ClockSettings
30 import com.android.systemui.shared.clocks.view.HorizontalAlignment
31 import com.android.systemui.shared.clocks.view.VerticalAlignment
32 
33 private val TAG = DefaultClockProvider::class.simpleName
34 const val DEFAULT_CLOCK_ID = "DEFAULT"
35 
36 data class ClockContext(
37     val context: Context,
38     val resources: Resources,
39     val settings: ClockSettings,
40     val typefaceCache: TypefaceCache,
41     val messageBuffers: ClockMessageBuffers,
42     val messageBuffer: MessageBuffer,
43 )
44 
45 /** Provides the default system clock */
46 class DefaultClockProvider(
47     val ctx: Context,
48     val layoutInflater: LayoutInflater,
49     val resources: Resources,
50     private val migratedClocks: Boolean = false,
51     private val isClockReactiveVariantsEnabled: Boolean = false,
52 ) : ClockProvider {
53     private var messageBuffers: ClockMessageBuffers? = null
54 
initializenull55     override fun initialize(buffers: ClockMessageBuffers?) {
56         messageBuffers = buffers
57     }
58 
getClocksnull59     override fun getClocks(): List<ClockMetadata> = listOf(ClockMetadata(DEFAULT_CLOCK_ID))
60 
61     override fun createClock(settings: ClockSettings): ClockController {
62         if (settings.clockId != DEFAULT_CLOCK_ID) {
63             throw IllegalArgumentException("${settings.clockId} is unsupported by $TAG")
64         }
65 
66         return if (isClockReactiveVariantsEnabled) {
67             val buffers = messageBuffers ?: ClockMessageBuffers(LogUtil.DEFAULT_MESSAGE_BUFFER)
68             val fontAxes = ClockFontAxis.merge(FlexClockController.FONT_AXES, settings.axes)
69             val clockSettings = settings.copy(axes = fontAxes.map { it.toSetting() })
70             val typefaceCache =
71                 TypefaceCache(buffers.infraMessageBuffer, NUM_CLOCK_FONT_ANIMATION_STEPS) {
72                     FLEX_TYPEFACE
73                 }
74             FlexClockController(
75                 ClockContext(
76                     ctx,
77                     resources,
78                     clockSettings,
79                     typefaceCache,
80                     buffers,
81                     buffers.infraMessageBuffer,
82                 ),
83                 FLEX_DESIGN,
84             )
85         } else {
86             DefaultClockController(
87                 ctx,
88                 layoutInflater,
89                 resources,
90                 settings,
91                 migratedClocks,
92                 messageBuffers,
93             )
94         }
95     }
96 
getClockPickerConfignull97     override fun getClockPickerConfig(settings: ClockSettings): ClockPickerConfig {
98         if (settings.clockId != DEFAULT_CLOCK_ID) {
99             throw IllegalArgumentException("${settings.clockId} is unsupported by $TAG")
100         }
101 
102         val fontAxes =
103             if (!isClockReactiveVariantsEnabled) listOf()
104             else ClockFontAxis.merge(FlexClockController.FONT_AXES, settings.axes)
105         return ClockPickerConfig(
106             DEFAULT_CLOCK_ID,
107             resources.getString(R.string.clock_default_name),
108             resources.getString(R.string.clock_default_description),
109             resources.getDrawable(R.drawable.clock_default_thumbnail, null),
110             isReactiveToTone = true,
111             axes = fontAxes,
112         )
113     }
114 
115     companion object {
116         const val NUM_CLOCK_FONT_ANIMATION_STEPS = 30
117 
118         // TODO(b/364681643): Variations for retargetted DIGITAL_CLOCK_FLEX
119         val LEGACY_FLEX_LS_VARIATION =
120             listOf(
121                 ClockFontAxisSetting("wght", 600f),
122                 ClockFontAxisSetting("wdth", 100f),
123                 ClockFontAxisSetting("ROND", 100f),
124                 ClockFontAxisSetting("slnt", 0f),
125             )
126 
127         val LEGACY_FLEX_AOD_VARIATION =
128             listOf(
129                 ClockFontAxisSetting("wght", 74f),
130                 ClockFontAxisSetting("wdth", 43f),
131                 ClockFontAxisSetting("ROND", 100f),
132                 ClockFontAxisSetting("slnt", 0f),
133             )
134 
<lambda>null135         val FLEX_TYPEFACE by lazy {
136             // TODO(b/364680873): Move constant to config_clockFontFamily when shipping
137             Typeface.create("google-sans-flex-clock", Typeface.NORMAL)
138         }
139 
<lambda>null140         val FLEX_DESIGN = run {
141             val largeLayer =
142                 listOf(
143                     ComposedDigitalHandLayer(
144                         layerBounds = LayerBounds.FIT,
145                         customizedView = "FlexClockView",
146                         digitalLayers =
147                             listOf(
148                                 DigitalHandLayer(
149                                     layerBounds = LayerBounds.FIT,
150                                     timespec = DigitalTimespec.FIRST_DIGIT,
151                                     style = FontTextStyle(lineHeight = 147.25f),
152                                     aodStyle =
153                                         FontTextStyle(
154                                             fillColorLight = "#FFFFFFFF",
155                                             outlineColor = "#00000000",
156                                             renderType = RenderType.CHANGE_WEIGHT,
157                                             transitionInterpolator = InterpolatorEnum.EMPHASIZED,
158                                             transitionDuration = 750,
159                                         ),
160                                     alignment =
161                                         DigitalAlignment(
162                                             HorizontalAlignment.CENTER,
163                                             VerticalAlignment.BASELINE,
164                                         ),
165                                     dateTimeFormat = "hh",
166                                 ),
167                                 DigitalHandLayer(
168                                     layerBounds = LayerBounds.FIT,
169                                     timespec = DigitalTimespec.SECOND_DIGIT,
170                                     style = FontTextStyle(lineHeight = 147.25f),
171                                     aodStyle =
172                                         FontTextStyle(
173                                             fillColorLight = "#FFFFFFFF",
174                                             outlineColor = "#00000000",
175                                             renderType = RenderType.CHANGE_WEIGHT,
176                                             transitionInterpolator = InterpolatorEnum.EMPHASIZED,
177                                             transitionDuration = 750,
178                                         ),
179                                     alignment =
180                                         DigitalAlignment(
181                                             HorizontalAlignment.CENTER,
182                                             VerticalAlignment.BASELINE,
183                                         ),
184                                     dateTimeFormat = "hh",
185                                 ),
186                                 DigitalHandLayer(
187                                     layerBounds = LayerBounds.FIT,
188                                     timespec = DigitalTimespec.FIRST_DIGIT,
189                                     style = FontTextStyle(lineHeight = 147.25f),
190                                     aodStyle =
191                                         FontTextStyle(
192                                             fillColorLight = "#FFFFFFFF",
193                                             outlineColor = "#00000000",
194                                             renderType = RenderType.CHANGE_WEIGHT,
195                                             transitionInterpolator = InterpolatorEnum.EMPHASIZED,
196                                             transitionDuration = 750,
197                                         ),
198                                     alignment =
199                                         DigitalAlignment(
200                                             HorizontalAlignment.CENTER,
201                                             VerticalAlignment.BASELINE,
202                                         ),
203                                     dateTimeFormat = "mm",
204                                 ),
205                                 DigitalHandLayer(
206                                     layerBounds = LayerBounds.FIT,
207                                     timespec = DigitalTimespec.SECOND_DIGIT,
208                                     style = FontTextStyle(lineHeight = 147.25f),
209                                     aodStyle =
210                                         FontTextStyle(
211                                             fillColorLight = "#FFFFFFFF",
212                                             outlineColor = "#00000000",
213                                             renderType = RenderType.CHANGE_WEIGHT,
214                                             transitionInterpolator = InterpolatorEnum.EMPHASIZED,
215                                             transitionDuration = 750,
216                                         ),
217                                     alignment =
218                                         DigitalAlignment(
219                                             HorizontalAlignment.CENTER,
220                                             VerticalAlignment.BASELINE,
221                                         ),
222                                     dateTimeFormat = "mm",
223                                 ),
224                             ),
225                     )
226                 )
227 
228             val smallLayer =
229                 listOf(
230                     DigitalHandLayer(
231                         layerBounds = LayerBounds.FIT,
232                         timespec = DigitalTimespec.TIME_FULL_FORMAT,
233                         style = FontTextStyle(fontSizeScale = 0.98f),
234                         aodStyle =
235                             FontTextStyle(
236                                 fillColorLight = "#FFFFFFFF",
237                                 outlineColor = "#00000000",
238                                 renderType = RenderType.CHANGE_WEIGHT,
239                             ),
240                         alignment = DigitalAlignment(HorizontalAlignment.LEFT, null),
241                         dateTimeFormat = "h:mm",
242                     )
243                 )
244 
245             ClockDesign(
246                 id = DEFAULT_CLOCK_ID,
247                 name = "@string/clock_default_name",
248                 description = "@string/clock_default_description",
249                 large = ClockFace(layers = largeLayer),
250                 small = ClockFace(layers = smallLayer),
251             )
252         }
253     }
254 }
255