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

Commit c502fe48 authored by Hawkwood Glazier's avatar Hawkwood Glazier Committed by Automerger Merge Worker
Browse files

Merge "Seperate clockface specific callbacks" into tm-qpr-dev am: 883ecccd am: e4c44939

parents 45b8aadf e4c44939
Loading
Loading
Loading
Loading
+22 −11
Original line number Original line Diff line number Diff line
@@ -39,19 +39,19 @@ interface ClockProvider {
    fun getClocks(): List<ClockMetadata>
    fun getClocks(): List<ClockMetadata>


    /** Initializes and returns the target clock design */
    /** Initializes and returns the target clock design */
    fun createClock(id: ClockId): Clock
    fun createClock(id: ClockId): ClockController


    /** A static thumbnail for rendering in some examples */
    /** A static thumbnail for rendering in some examples */
    fun getClockThumbnail(id: ClockId): Drawable?
    fun getClockThumbnail(id: ClockId): Drawable?
}
}


/** Interface for controlling an active clock */
/** Interface for controlling an active clock */
interface Clock {
interface ClockController {
    /** A small version of the clock, appropriate for smaller viewports */
    /** A small version of the clock, appropriate for smaller viewports */
    val smallClock: View
    val smallClock: ClockFaceController


    /** A large version of the clock, appropriate when a bigger viewport is available */
    /** A large version of the clock, appropriate when a bigger viewport is available */
    val largeClock: View
    val largeClock: ClockFaceController


    /** Events that clocks may need to respond to */
    /** Events that clocks may need to respond to */
    val events: ClockEvents
    val events: ClockEvents
@@ -61,7 +61,7 @@ interface Clock {


    /** Initializes various rendering parameters. If never called, provides reasonable defaults. */
    /** Initializes various rendering parameters. If never called, provides reasonable defaults. */
    fun initialize(resources: Resources, dozeFraction: Float, foldFraction: Float) {
    fun initialize(resources: Resources, dozeFraction: Float, foldFraction: Float) {
        events.onColorPaletteChanged(resources, true, true)
        events.onColorPaletteChanged(resources)
        animations.doze(dozeFraction)
        animations.doze(dozeFraction)
        animations.fold(foldFraction)
        animations.fold(foldFraction)
        events.onTimeTick()
        events.onTimeTick()
@@ -71,10 +71,19 @@ interface Clock {
    fun dump(pw: PrintWriter) { }
    fun dump(pw: PrintWriter) { }
}
}


/** Interface for a specific clock face version rendered by the clock */
interface ClockFaceController {
    /** View that renders the clock face */
    val view: View

    /** Events specific to this clock face */
    val events: ClockFaceEvents
}

/** Events that should call when various rendering parameters change */
/** Events that should call when various rendering parameters change */
interface ClockEvents {
interface ClockEvents {
    /** Call every time tick */
    /** Call every time tick */
    fun onTimeTick()
    fun onTimeTick() { }


    /** Call whenever timezone changes */
    /** Call whenever timezone changes */
    fun onTimeZoneChanged(timeZone: TimeZone) { }
    fun onTimeZoneChanged(timeZone: TimeZone) { }
@@ -89,11 +98,7 @@ interface ClockEvents {
    fun onFontSettingChanged() { }
    fun onFontSettingChanged() { }


    /** Call whenever the color palette should update */
    /** Call whenever the color palette should update */
    fun onColorPaletteChanged(
    fun onColorPaletteChanged(resources: Resources) { }
            resources: Resources,
            smallClockIsDark: Boolean,
            largeClockIsDark: Boolean
    ) { }
}
}


/** Methods which trigger various clock animations */
/** Methods which trigger various clock animations */
@@ -111,6 +116,12 @@ interface ClockAnimations {
    fun charge() { }
    fun charge() { }
}
}


/** Events that have specific data about the related face */
interface ClockFaceEvents {
    /** Region Darkness specific to the clock face */
    fun onRegionDarknessChanged(isDark: Boolean) { }
}

/** Some data about a clock design */
/** Some data about a clock design */
data class ClockMetadata(
data class ClockMetadata(
    val clockId: ClockId,
    val clockId: ClockId,
+15 −5
Original line number Original line Diff line number Diff line
@@ -22,7 +22,7 @@ import android.os.UserHandle
import android.provider.Settings
import android.provider.Settings
import android.util.Log
import android.util.Log
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.Clock
import com.android.systemui.plugins.ClockController
import com.android.systemui.plugins.ClockId
import com.android.systemui.plugins.ClockId
import com.android.systemui.plugins.ClockMetadata
import com.android.systemui.plugins.ClockMetadata
import com.android.systemui.plugins.ClockProvider
import com.android.systemui.plugins.ClockProvider
@@ -33,7 +33,7 @@ import com.google.gson.Gson
import javax.inject.Inject
import javax.inject.Inject


private val TAG = ClockRegistry::class.simpleName
private val TAG = ClockRegistry::class.simpleName
private val DEBUG = true
private const val DEBUG = true


/** ClockRegistry aggregates providers and plugins */
/** ClockRegistry aggregates providers and plugins */
open class ClockRegistry(
open class ClockRegistry(
@@ -130,6 +130,10 @@ open class ClockRegistry(
            }
            }


            availableClocks[id] = ClockInfo(clock, provider)
            availableClocks[id] = ClockInfo(clock, provider)
            if (DEBUG) {
                Log.i(TAG, "Added ${clock.clockId}")
            }

            if (currentId == id) {
            if (currentId == id) {
                if (DEBUG) {
                if (DEBUG) {
                    Log.i(TAG, "Current clock ($currentId) was connected")
                    Log.i(TAG, "Current clock ($currentId) was connected")
@@ -143,6 +147,9 @@ open class ClockRegistry(
        val currentId = currentClockId
        val currentId = currentClockId
        for (clock in provider.getClocks()) {
        for (clock in provider.getClocks()) {
            availableClocks.remove(clock.clockId)
            availableClocks.remove(clock.clockId)
            if (DEBUG) {
                Log.i(TAG, "Removed ${clock.clockId}")
            }


            if (currentId == clock.clockId) {
            if (currentId == clock.clockId) {
                Log.w(TAG, "Current clock ($currentId) was disconnected")
                Log.w(TAG, "Current clock ($currentId) was disconnected")
@@ -161,7 +168,7 @@ open class ClockRegistry(
    fun getClockThumbnail(clockId: ClockId): Drawable? =
    fun getClockThumbnail(clockId: ClockId): Drawable? =
        availableClocks[clockId]?.provider?.getClockThumbnail(clockId)
        availableClocks[clockId]?.provider?.getClockThumbnail(clockId)


    fun createExampleClock(clockId: ClockId): Clock? = createClock(clockId)
    fun createExampleClock(clockId: ClockId): ClockController? = createClock(clockId)


    fun registerClockChangeListener(listener: ClockChangeListener) =
    fun registerClockChangeListener(listener: ClockChangeListener) =
        clockChangeListeners.add(listener)
        clockChangeListeners.add(listener)
@@ -169,11 +176,14 @@ open class ClockRegistry(
    fun unregisterClockChangeListener(listener: ClockChangeListener) =
    fun unregisterClockChangeListener(listener: ClockChangeListener) =
        clockChangeListeners.remove(listener)
        clockChangeListeners.remove(listener)


    fun createCurrentClock(): Clock {
    fun createCurrentClock(): ClockController {
        val clockId = currentClockId
        val clockId = currentClockId
        if (isEnabled && clockId.isNotEmpty()) {
        if (isEnabled && clockId.isNotEmpty()) {
            val clock = createClock(clockId)
            val clock = createClock(clockId)
            if (clock != null) {
            if (clock != null) {
                if (DEBUG) {
                    Log.i(TAG, "Rendering clock $clockId")
                }
                return clock
                return clock
            } else {
            } else {
                Log.e(TAG, "Clock $clockId not found; using default")
                Log.e(TAG, "Clock $clockId not found; using default")
@@ -183,7 +193,7 @@ open class ClockRegistry(
        return createClock(DEFAULT_CLOCK_ID)!!
        return createClock(DEFAULT_CLOCK_ID)!!
    }
    }


    private fun createClock(clockId: ClockId): Clock? =
    private fun createClock(clockId: ClockId): ClockController? =
        availableClocks[clockId]?.provider?.createClock(clockId)
        availableClocks[clockId]?.provider?.createClock(clockId)


    private data class ClockInfo(
    private data class ClockInfo(
+234 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2022 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.content.Context
import android.content.res.Resources
import android.graphics.Color
import android.icu.text.NumberFormat
import android.util.TypedValue
import android.view.LayoutInflater
import android.widget.FrameLayout
import androidx.annotation.VisibleForTesting
import com.android.systemui.plugins.ClockAnimations
import com.android.systemui.plugins.ClockController
import com.android.systemui.plugins.ClockEvents
import com.android.systemui.plugins.ClockFaceController
import com.android.systemui.plugins.ClockFaceEvents
import com.android.systemui.shared.R
import java.io.PrintWriter
import java.util.Locale
import java.util.TimeZone

private val TAG = DefaultClockController::class.simpleName

/**
 * Controls the default clock visuals.
 *
 * This serves as an adapter between the clock interface and the AnimatableClockView used by the
 * existing lockscreen clock.
 */
class DefaultClockController(
    ctx: Context,
    private val layoutInflater: LayoutInflater,
    private val resources: Resources,
) : ClockController {
    override val smallClock: DefaultClockFaceController
    override val largeClock: LargeClockFaceController
    private val clocks: List<AnimatableClockView>

    private val burmeseNf = NumberFormat.getInstance(Locale.forLanguageTag("my"))
    private val burmeseNumerals = burmeseNf.format(FORMAT_NUMBER.toLong())
    private val burmeseLineSpacing =
        resources.getFloat(R.dimen.keyguard_clock_line_spacing_scale_burmese)
    private val defaultLineSpacing = resources.getFloat(R.dimen.keyguard_clock_line_spacing_scale)

    override val events: DefaultClockEvents
    override lateinit var animations: DefaultClockAnimations
        private set

    init {
        val parent = FrameLayout(ctx)
        smallClock =
            DefaultClockFaceController(
                layoutInflater.inflate(R.layout.clock_default_small, parent, false)
                    as AnimatableClockView
            )
        largeClock =
            LargeClockFaceController(
                layoutInflater.inflate(R.layout.clock_default_large, parent, false)
                    as AnimatableClockView
            )
        clocks = listOf(smallClock.view, largeClock.view)

        events = DefaultClockEvents()
        animations = DefaultClockAnimations(0f, 0f)
        events.onLocaleChanged(Locale.getDefault())
    }

    override fun initialize(resources: Resources, dozeFraction: Float, foldFraction: Float) {
        largeClock.recomputePadding()
        animations = DefaultClockAnimations(dozeFraction, foldFraction)
        events.onColorPaletteChanged(resources)
        events.onTimeZoneChanged(TimeZone.getDefault())
        events.onTimeTick()
    }

    open inner class DefaultClockFaceController(
        override val view: AnimatableClockView,
    ) : ClockFaceController {
        // MAGENTA is a placeholder, and will be assigned correctly in initialize
        private var currentColor = Color.MAGENTA
        private var isRegionDark = false

        init {
            view.setColors(currentColor, currentColor)
        }

        override val events =
            object : ClockFaceEvents {
                override fun onRegionDarknessChanged(isRegionDark: Boolean) {
                    this@DefaultClockFaceController.isRegionDark = isRegionDark
                    updateColor()
                }
            }

        fun updateColor() {
            val color =
                if (isRegionDark) {
                    resources.getColor(android.R.color.system_accent1_100)
                } else {
                    resources.getColor(android.R.color.system_accent2_600)
                }

            if (currentColor == color) {
                return
            }

            currentColor = color
            view.setColors(DOZE_COLOR, color)
            view.animateAppearOnLockscreen()
        }
    }

    inner class LargeClockFaceController(
        view: AnimatableClockView,
    ) : DefaultClockFaceController(view) {
        fun recomputePadding() {
            val lp = view.getLayoutParams() as FrameLayout.LayoutParams
            lp.topMargin = (-0.5f * view.bottom).toInt()
            view.setLayoutParams(lp)
        }
    }

    inner class DefaultClockEvents : ClockEvents {
        override fun onTimeTick() = clocks.forEach { it.refreshTime() }

        override fun onTimeFormatChanged(is24Hr: Boolean) =
            clocks.forEach { it.refreshFormat(is24Hr) }

        override fun onTimeZoneChanged(timeZone: TimeZone) =
            clocks.forEach { it.onTimeZoneChanged(timeZone) }

        override fun onFontSettingChanged() {
            smallClock.view.setTextSize(
                TypedValue.COMPLEX_UNIT_PX,
                resources.getDimensionPixelSize(R.dimen.small_clock_text_size).toFloat()
            )
            largeClock.view.setTextSize(
                TypedValue.COMPLEX_UNIT_PX,
                resources.getDimensionPixelSize(R.dimen.large_clock_text_size).toFloat()
            )
            largeClock.recomputePadding()
        }

        override fun onColorPaletteChanged(resources: Resources) {
            largeClock.updateColor()
            smallClock.updateColor()
        }

        override fun onLocaleChanged(locale: Locale) {
            val nf = NumberFormat.getInstance(locale)
            if (nf.format(FORMAT_NUMBER.toLong()) == burmeseNumerals) {
                clocks.forEach { it.setLineSpacingScale(burmeseLineSpacing) }
            } else {
                clocks.forEach { it.setLineSpacingScale(defaultLineSpacing) }
            }

            clocks.forEach { it.refreshFormat() }
        }
    }

    inner class DefaultClockAnimations(
        dozeFraction: Float,
        foldFraction: Float,
    ) : ClockAnimations {
        private var foldState = AnimationState(0f)
        private var dozeState = AnimationState(0f)

        init {
            dozeState = AnimationState(dozeFraction)
            foldState = AnimationState(foldFraction)

            if (foldState.isActive) {
                clocks.forEach { it.animateFoldAppear(false) }
            } else {
                clocks.forEach { it.animateDoze(dozeState.isActive, false) }
            }
        }

        override fun enter() {
            if (!dozeState.isActive) {
                clocks.forEach { it.animateAppearOnLockscreen() }
            }
        }

        override fun charge() = clocks.forEach { it.animateCharge { dozeState.isActive } }

        override fun fold(fraction: Float) {
            val (hasChanged, hasJumped) = foldState.update(fraction)
            if (hasChanged) {
                clocks.forEach { it.animateFoldAppear(!hasJumped) }
            }
        }

        override fun doze(fraction: Float) {
            val (hasChanged, hasJumped) = dozeState.update(fraction)
            if (hasChanged) {
                clocks.forEach { it.animateDoze(dozeState.isActive, !hasJumped) }
            }
        }
    }

    private class AnimationState(
        var fraction: Float,
    ) {
        var isActive: Boolean = fraction < 0.5f
        fun update(newFraction: Float): Pair<Boolean, Boolean> {
            val wasActive = isActive
            val hasJumped =
                (fraction == 0f && newFraction == 1f) || (fraction == 1f && newFraction == 0f)
            isActive = newFraction > fraction
            fraction = newFraction
            return Pair(wasActive != isActive, hasJumped)
        }
    }

    override fun dump(pw: PrintWriter) = clocks.forEach { it.dump(pw) }

    companion object {
        @VisibleForTesting const val DOZE_COLOR = Color.WHITE
        private const val FORMAT_NUMBER = 1234567890
    }
}
+4 −200
Original line number Original line Diff line number Diff line
@@ -15,24 +15,14 @@ package com.android.systemui.shared.clocks


import android.content.Context
import android.content.Context
import android.content.res.Resources
import android.content.res.Resources
import android.graphics.Color
import android.graphics.drawable.Drawable
import android.graphics.drawable.Drawable
import android.icu.text.NumberFormat
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.LayoutInflater
import android.widget.FrameLayout
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.Clock
import com.android.systemui.plugins.ClockController
import com.android.systemui.plugins.ClockAnimations
import com.android.systemui.plugins.ClockEvents
import com.android.systemui.plugins.ClockId
import com.android.systemui.plugins.ClockId
import com.android.systemui.plugins.ClockMetadata
import com.android.systemui.plugins.ClockMetadata
import com.android.systemui.plugins.ClockProvider
import com.android.systemui.plugins.ClockProvider
import com.android.systemui.shared.R
import com.android.systemui.shared.R
import java.io.PrintWriter
import java.util.Locale
import java.util.TimeZone
import javax.inject.Inject
import javax.inject.Inject


private val TAG = DefaultClockProvider::class.simpleName
private val TAG = DefaultClockProvider::class.simpleName
@@ -48,11 +38,12 @@ class DefaultClockProvider @Inject constructor(
    override fun getClocks(): List<ClockMetadata> =
    override fun getClocks(): List<ClockMetadata> =
        listOf(ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME))
        listOf(ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME))


    override fun createClock(id: ClockId): Clock {
    override fun createClock(id: ClockId): ClockController {
        if (id != DEFAULT_CLOCK_ID) {
        if (id != DEFAULT_CLOCK_ID) {
            throw IllegalArgumentException("$id is unsupported by $TAG")
            throw IllegalArgumentException("$id is unsupported by $TAG")
        }
        }
        return DefaultClock(ctx, layoutInflater, resources)

        return DefaultClockController(ctx, layoutInflater, resources)
    }
    }


    override fun getClockThumbnail(id: ClockId): Drawable? {
    override fun getClockThumbnail(id: ClockId): Drawable? {
@@ -64,190 +55,3 @@ class DefaultClockProvider @Inject constructor(
        return resources.getDrawable(R.drawable.clock_default_thumbnail, null)
        return resources.getDrawable(R.drawable.clock_default_thumbnail, null)
    }
    }
}
}

/**
 * Controls the default clock visuals.
 *
 * This serves as an adapter between the clock interface and the
 * AnimatableClockView used by the existing lockscreen clock.
 */
class DefaultClock(
        ctx: Context,
        private val layoutInflater: LayoutInflater,
        private val resources: Resources
) : Clock {
    override val smallClock: AnimatableClockView
    override val largeClock: AnimatableClockView
    private val clocks get() = listOf(smallClock, largeClock)

    private val burmeseNf = NumberFormat.getInstance(Locale.forLanguageTag("my"))
    private val burmeseNumerals = burmeseNf.format(FORMAT_NUMBER.toLong())
    private val burmeseLineSpacing =
        resources.getFloat(R.dimen.keyguard_clock_line_spacing_scale_burmese)
    private val defaultLineSpacing = resources.getFloat(R.dimen.keyguard_clock_line_spacing_scale)

    override val events: ClockEvents
    override lateinit var animations: ClockAnimations
        private set

    private var smallRegionDarkness = false
    private var largeRegionDarkness = false

    init {
        val parent = FrameLayout(ctx)

        smallClock = layoutInflater.inflate(
            R.layout.clock_default_small,
            parent,
            false
        ) as AnimatableClockView

        largeClock = layoutInflater.inflate(
            R.layout.clock_default_large,
            parent,
            false
        ) as AnimatableClockView

        events = DefaultClockEvents()
        animations = DefaultClockAnimations(0f, 0f)

        events.onLocaleChanged(Locale.getDefault())

        // DOZE_COLOR is a placeholder, and will be assigned correctly in initialize
        clocks.forEach { it.setColors(DOZE_COLOR, DOZE_COLOR) }
    }

    override fun initialize(resources: Resources, dozeFraction: Float, foldFraction: Float) {
        recomputePadding()
        animations = DefaultClockAnimations(dozeFraction, foldFraction)
        events.onColorPaletteChanged(resources, true, true)
        events.onTimeZoneChanged(TimeZone.getDefault())
        events.onTimeTick()
    }

    inner class DefaultClockEvents() : ClockEvents {
        override fun onTimeTick() = clocks.forEach { it.refreshTime() }

        override fun onTimeFormatChanged(is24Hr: Boolean) =
            clocks.forEach { it.refreshFormat(is24Hr) }

        override fun onTimeZoneChanged(timeZone: TimeZone) =
            clocks.forEach { it.onTimeZoneChanged(timeZone) }

        override fun onFontSettingChanged() {
            smallClock.setTextSize(
                TypedValue.COMPLEX_UNIT_PX,
                resources.getDimensionPixelSize(R.dimen.small_clock_text_size).toFloat()
            )
            largeClock.setTextSize(
                TypedValue.COMPLEX_UNIT_PX,
                resources.getDimensionPixelSize(R.dimen.large_clock_text_size).toFloat()
            )
            recomputePadding()
        }

        override fun onColorPaletteChanged(
                resources: Resources,
                smallClockIsDark: Boolean,
                largeClockIsDark: Boolean
        ) {
            if (smallRegionDarkness != smallClockIsDark) {
                smallRegionDarkness = smallClockIsDark
                updateClockColor(smallClock, smallClockIsDark)
            }
            if (largeRegionDarkness != largeClockIsDark) {
                largeRegionDarkness = largeClockIsDark
                updateClockColor(largeClock, largeClockIsDark)
            }
        }

        override fun onLocaleChanged(locale: Locale) {
            val nf = NumberFormat.getInstance(locale)
            if (nf.format(FORMAT_NUMBER.toLong()) == burmeseNumerals) {
                clocks.forEach { it.setLineSpacingScale(burmeseLineSpacing) }
            } else {
                clocks.forEach { it.setLineSpacingScale(defaultLineSpacing) }
            }

            clocks.forEach { it.refreshFormat() }
        }
    }

    inner class DefaultClockAnimations(
        dozeFraction: Float,
        foldFraction: Float
    ) : ClockAnimations {
        private var foldState = AnimationState(0f)
        private var dozeState = AnimationState(0f)

        init {
            dozeState = AnimationState(dozeFraction)
            foldState = AnimationState(foldFraction)

            if (foldState.isActive) {
                clocks.forEach { it.animateFoldAppear(false) }
            } else {
                clocks.forEach { it.animateDoze(dozeState.isActive, false) }
            }
        }

        override fun enter() {
            if (!dozeState.isActive) {
                clocks.forEach { it.animateAppearOnLockscreen() }
            }
        }

        override fun charge() = clocks.forEach { it.animateCharge { dozeState.isActive } }

        override fun fold(fraction: Float) {
            val (hasChanged, hasJumped) = foldState.update(fraction)
            if (hasChanged) {
                clocks.forEach { it.animateFoldAppear(!hasJumped) }
            }
        }

        override fun doze(fraction: Float) {
            val (hasChanged, hasJumped) = dozeState.update(fraction)
            if (hasChanged) {
                clocks.forEach { it.animateDoze(dozeState.isActive, !hasJumped) }
            }
        }
    }

    private class AnimationState(
        var fraction: Float
    ) {
        var isActive: Boolean = fraction < 0.5f
        fun update(newFraction: Float): Pair<Boolean, Boolean> {
            val wasActive = isActive
            val hasJumped = (fraction == 0f && newFraction == 1f) ||
                (fraction == 1f && newFraction == 0f)
            isActive = newFraction > fraction
            fraction = newFraction
            return Pair(wasActive != isActive, hasJumped)
        }
    }

    private fun updateClockColor(clock: AnimatableClockView, isRegionDark: Boolean) {
        val color = if (isRegionDark) {
            resources.getColor(android.R.color.system_accent1_100)
        } else {
            resources.getColor(android.R.color.system_accent2_600)
        }
        clock.setColors(DOZE_COLOR, color)
        clock.animateAppearOnLockscreen()
    }

    private fun recomputePadding() {
        val lp = largeClock.getLayoutParams() as FrameLayout.LayoutParams
        lp.topMargin = (-0.5f * largeClock.bottom).toInt()
        largeClock.setLayoutParams(lp)
    }

    override fun dump(pw: PrintWriter) = clocks.forEach { it.dump(pw) }

    companion object {
        @VisibleForTesting const val DOZE_COLOR = Color.WHITE
        private const val FORMAT_NUMBER = 1234567890
    }
}
+52 −45

File changed.

Preview size limit exceeded, changes collapsed.

Loading