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

Commit 356e8bbc authored by Hawkwood Glazier's avatar Hawkwood Glazier
Browse files

Remove most of ClockDesign

Bug: 364680879
Test: Manual + Screenshot
Flag: com.android.systemui.clock_reactive_variants
Change-Id: I17ef499943cc5c5cc79bc55629ff835c62b1d9ba
parent 5a754450
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