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.Rect 20 import androidx.annotation.VisibleForTesting 21 import com.android.app.animation.Interpolators 22 import com.android.systemui.log.core.Logger 23 import com.android.systemui.plugins.clocks.AlarmData 24 import com.android.systemui.plugins.clocks.ClockAnimations 25 import com.android.systemui.plugins.clocks.ClockAxisStyle 26 import com.android.systemui.plugins.clocks.ClockEvents 27 import com.android.systemui.plugins.clocks.ClockFaceConfig 28 import com.android.systemui.plugins.clocks.ClockFaceEvents 29 import com.android.systemui.plugins.clocks.ThemeConfig 30 import com.android.systemui.plugins.clocks.WeatherData 31 import com.android.systemui.plugins.clocks.ZenData 32 import com.android.systemui.shared.clocks.view.FlexClockView 33 import com.android.systemui.shared.clocks.view.HorizontalAlignment 34 import com.android.systemui.shared.clocks.view.VerticalAlignment 35 import java.util.Locale 36 import java.util.TimeZone 37 38 class ComposedDigitalLayerController(private val clockCtx: ClockContext) : 39 SimpleClockLayerController { 40 private val logger = 41 Logger(clockCtx.messageBuffer, ComposedDigitalLayerController::class.simpleName!!) 42 43 val layerControllers = mutableListOf<SimpleClockLayerController>() 44 val dozeState = DefaultClockController.AnimationState(1F) 45 46 override val view = FlexClockView(clockCtx) 47 override var onViewBoundsChanged by view::onViewBoundsChanged 48 49 init { createControllernull50 fun createController(cfg: LayerConfig) { 51 val controller = SimpleDigitalHandLayerController(clockCtx, cfg, isLargeClock = true) 52 view.addView(controller.view) 53 layerControllers.add(controller) 54 } 55 56 val layerCfg = 57 LayerConfig( 58 style = FontTextStyle(lineHeight = 147.25f), 59 alignment = DigitalAlignment(HorizontalAlignment.CENTER, VerticalAlignment.CENTER), 60 aodStyle = 61 FontTextStyle( 62 transitionInterpolator = Interpolators.EMPHASIZED, 63 transitionDuration = 750, 64 ), 65 66 // Placeholders 67 timespec = DigitalTimespec.TIME_FULL_FORMAT, 68 dateTimeFormat = "hh:mm", 69 ) 70 71 createController( 72 layerCfg.copy(timespec = DigitalTimespec.FIRST_DIGIT, dateTimeFormat = "hh") 73 ) 74 createController( 75 layerCfg.copy(timespec = DigitalTimespec.SECOND_DIGIT, dateTimeFormat = "hh") 76 ) 77 createController( 78 layerCfg.copy(timespec = DigitalTimespec.FIRST_DIGIT, dateTimeFormat = "mm") 79 ) 80 createController( 81 layerCfg.copy(timespec = DigitalTimespec.SECOND_DIGIT, dateTimeFormat = "mm") 82 ) 83 } 84 refreshTimenull85 private fun refreshTime() { 86 layerControllers.forEach { it.faceEvents.onTimeTick() } 87 view.refreshTime() 88 } 89 90 override val events = 91 object : ClockEvents { onTimeZoneChangednull92 override fun onTimeZoneChanged(timeZone: TimeZone) { 93 layerControllers.forEach { it.events.onTimeZoneChanged(timeZone) } 94 refreshTime() 95 } 96 onTimeFormatChangednull97 override fun onTimeFormatChanged(is24Hr: Boolean) { 98 layerControllers.forEach { it.events.onTimeFormatChanged(is24Hr) } 99 refreshTime() 100 } 101 onLocaleChangednull102 override fun onLocaleChanged(locale: Locale) { 103 layerControllers.forEach { it.events.onLocaleChanged(locale) } 104 view.onLocaleChanged(locale) 105 refreshTime() 106 } 107 onWeatherDataChangednull108 override fun onWeatherDataChanged(data: WeatherData) {} 109 onAlarmDataChangednull110 override fun onAlarmDataChanged(data: AlarmData) {} 111 onZenDataChangednull112 override fun onZenDataChanged(data: ZenData) {} 113 114 override var isReactiveTouchInteractionEnabled 115 get() = view.isReactiveTouchInteractionEnabled 116 set(value) { 117 view.isReactiveTouchInteractionEnabled = value 118 } 119 } 120 121 override val animations = 122 object : ClockAnimations { enternull123 override fun enter() { 124 refreshTime() 125 } 126 dozenull127 override fun doze(fraction: Float) { 128 val (hasChanged, hasJumped) = dozeState.update(fraction) 129 if (hasChanged) view.animateDoze(dozeState.isActive, !hasJumped) 130 view.dozeFraction = fraction 131 view.invalidate() 132 } 133 foldnull134 override fun fold(fraction: Float) { 135 refreshTime() 136 } 137 chargenull138 override fun charge() { 139 view.animateCharge() 140 } 141 onPositionUpdatednull142 override fun onPositionUpdated(fromLeft: Int, direction: Int, fraction: Float) {} 143 onPositionUpdatednull144 override fun onPositionUpdated(distance: Float, fraction: Float) {} 145 onPickerCarouselSwipingnull146 override fun onPickerCarouselSwiping(swipingFraction: Float) {} 147 onFidgetTapnull148 override fun onFidgetTap(x: Float, y: Float) { 149 view.animateFidget(x, y) 150 } 151 152 private var hasFontAxes = false 153 onFontAxesChangednull154 override fun onFontAxesChanged(style: ClockAxisStyle) { 155 view.updateAxes(style, isAnimated = hasFontAxes) 156 hasFontAxes = true 157 } 158 } 159 160 override val faceEvents = 161 object : ClockFaceEvents { onTimeTicknull162 override fun onTimeTick() { 163 refreshTime() 164 } 165 onThemeChangednull166 override fun onThemeChanged(theme: ThemeConfig) { 167 view.updateColor(theme.getDefaultColor(clockCtx.context)) 168 } 169 onFontSettingChangednull170 override fun onFontSettingChanged(fontSizePx: Float) { 171 view.onFontSettingChanged(fontSizePx) 172 } 173 onTargetRegionChangednull174 override fun onTargetRegionChanged(targetRegion: Rect?) {} 175 onSecondaryDisplayChangednull176 override fun onSecondaryDisplayChanged(onSecondaryDisplay: Boolean) {} 177 } 178 179 override val config = 180 ClockFaceConfig( 181 hasCustomWeatherDataDisplay = false, 182 hasCustomPositionUpdatedAnimation = true, 183 ) 184 185 @VisibleForTesting 186 override var fakeTimeMills: Long? = null 187 get() = field 188 set(timeInMills) { 189 field = timeInMills 190 for (layerController in layerControllers) { 191 layerController.fakeTimeMills = timeInMills 192 } 193 } 194 } 195