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