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.content.res.Resources 20 import com.android.systemui.animation.GSFAxes 21 import com.android.systemui.customization.R 22 import com.android.systemui.plugins.clocks.AlarmData 23 import com.android.systemui.plugins.clocks.AxisPresetConfig 24 import com.android.systemui.plugins.clocks.AxisType 25 import com.android.systemui.plugins.clocks.ClockAxisStyle 26 import com.android.systemui.plugins.clocks.ClockConfig 27 import com.android.systemui.plugins.clocks.ClockController 28 import com.android.systemui.plugins.clocks.ClockEventListener 29 import com.android.systemui.plugins.clocks.ClockEvents 30 import com.android.systemui.plugins.clocks.ClockFontAxis 31 import com.android.systemui.plugins.clocks.ClockFontAxis.Companion.merge 32 import com.android.systemui.plugins.clocks.ClockSettings 33 import com.android.systemui.plugins.clocks.WeatherData 34 import com.android.systemui.plugins.clocks.ZenData 35 import com.android.systemui.shared.clocks.FontUtils.put 36 import com.android.systemui.shared.clocks.FontUtils.toClockAxis 37 import com.android.systemui.shared.clocks.view.FlexClockView 38 import java.io.PrintWriter 39 import java.util.Locale 40 import java.util.TimeZone 41 42 /** Controller for the default flex clock */ 43 class FlexClockController(private val clockCtx: ClockContext) : ClockController { 44 override val smallClock = 45 FlexClockFaceController( 46 clockCtx.copy(messageBuffer = clockCtx.messageBuffers.smallClockMessageBuffer), 47 isLargeClock = false, 48 ) 49 50 override val largeClock = 51 FlexClockFaceController( 52 clockCtx.copy(messageBuffer = clockCtx.messageBuffers.largeClockMessageBuffer), 53 isLargeClock = true, 54 ) 55 <lambda>null56 override val config: ClockConfig by lazy { 57 ClockConfig( 58 DEFAULT_CLOCK_ID, 59 clockCtx.resources.getString(R.string.clock_default_name), 60 clockCtx.resources.getString(R.string.clock_default_description), 61 ) 62 } 63 64 override val events = 65 object : ClockEvents { 66 override var isReactiveTouchInteractionEnabled = false 67 set(value) { 68 field = value 69 val view = largeClock.view as FlexClockView 70 view.isReactiveTouchInteractionEnabled = value 71 } 72 onTimeZoneChangednull73 override fun onTimeZoneChanged(timeZone: TimeZone) { 74 smallClock.events.onTimeZoneChanged(timeZone) 75 largeClock.events.onTimeZoneChanged(timeZone) 76 } 77 onTimeFormatChangednull78 override fun onTimeFormatChanged(is24Hr: Boolean) { 79 smallClock.events.onTimeFormatChanged(is24Hr) 80 largeClock.events.onTimeFormatChanged(is24Hr) 81 } 82 onLocaleChangednull83 override fun onLocaleChanged(locale: Locale) { 84 smallClock.events.onLocaleChanged(locale) 85 largeClock.events.onLocaleChanged(locale) 86 } 87 onWeatherDataChangednull88 override fun onWeatherDataChanged(data: WeatherData) { 89 smallClock.events.onWeatherDataChanged(data) 90 largeClock.events.onWeatherDataChanged(data) 91 } 92 onAlarmDataChangednull93 override fun onAlarmDataChanged(data: AlarmData) { 94 smallClock.events.onAlarmDataChanged(data) 95 largeClock.events.onAlarmDataChanged(data) 96 } 97 onZenDataChangednull98 override fun onZenDataChanged(data: ZenData) { 99 smallClock.events.onZenDataChanged(data) 100 largeClock.events.onZenDataChanged(data) 101 } 102 } 103 initializenull104 override fun initialize( 105 isDarkTheme: Boolean, 106 dozeFraction: Float, 107 foldFraction: Float, 108 clockListener: ClockEventListener?, 109 ) { 110 smallClock.run { 111 layerController.onViewBoundsChanged = { clockListener?.onBoundsChanged(it) } 112 events.onThemeChanged(theme.copy(isDarkTheme = isDarkTheme)) 113 animations.onFontAxesChanged(clockCtx.settings.axes) 114 animations.doze(dozeFraction) 115 animations.fold(foldFraction) 116 events.onTimeTick() 117 } 118 119 largeClock.run { 120 layerController.onViewBoundsChanged = { clockListener?.onBoundsChanged(it) } 121 events.onThemeChanged(theme.copy(isDarkTheme = isDarkTheme)) 122 animations.onFontAxesChanged(clockCtx.settings.axes) 123 animations.doze(dozeFraction) 124 animations.fold(foldFraction) 125 events.onTimeTick() 126 } 127 } 128 dumpnull129 override fun dump(pw: PrintWriter) {} 130 131 companion object { getDefaultAxesnull132 fun getDefaultAxes(settings: ClockSettings): List<ClockFontAxis> { 133 return if (settings.clockId == FLEX_CLOCK_ID) { 134 FONT_AXES.merge(LEGACY_FLEX_SETTINGS) 135 } else FONT_AXES 136 } 137 138 private val FONT_AXES = 139 listOf( 140 GSFAxes.WEIGHT.toClockAxis( 141 type = AxisType.Float, 142 currentValue = 400f, 143 name = "Weight", 144 description = "Glyph Weight", 145 ), 146 GSFAxes.WIDTH.toClockAxis( 147 type = AxisType.Float, 148 currentValue = 80f, 149 name = "Width", 150 description = "Glyph Width", 151 ), 152 GSFAxes.ROUND.toClockAxis( 153 type = AxisType.Boolean, 154 currentValue = 100f, 155 name = "Round", 156 description = "Glyph Roundness", 157 ), 158 GSFAxes.SLANT.toClockAxis( 159 type = AxisType.Boolean, 160 currentValue = 0f, 161 name = "Slant", 162 description = "Glyph Slant", 163 ), 164 ) 165 <lambda>null166 private val LEGACY_FLEX_SETTINGS = ClockAxisStyle { 167 put(GSFAxes.WEIGHT, 600f) 168 put(GSFAxes.WIDTH, 100f) 169 put(GSFAxes.ROUND, 100f) 170 put(GSFAxes.SLANT, 0f) 171 } 172 173 private val PRESET_COUNT = 8 174 private val PRESET_WIDTH_INIT = 30f 175 private val PRESET_WIDTH_STEP = 12.5f 176 private val PRESET_WEIGHT_INIT = 800f 177 private val PRESET_WEIGHT_STEP = -100f <lambda>null178 private val BASE_PRESETS: List<ClockAxisStyle> = run { 179 val presets = mutableListOf<ClockAxisStyle>() 180 var weight = PRESET_WEIGHT_INIT 181 var width = PRESET_WIDTH_INIT 182 for (i in 1..PRESET_COUNT) { 183 presets.add( 184 ClockAxisStyle { 185 put(GSFAxes.WEIGHT, weight) 186 put(GSFAxes.WIDTH, width) 187 put(GSFAxes.ROUND, 0f) 188 put(GSFAxes.SLANT, 0f) 189 } 190 ) 191 192 weight += PRESET_WEIGHT_STEP 193 width += PRESET_WIDTH_STEP 194 } 195 196 return@run presets 197 } 198 buildPresetGroupnull199 fun buildPresetGroup(resources: Resources, isRound: Boolean): AxisPresetConfig.Group { 200 val round = if (isRound) GSFAxes.ROUND.maxValue else GSFAxes.ROUND.minValue 201 return AxisPresetConfig.Group( 202 presets = BASE_PRESETS.map { it.copy { put(GSFAxes.ROUND, round) } }, 203 // TODO(b/395647577): Placeholder Icon; Replace or remove 204 icon = resources.getDrawable(R.drawable.clock_default_thumbnail, null), 205 ) 206 } 207 } 208 } 209