Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cc63b2fa authored by Hawkwood Glazier's avatar Hawkwood Glazier Committed by Android (Google) Code Review
Browse files

Merge "Remove most of ClockDesign" into main

parents fc6a1218 356e8bbc
Loading
Loading
Loading
Loading
+0 −282
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.shared.clocks

import android.graphics.Point
import android.view.animation.Interpolator
import com.android.app.animation.Interpolators
import com.android.internal.annotations.Keep
import com.android.systemui.monet.Style as MonetStyle
import com.android.systemui.shared.clocks.view.HorizontalAlignment
import com.android.systemui.shared.clocks.view.VerticalAlignment

/** Data format for a simple asset-defined clock */
@Keep
data class ClockDesign(
    val id: String,
    val name: String? = null,
    val description: String? = null,
    val thumbnail: String? = null,
    val large: ClockFace? = null,
    val small: ClockFace? = null,
    @MonetStyle.Type val colorPalette: Int? = null,
)

/** Describes a clock using layers */
@Keep
data class ClockFace(
    val layers: List<ClockLayer> = listOf<ClockLayer>(),
    val layerBounds: LayerBounds = LayerBounds.FIT,
    val wallpaper: String? = null,
    val faceLayout: DigitalFaceLayout? = null,
    val pickerScale: ClockFaceScaleInPicker? = ClockFaceScaleInPicker(1.0f, 1.0f),
)

@Keep data class ClockFaceScaleInPicker(val scaleX: Float, val scaleY: Float)

/** Base Type for a Clock Layer */
@Keep
interface ClockLayer {
    /** Override of face LayerBounds setting for this layer */
    val layerBounds: LayerBounds?
}

/** Clock layer that renders a static asset */
@Keep
data class AssetLayer(
    /** Asset to render in this layer */
    val asset: AssetReference,
    override val layerBounds: LayerBounds? = null,
) : ClockLayer

/** Clock layer that renders the time (or a component of it) using numerals */
@Keep
data class DigitalHandLayer(
    /** See SimpleDateFormat for timespec format info */
    val timespec: DigitalTimespec,
    val style: TextStyle,
    // adoStyle concrete type must match style,
    // cause styles will transition between style and aodStyle
    val aodStyle: TextStyle?,
    val timer: Int? = null,
    override val layerBounds: LayerBounds? = null,
    var faceLayout: DigitalFaceLayout? = null,
    // we pass 12-hour format from json, which will be converted to 24-hour format in codes
    val dateTimeFormat: String,
    val alignment: DigitalAlignment?,
    // ratio of margins to measured size, currently used for handwritten clocks
    val marginRatio: DigitalMarginRatio? = DigitalMarginRatio(),
) : ClockLayer

/** Clock layer that renders the time (or a component of it) using numerals */
@Keep
data class ComposedDigitalHandLayer(
    val customizedView: String? = null,
    /** See SimpleDateFormat for timespec format info */
    val digitalLayers: List<DigitalHandLayer> = listOf<DigitalHandLayer>(),
    override val layerBounds: LayerBounds? = null,
) : ClockLayer

@Keep
data class DigitalAlignment(
    val horizontalAlignment: HorizontalAlignment?,
    val verticalAlignment: VerticalAlignment?,
)

@Keep
data class DigitalMarginRatio(
    val left: Float = 0F,
    val top: Float = 0F,
    val right: Float = 0F,
    val bottom: Float = 0F,
)

/** Clock layer which renders a component of the time using an analog hand */
@Keep
data class AnalogHandLayer(
    val timespec: AnalogTimespec,
    val tickMode: AnalogTickMode,
    val asset: AssetReference,
    val timer: Int? = null,
    val clock_pivot: Point = Point(0, 0),
    val asset_pivot: Point? = null,
    val length: Float = 1f,
    override val layerBounds: LayerBounds? = null,
) : ClockLayer

/** Clock layer which renders the time using an AVD */
@Keep
data class AnimatedHandLayer(
    val timespec: AnalogTimespec,
    val asset: AssetReference,
    val timer: Int? = null,
    override val layerBounds: LayerBounds? = null,
) : ClockLayer

/** A collection of asset references for use in different device modes */
@Keep
data class AssetReference(
    val light: String,
    val dark: String,
    val doze: String? = null,
    val lightTint: String? = null,
    val darkTint: String? = null,
    val dozeTint: String? = null,
)

/**
 * Core TextStyling attributes for text clocks. Both color and sizing information can be applied to
 * either subtype.
 */
@Keep
interface TextStyle {
    // fontSizeScale is a scale factor applied to the default clock's font size.
    val fontSizeScale: Float?
}

/**
 * This specifies a font and styling parameters for that font. This is rendered using a text view
 * and the text animation classes used by the default clock. To ensure default value take effects,
 * all parameters MUST have a default value
 */
@Keep
data class FontTextStyle(
    // Font to load and use in the TextView
    val fontFamily: String? = null,
    val lineHeight: Float? = null,
    val borderWidth: String? = null,
    // ratio of borderWidth / fontSize
    val borderWidthScale: Float? = null,
    // A color literal like `#FF00FF` or a color resource like `@android:color/system_accent1_100`
    val fillColorLight: String? = null,
    // A color literal like `#FF00FF` or a color resource like `@android:color/system_accent1_100`
    val fillColorDark: String? = null,
    override val fontSizeScale: Float? = null,
    // used when alternate in one font file is needed
    var fontFeatureSettings: String? = null,
    val renderType: RenderType = RenderType.STROKE_TEXT,
    val outlineColor: String? = null,
    val transitionDuration: Long = -1L,
    val transitionInterpolator: InterpolatorEnum? = null,
) : TextStyle

/**
 * As an alternative to using a font, we can instead render a digital clock using a set of drawables
 * for each numeral, and optionally a colon. These drawables will be rendered directly after sizing
 * and placing them. This may be easier than generating a font file in some cases, and is provided
 * for ease of use. Unlike fonts, these are not localizable to other numeric systems (like Burmese).
 */
@Keep
data class LottieTextStyle(
    val numbers: List<String> = listOf(),
    // Spacing between numbers, dimension string
    val spacing: String = "0dp",
    // Colon drawable may be omitted if unused in format spec
    val colon: String? = null,
    // key is keypath name to get strokes from lottie, value is the color name to query color in
    // palette, e.g. @android:color/system_accent1_100
    val fillColorLightMap: Map<String, String>? = null,
    val fillColorDarkMap: Map<String, String>? = null,
    override val fontSizeScale: Float? = null,
    val paddingVertical: String = "0dp",
    val paddingHorizontal: String = "0dp",
) : TextStyle

/** Layer sizing mode for the clockface or layer */
enum class LayerBounds {
    /**
     * Sized so the larger dimension matches the allocated space. This results in some of the
     * allocated space being unused.
     */
    FIT,

    /**
     * Sized so the smaller dimension matches the allocated space. This will clip some content to
     * the edges of the space.
     */
    FILL,

    /** Fills the allocated space exactly by stretching the layer */
    STRETCH,
}

/** Ticking mode for analog hands. */
enum class AnalogTickMode {
    SWEEP,
    TICK,
}

/** Timspec options for Analog Hands. Named for tick interval. */
enum class AnalogTimespec {
    SECONDS,
    MINUTES,
    HOURS,
    HOURS_OF_DAY,
    DAY_OF_WEEK,
    DAY_OF_MONTH,
    DAY_OF_YEAR,
    WEEK,
    MONTH,
    TIMER,
}

enum class DigitalTimespec {
    TIME_FULL_FORMAT,
    DIGIT_PAIR,
    FIRST_DIGIT,
    SECOND_DIGIT,
    DATE_FORMAT,
}

enum class DigitalFaceLayout {
    // can only use HH_PAIR, MM_PAIR from DigitalTimespec
    TWO_PAIRS_VERTICAL,
    TWO_PAIRS_HORIZONTAL,
    // can only use HOUR_FIRST_DIGIT, HOUR_SECOND_DIGIT, MINUTE_FIRST_DIGIT, MINUTE_SECOND_DIGIT
    // from DigitalTimespec, used for tabular layout when the font doesn't support tnum
    FOUR_DIGITS_ALIGN_CENTER,
    FOUR_DIGITS_HORIZONTAL,
}

enum class RenderType {
    CHANGE_WEIGHT,
    HOLLOW_TEXT,
    STROKE_TEXT,
    OUTER_OUTLINE_TEXT,
}

enum class InterpolatorEnum(factory: () -> Interpolator) {
    STANDARD({ Interpolators.STANDARD }),
    EMPHASIZED({ Interpolators.EMPHASIZED });

    val interpolator: Interpolator by lazy(factory)
}

fun generateDigitalLayerIdString(layer: DigitalHandLayer): String {
    return if (
        layer.timespec == DigitalTimespec.TIME_FULL_FORMAT ||
            layer.timespec == DigitalTimespec.DATE_FORMAT
    ) {
        layer.timespec.toString()
    } else {
        if ("h" in layer.dateTimeFormat) {
            "HOUR" + "_" + layer.timespec.toString()
        } else {
            "MINUTE" + "_" + layer.timespec.toString()
        }
    }
}
+44 −29
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.systemui.shared.clocks

import android.graphics.Rect
import androidx.annotation.VisibleForTesting
import com.android.app.animation.Interpolators
import com.android.systemui.log.core.Logger
import com.android.systemui.plugins.clocks.AlarmData
import com.android.systemui.plugins.clocks.ClockAnimations
@@ -29,14 +30,13 @@ import com.android.systemui.plugins.clocks.ThemeConfig
import com.android.systemui.plugins.clocks.WeatherData
import com.android.systemui.plugins.clocks.ZenData
import com.android.systemui.shared.clocks.view.FlexClockView
import com.android.systemui.shared.clocks.view.SimpleDigitalClockTextView
import com.android.systemui.shared.clocks.view.HorizontalAlignment
import com.android.systemui.shared.clocks.view.VerticalAlignment
import java.util.Locale
import java.util.TimeZone

class ComposedDigitalLayerController(
    private val clockCtx: ClockContext,
    private val layer: ComposedDigitalHandLayer,
) : SimpleClockLayerController {
class ComposedDigitalLayerController(private val clockCtx: ClockContext) :
    SimpleClockLayerController {
    private val logger =
        Logger(clockCtx.messageBuffer, ComposedDigitalLayerController::class.simpleName!!)

@@ -46,14 +46,40 @@ class ComposedDigitalLayerController(
    override val view = FlexClockView(clockCtx)

    init {
        layer.digitalLayers.forEach {
            val childView = SimpleDigitalClockTextView(clockCtx)
            val controller =
                SimpleDigitalHandLayerController(clockCtx, it as DigitalHandLayer, childView)

            view.addView(childView)
        fun createController(cfg: LayerConfig) {
            val controller = SimpleDigitalHandLayerController(clockCtx, cfg)
            view.addView(controller.view)
            layerControllers.add(controller)
        }

        val layerCfg =
            LayerConfig(
                style = FontTextStyle(lineHeight = 147.25f),
                aodStyle =
                    FontTextStyle(
                        transitionInterpolator = Interpolators.EMPHASIZED,
                        transitionDuration = 750,
                    ),
                alignment =
                    DigitalAlignment(HorizontalAlignment.CENTER, VerticalAlignment.BASELINE),

                // Placeholders
                timespec = DigitalTimespec.TIME_FULL_FORMAT,
                dateTimeFormat = "hh:mm",
            )

        createController(
            layerCfg.copy(timespec = DigitalTimespec.FIRST_DIGIT, dateTimeFormat = "hh")
        )
        createController(
            layerCfg.copy(timespec = DigitalTimespec.SECOND_DIGIT, dateTimeFormat = "hh")
        )
        createController(
            layerCfg.copy(timespec = DigitalTimespec.FIRST_DIGIT, dateTimeFormat = "mm")
        )
        createController(
            layerCfg.copy(timespec = DigitalTimespec.SECOND_DIGIT, dateTimeFormat = "mm")
        )
    }

    private fun refreshTime() {
@@ -79,17 +105,11 @@ class ComposedDigitalLayerController(
                refreshTime()
            }

            override fun onWeatherDataChanged(data: WeatherData) {
                view.onWeatherDataChanged(data)
            }
            override fun onWeatherDataChanged(data: WeatherData) {}

            override fun onAlarmDataChanged(data: AlarmData) {
                view.onAlarmDataChanged(data)
            }
            override fun onAlarmDataChanged(data: AlarmData) {}

            override fun onZenDataChanged(data: ZenData) {
                view.onZenDataChanged(data)
            }
            override fun onZenDataChanged(data: ZenData) {}

            override fun onFontAxesChanged(axes: List<ClockFontAxisSetting>) {
                view.updateAxes(axes)
@@ -123,15 +143,11 @@ class ComposedDigitalLayerController(
                view.animateCharge()
            }

            override fun onPositionUpdated(fromLeft: Int, direction: Int, fraction: Float) {
                view.onPositionUpdated(fromLeft, direction, fraction)
            }
            override fun onPositionUpdated(fromLeft: Int, direction: Int, fraction: Float) {}

            override fun onPositionUpdated(distance: Float, fraction: Float) {}

            override fun onPickerCarouselSwiping(swipingFraction: Float) {
                view.onPickerCarouselSwiping(swipingFraction)
            }
            override fun onPickerCarouselSwiping(swipingFraction: Float) {}
        }

    override val faceEvents =
@@ -163,9 +179,8 @@ class ComposedDigitalLayerController(

    override val config =
        ClockFaceConfig(
            hasCustomWeatherDataDisplay = view.hasCustomWeatherDataDisplay,
            hasCustomPositionUpdatedAnimation = view.hasCustomPositionUpdatedAnimation,
            useCustomClockScene = view.useCustomClockScene,
            hasCustomWeatherDataDisplay = false,
            hasCustomPositionUpdatedAnimation = true,
        )

    @VisibleForTesting
+1 −118
Original line number Diff line number Diff line
@@ -27,8 +27,6 @@ import com.android.systemui.plugins.clocks.ClockMetadata
import com.android.systemui.plugins.clocks.ClockPickerConfig
import com.android.systemui.plugins.clocks.ClockProvider
import com.android.systemui.plugins.clocks.ClockSettings
import com.android.systemui.shared.clocks.view.HorizontalAlignment
import com.android.systemui.shared.clocks.view.VerticalAlignment

private val TAG = DefaultClockProvider::class.simpleName
const val DEFAULT_CLOCK_ID = "DEFAULT"
@@ -78,8 +76,7 @@ class DefaultClockProvider(
                    typefaceCache,
                    buffers,
                    buffers.infraMessageBuffer,
                ),
                FLEX_DESIGN,
                )
            )
        } else {
            DefaultClockController(ctx, layoutInflater, resources, settings, messageBuffers)
@@ -128,119 +125,5 @@ class DefaultClockProvider(
            // TODO(b/364680873): Move constant to config_clockFontFamily when shipping
            Typeface.create("google-sans-flex-clock", Typeface.NORMAL)
        }

        val FLEX_DESIGN = run {
            val largeLayer =
                listOf(
                    ComposedDigitalHandLayer(
                        layerBounds = LayerBounds.FIT,
                        customizedView = "FlexClockView",
                        digitalLayers =
                            listOf(
                                DigitalHandLayer(
                                    layerBounds = LayerBounds.FIT,
                                    timespec = DigitalTimespec.FIRST_DIGIT,
                                    style = FontTextStyle(lineHeight = 147.25f),
                                    aodStyle =
                                        FontTextStyle(
                                            fillColorLight = "#FFFFFFFF",
                                            outlineColor = "#00000000",
                                            renderType = RenderType.CHANGE_WEIGHT,
                                            transitionInterpolator = InterpolatorEnum.EMPHASIZED,
                                            transitionDuration = 750,
                                        ),
                                    alignment =
                                        DigitalAlignment(
                                            HorizontalAlignment.CENTER,
                                            VerticalAlignment.BASELINE,
                                        ),
                                    dateTimeFormat = "hh",
                                ),
                                DigitalHandLayer(
                                    layerBounds = LayerBounds.FIT,
                                    timespec = DigitalTimespec.SECOND_DIGIT,
                                    style = FontTextStyle(lineHeight = 147.25f),
                                    aodStyle =
                                        FontTextStyle(
                                            fillColorLight = "#FFFFFFFF",
                                            outlineColor = "#00000000",
                                            renderType = RenderType.CHANGE_WEIGHT,
                                            transitionInterpolator = InterpolatorEnum.EMPHASIZED,
                                            transitionDuration = 750,
                                        ),
                                    alignment =
                                        DigitalAlignment(
                                            HorizontalAlignment.CENTER,
                                            VerticalAlignment.BASELINE,
                                        ),
                                    dateTimeFormat = "hh",
                                ),
                                DigitalHandLayer(
                                    layerBounds = LayerBounds.FIT,
                                    timespec = DigitalTimespec.FIRST_DIGIT,
                                    style = FontTextStyle(lineHeight = 147.25f),
                                    aodStyle =
                                        FontTextStyle(
                                            fillColorLight = "#FFFFFFFF",
                                            outlineColor = "#00000000",
                                            renderType = RenderType.CHANGE_WEIGHT,
                                            transitionInterpolator = InterpolatorEnum.EMPHASIZED,
                                            transitionDuration = 750,
                                        ),
                                    alignment =
                                        DigitalAlignment(
                                            HorizontalAlignment.CENTER,
                                            VerticalAlignment.BASELINE,
                                        ),
                                    dateTimeFormat = "mm",
                                ),
                                DigitalHandLayer(
                                    layerBounds = LayerBounds.FIT,
                                    timespec = DigitalTimespec.SECOND_DIGIT,
                                    style = FontTextStyle(lineHeight = 147.25f),
                                    aodStyle =
                                        FontTextStyle(
                                            fillColorLight = "#FFFFFFFF",
                                            outlineColor = "#00000000",
                                            renderType = RenderType.CHANGE_WEIGHT,
                                            transitionInterpolator = InterpolatorEnum.EMPHASIZED,
                                            transitionDuration = 750,
                                        ),
                                    alignment =
                                        DigitalAlignment(
                                            HorizontalAlignment.CENTER,
                                            VerticalAlignment.BASELINE,
                                        ),
                                    dateTimeFormat = "mm",
                                ),
                            ),
                    )
                )

            val smallLayer =
                listOf(
                    DigitalHandLayer(
                        layerBounds = LayerBounds.FIT,
                        timespec = DigitalTimespec.TIME_FULL_FORMAT,
                        style = FontTextStyle(fontSizeScale = 0.98f),
                        aodStyle =
                            FontTextStyle(
                                fillColorLight = "#FFFFFFFF",
                                outlineColor = "#00000000",
                                renderType = RenderType.CHANGE_WEIGHT,
                            ),
                        alignment = DigitalAlignment(HorizontalAlignment.LEFT, null),
                        dateTimeFormat = "h:mm",
                    )
                )

            ClockDesign(
                id = DEFAULT_CLOCK_ID,
                name = "@string/clock_default_name",
                description = "@string/clock_default_description",
                large = ClockFace(layers = largeLayer),
                small = ClockFace(layers = smallLayer),
            )
        }
    }
}
+1 −6
Original line number Diff line number Diff line
@@ -32,21 +32,16 @@ import java.util.Locale
import java.util.TimeZone

/** Controller for the default flex clock */
class FlexClockController(
    private val clockCtx: ClockContext,
    val design: ClockDesign, // TODO(b/364680879): Remove when done inlining
) : ClockController {
class FlexClockController(private val clockCtx: ClockContext) : ClockController {
    override val smallClock =
        FlexClockFaceController(
            clockCtx.copy(messageBuffer = clockCtx.messageBuffers.smallClockMessageBuffer),
            design.small ?: design.large!!,
            isLargeClock = false,
        )

    override val largeClock =
        FlexClockFaceController(
            clockCtx.copy(messageBuffer = clockCtx.messageBuffers.largeClockMessageBuffer),
            design.large ?: design.small!!,
            isLargeClock = true,
        )

+19 −22
Original line number Diff line number Diff line
@@ -35,17 +35,14 @@ import com.android.systemui.plugins.clocks.ThemeConfig
import com.android.systemui.plugins.clocks.WeatherData
import com.android.systemui.plugins.clocks.ZenData
import com.android.systemui.shared.clocks.view.FlexClockView
import com.android.systemui.shared.clocks.view.SimpleDigitalClockTextView
import com.android.systemui.shared.clocks.view.HorizontalAlignment
import java.util.Locale
import java.util.TimeZone
import kotlin.math.max

// TODO(b/364680879): Merge w/ ComposedDigitalLayerController
class FlexClockFaceController(
    clockCtx: ClockContext,
    face: ClockFace,
    private val isLargeClock: Boolean,
) : ClockFaceController {
class FlexClockFaceController(clockCtx: ClockContext, private val isLargeClock: Boolean) :
    ClockFaceController {
    override val view: View
        get() = layerController.view

@@ -59,19 +56,12 @@ class FlexClockFaceController(
    val timespecHandler = DigitalTimespecHandler(DigitalTimespec.TIME_FULL_FORMAT, "hh:mm")

    init {
        val lp = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
        lp.gravity = Gravity.CENTER

        val layer = face.layers[0]

        layerController =
            if (isLargeClock) {
                ComposedDigitalLayerController(clockCtx, layer as ComposedDigitalHandLayer)
            } else {
                val childView = SimpleDigitalClockTextView(clockCtx)
                SimpleDigitalHandLayerController(clockCtx, layer as DigitalHandLayer, childView)
            }
        layerController.view.layoutParams = lp
            if (isLargeClock) ComposedDigitalLayerController(clockCtx)
            else SimpleDigitalHandLayerController(clockCtx, SMALL_LAYER_CONFIG)

        layerController.view.layoutParams =
            FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT).apply { gravity = Gravity.CENTER }
    }

    /** See documentation at [FlexClockView.offsetGlyphsForStepClockAnimation]. */
@@ -227,10 +217,6 @@ class FlexClockFaceController(
            }

            override fun onPickerCarouselSwiping(swipingFraction: Float) {
                face.pickerScale?.let {
                    view.scaleX = swipingFraction * (1 - it.scaleX) + it.scaleX
                    view.scaleY = swipingFraction * (1 - it.scaleY) + it.scaleY
                }
                if (isLargeClock && !(view as FlexClockView).isAlignedWithScreen()) {
                    view.translationY = keyguardLargeClockTopMargin / 2F * swipingFraction
                }
@@ -248,4 +234,15 @@ class FlexClockFaceController(
                // TODO(b/378128811) port stepping animation
            }
        }

    companion object {
        val SMALL_LAYER_CONFIG =
            LayerConfig(
                timespec = DigitalTimespec.TIME_FULL_FORMAT,
                style = FontTextStyle(fontSizeScale = 0.98f),
                aodStyle = FontTextStyle(),
                alignment = DigitalAlignment(HorizontalAlignment.LEFT, null),
                dateTimeFormat = "h:mm",
            )
    }
}
Loading