1 /* <lambda>null2 * 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.os.Vibrator 20 import android.view.LayoutInflater 21 import com.android.systemui.customization.R 22 import com.android.systemui.log.core.MessageBuffer 23 import com.android.systemui.plugins.clocks.AxisPresetConfig 24 import com.android.systemui.plugins.clocks.ClockAxisStyle 25 import com.android.systemui.plugins.clocks.ClockController 26 import com.android.systemui.plugins.clocks.ClockFontAxis.Companion.merge 27 import com.android.systemui.plugins.clocks.ClockLogger 28 import com.android.systemui.plugins.clocks.ClockMessageBuffers 29 import com.android.systemui.plugins.clocks.ClockMetadata 30 import com.android.systemui.plugins.clocks.ClockPickerConfig 31 import com.android.systemui.plugins.clocks.ClockProvider 32 import com.android.systemui.plugins.clocks.ClockSettings 33 import com.android.systemui.shared.clocks.FlexClockController.Companion.buildPresetGroup 34 import com.android.systemui.shared.clocks.FlexClockController.Companion.getDefaultAxes 35 36 private val TAG = DefaultClockProvider::class.simpleName 37 const val DEFAULT_CLOCK_ID = "DEFAULT" 38 const val FLEX_CLOCK_ID = "DIGITAL_CLOCK_FLEX" 39 40 data class ClockContext( 41 val context: Context, 42 val resources: Resources, 43 val settings: ClockSettings, 44 val typefaceCache: TypefaceCache, 45 val messageBuffers: ClockMessageBuffers, 46 val messageBuffer: MessageBuffer, 47 val vibrator: Vibrator?, 48 ) 49 50 /** Provides the default system clock */ 51 class DefaultClockProvider( 52 val ctx: Context, 53 val layoutInflater: LayoutInflater, 54 val resources: Resources, 55 private val isClockReactiveVariantsEnabled: Boolean = false, 56 private val vibrator: Vibrator?, 57 ) : ClockProvider { 58 private var messageBuffers: ClockMessageBuffers? = null 59 60 override fun initialize(buffers: ClockMessageBuffers?) { 61 messageBuffers = buffers 62 } 63 64 override fun getClocks(): List<ClockMetadata> { 65 var clocks = listOf(ClockMetadata(DEFAULT_CLOCK_ID)) 66 if (isClockReactiveVariantsEnabled) { 67 clocks += 68 ClockMetadata( 69 FLEX_CLOCK_ID, 70 isDeprecated = true, 71 replacementTarget = DEFAULT_CLOCK_ID, 72 ) 73 } 74 return clocks 75 } 76 77 override fun createClock(settings: ClockSettings): ClockController { 78 if (getClocks().all { it.clockId != settings.clockId }) { 79 throw IllegalArgumentException("${settings.clockId} is unsupported by $TAG") 80 } 81 82 return if (isClockReactiveVariantsEnabled) { 83 val buffers = messageBuffers ?: ClockMessageBuffers(ClockLogger.DEFAULT_MESSAGE_BUFFER) 84 val fontAxes = getDefaultAxes(settings).merge(settings.axes) 85 val clockSettings = settings.copy(axes = ClockAxisStyle(fontAxes)) 86 val typefaceCache = 87 TypefaceCache(buffers.infraMessageBuffer, NUM_CLOCK_FONT_ANIMATION_STEPS) { 88 FLEX_TYPEFACE 89 } 90 FlexClockController( 91 ClockContext( 92 ctx, 93 resources, 94 clockSettings, 95 typefaceCache, 96 buffers, 97 buffers.infraMessageBuffer, 98 vibrator, 99 ) 100 ) 101 } else { 102 DefaultClockController(ctx, layoutInflater, resources, settings, messageBuffers) 103 } 104 } 105 106 override fun getClockPickerConfig(settings: ClockSettings): ClockPickerConfig { 107 if (getClocks().all { it.clockId != settings.clockId }) { 108 throw IllegalArgumentException("${settings.clockId} is unsupported by $TAG") 109 } 110 111 if (!isClockReactiveVariantsEnabled) { 112 return ClockPickerConfig( 113 settings.clockId ?: DEFAULT_CLOCK_ID, 114 resources.getString(R.string.clock_default_name), 115 resources.getString(R.string.clock_default_description), 116 resources.getDrawable(R.drawable.clock_default_thumbnail, null), 117 isReactiveToTone = true, 118 axes = emptyList(), 119 presetConfig = null, 120 ) 121 } else { 122 val fontAxes = getDefaultAxes(settings).merge(settings.axes) 123 return ClockPickerConfig( 124 settings.clockId ?: DEFAULT_CLOCK_ID, 125 resources.getString(R.string.clock_default_name), 126 resources.getString(R.string.clock_default_description), 127 resources.getDrawable(R.drawable.clock_default_thumbnail, null), 128 isReactiveToTone = true, 129 axes = fontAxes, 130 presetConfig = 131 AxisPresetConfig( 132 listOf( 133 buildPresetGroup(resources, isRound = true), 134 buildPresetGroup(resources, isRound = false), 135 ) 136 ) 137 .let { cfg -> cfg.copy(current = cfg.findStyle(ClockAxisStyle(fontAxes))) }, 138 ) 139 } 140 } 141 142 companion object { 143 // 750ms @ 120hz -> 90 frames of animation 144 // In practice, 30 looks good enough and limits our memory usage 145 const val NUM_CLOCK_FONT_ANIMATION_STEPS = 30 146 147 val FLEX_TYPEFACE by lazy { 148 // TODO(b/364680873): Move constant to config_clockFontFamily when shipping 149 Typeface.create("google-sans-flex-clock", Typeface.NORMAL) 150 } 151 } 152 } 153