Loading packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt +22 −11 Original line number Diff line number Diff line Loading @@ -39,19 +39,19 @@ interface ClockProvider { fun getClocks(): List<ClockMetadata> /** 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 */ fun getClockThumbnail(id: ClockId): Drawable? } /** Interface for controlling an active clock */ interface Clock { interface ClockController { /** 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 */ val largeClock: View val largeClock: ClockFaceController /** Events that clocks may need to respond to */ val events: ClockEvents Loading @@ -61,7 +61,7 @@ interface Clock { /** Initializes various rendering parameters. If never called, provides reasonable defaults. */ fun initialize(resources: Resources, dozeFraction: Float, foldFraction: Float) { events.onColorPaletteChanged(resources, true, true) events.onColorPaletteChanged(resources) animations.doze(dozeFraction) animations.fold(foldFraction) events.onTimeTick() Loading @@ -71,10 +71,19 @@ interface Clock { 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 */ interface ClockEvents { /** Call every time tick */ fun onTimeTick() fun onTimeTick() { } /** Call whenever timezone changes */ fun onTimeZoneChanged(timeZone: TimeZone) { } Loading @@ -89,11 +98,7 @@ interface ClockEvents { fun onFontSettingChanged() { } /** Call whenever the color palette should update */ fun onColorPaletteChanged( resources: Resources, smallClockIsDark: Boolean, largeClockIsDark: Boolean ) { } fun onColorPaletteChanged(resources: Resources) { } } /** Methods which trigger various clock animations */ Loading @@ -111,6 +116,12 @@ interface ClockAnimations { 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 */ data class ClockMetadata( val clockId: ClockId, Loading packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt +15 −5 Original line number Diff line number Diff line Loading @@ -22,7 +22,7 @@ import android.os.UserHandle import android.provider.Settings import android.util.Log 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.ClockMetadata import com.android.systemui.plugins.ClockProvider Loading @@ -33,7 +33,7 @@ import com.google.gson.Gson import javax.inject.Inject private val TAG = ClockRegistry::class.simpleName private val DEBUG = true private const val DEBUG = true /** ClockRegistry aggregates providers and plugins */ open class ClockRegistry( Loading Loading @@ -130,6 +130,10 @@ open class ClockRegistry( } availableClocks[id] = ClockInfo(clock, provider) if (DEBUG) { Log.i(TAG, "Added ${clock.clockId}") } if (currentId == id) { if (DEBUG) { Log.i(TAG, "Current clock ($currentId) was connected") Loading @@ -143,6 +147,9 @@ open class ClockRegistry( val currentId = currentClockId for (clock in provider.getClocks()) { availableClocks.remove(clock.clockId) if (DEBUG) { Log.i(TAG, "Removed ${clock.clockId}") } if (currentId == clock.clockId) { Log.w(TAG, "Current clock ($currentId) was disconnected") Loading @@ -161,7 +168,7 @@ open class ClockRegistry( fun getClockThumbnail(clockId: ClockId): Drawable? = availableClocks[clockId]?.provider?.getClockThumbnail(clockId) fun createExampleClock(clockId: ClockId): Clock? = createClock(clockId) fun createExampleClock(clockId: ClockId): ClockController? = createClock(clockId) fun registerClockChangeListener(listener: ClockChangeListener) = clockChangeListeners.add(listener) Loading @@ -169,11 +176,14 @@ open class ClockRegistry( fun unregisterClockChangeListener(listener: ClockChangeListener) = clockChangeListeners.remove(listener) fun createCurrentClock(): Clock { fun createCurrentClock(): ClockController { val clockId = currentClockId if (isEnabled && clockId.isNotEmpty()) { val clock = createClock(clockId) if (clock != null) { if (DEBUG) { Log.i(TAG, "Rendering clock $clockId") } return clock } else { Log.e(TAG, "Clock $clockId not found; using default") Loading @@ -183,7 +193,7 @@ open class ClockRegistry( return createClock(DEFAULT_CLOCK_ID)!! } private fun createClock(clockId: ClockId): Clock? = private fun createClock(clockId: ClockId): ClockController? = availableClocks[clockId]?.provider?.createClock(clockId) private data class ClockInfo( Loading packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockController.kt 0 → 100644 +234 −0 Original line number 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 } } packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt +4 −200 Original line number Diff line number Diff line Loading @@ -15,24 +15,14 @@ package com.android.systemui.shared.clocks import android.content.Context import android.content.res.Resources import android.graphics.Color import android.graphics.drawable.Drawable import android.icu.text.NumberFormat import android.util.TypedValue 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.plugins.Clock import com.android.systemui.plugins.ClockAnimations import com.android.systemui.plugins.ClockEvents import com.android.systemui.plugins.ClockController import com.android.systemui.plugins.ClockId import com.android.systemui.plugins.ClockMetadata import com.android.systemui.plugins.ClockProvider import com.android.systemui.shared.R import java.io.PrintWriter import java.util.Locale import java.util.TimeZone import javax.inject.Inject private val TAG = DefaultClockProvider::class.simpleName Loading @@ -48,11 +38,12 @@ class DefaultClockProvider @Inject constructor( override fun getClocks(): List<ClockMetadata> = 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) { throw IllegalArgumentException("$id is unsupported by $TAG") } return DefaultClock(ctx, layoutInflater, resources) return DefaultClockController(ctx, layoutInflater, resources) } override fun getClockThumbnail(id: ClockId): Drawable? { Loading @@ -64,190 +55,3 @@ class DefaultClockProvider @Inject constructor( 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 } } packages/SystemUI/src/com/android/keyguard/ClockEventController.kt +52 −45 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt +22 −11 Original line number Diff line number Diff line Loading @@ -39,19 +39,19 @@ interface ClockProvider { fun getClocks(): List<ClockMetadata> /** 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 */ fun getClockThumbnail(id: ClockId): Drawable? } /** Interface for controlling an active clock */ interface Clock { interface ClockController { /** 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 */ val largeClock: View val largeClock: ClockFaceController /** Events that clocks may need to respond to */ val events: ClockEvents Loading @@ -61,7 +61,7 @@ interface Clock { /** Initializes various rendering parameters. If never called, provides reasonable defaults. */ fun initialize(resources: Resources, dozeFraction: Float, foldFraction: Float) { events.onColorPaletteChanged(resources, true, true) events.onColorPaletteChanged(resources) animations.doze(dozeFraction) animations.fold(foldFraction) events.onTimeTick() Loading @@ -71,10 +71,19 @@ interface Clock { 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 */ interface ClockEvents { /** Call every time tick */ fun onTimeTick() fun onTimeTick() { } /** Call whenever timezone changes */ fun onTimeZoneChanged(timeZone: TimeZone) { } Loading @@ -89,11 +98,7 @@ interface ClockEvents { fun onFontSettingChanged() { } /** Call whenever the color palette should update */ fun onColorPaletteChanged( resources: Resources, smallClockIsDark: Boolean, largeClockIsDark: Boolean ) { } fun onColorPaletteChanged(resources: Resources) { } } /** Methods which trigger various clock animations */ Loading @@ -111,6 +116,12 @@ interface ClockAnimations { 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 */ data class ClockMetadata( val clockId: ClockId, Loading
packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt +15 −5 Original line number Diff line number Diff line Loading @@ -22,7 +22,7 @@ import android.os.UserHandle import android.provider.Settings import android.util.Log 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.ClockMetadata import com.android.systemui.plugins.ClockProvider Loading @@ -33,7 +33,7 @@ import com.google.gson.Gson import javax.inject.Inject private val TAG = ClockRegistry::class.simpleName private val DEBUG = true private const val DEBUG = true /** ClockRegistry aggregates providers and plugins */ open class ClockRegistry( Loading Loading @@ -130,6 +130,10 @@ open class ClockRegistry( } availableClocks[id] = ClockInfo(clock, provider) if (DEBUG) { Log.i(TAG, "Added ${clock.clockId}") } if (currentId == id) { if (DEBUG) { Log.i(TAG, "Current clock ($currentId) was connected") Loading @@ -143,6 +147,9 @@ open class ClockRegistry( val currentId = currentClockId for (clock in provider.getClocks()) { availableClocks.remove(clock.clockId) if (DEBUG) { Log.i(TAG, "Removed ${clock.clockId}") } if (currentId == clock.clockId) { Log.w(TAG, "Current clock ($currentId) was disconnected") Loading @@ -161,7 +168,7 @@ open class ClockRegistry( fun getClockThumbnail(clockId: ClockId): Drawable? = availableClocks[clockId]?.provider?.getClockThumbnail(clockId) fun createExampleClock(clockId: ClockId): Clock? = createClock(clockId) fun createExampleClock(clockId: ClockId): ClockController? = createClock(clockId) fun registerClockChangeListener(listener: ClockChangeListener) = clockChangeListeners.add(listener) Loading @@ -169,11 +176,14 @@ open class ClockRegistry( fun unregisterClockChangeListener(listener: ClockChangeListener) = clockChangeListeners.remove(listener) fun createCurrentClock(): Clock { fun createCurrentClock(): ClockController { val clockId = currentClockId if (isEnabled && clockId.isNotEmpty()) { val clock = createClock(clockId) if (clock != null) { if (DEBUG) { Log.i(TAG, "Rendering clock $clockId") } return clock } else { Log.e(TAG, "Clock $clockId not found; using default") Loading @@ -183,7 +193,7 @@ open class ClockRegistry( return createClock(DEFAULT_CLOCK_ID)!! } private fun createClock(clockId: ClockId): Clock? = private fun createClock(clockId: ClockId): ClockController? = availableClocks[clockId]?.provider?.createClock(clockId) private data class ClockInfo( Loading
packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockController.kt 0 → 100644 +234 −0 Original line number 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 } }
packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt +4 −200 Original line number Diff line number Diff line Loading @@ -15,24 +15,14 @@ package com.android.systemui.shared.clocks import android.content.Context import android.content.res.Resources import android.graphics.Color import android.graphics.drawable.Drawable import android.icu.text.NumberFormat import android.util.TypedValue 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.plugins.Clock import com.android.systemui.plugins.ClockAnimations import com.android.systemui.plugins.ClockEvents import com.android.systemui.plugins.ClockController import com.android.systemui.plugins.ClockId import com.android.systemui.plugins.ClockMetadata import com.android.systemui.plugins.ClockProvider import com.android.systemui.shared.R import java.io.PrintWriter import java.util.Locale import java.util.TimeZone import javax.inject.Inject private val TAG = DefaultClockProvider::class.simpleName Loading @@ -48,11 +38,12 @@ class DefaultClockProvider @Inject constructor( override fun getClocks(): List<ClockMetadata> = 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) { throw IllegalArgumentException("$id is unsupported by $TAG") } return DefaultClock(ctx, layoutInflater, resources) return DefaultClockController(ctx, layoutInflater, resources) } override fun getClockThumbnail(id: ClockId): Drawable? { Loading @@ -64,190 +55,3 @@ class DefaultClockProvider @Inject constructor( 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 } }
packages/SystemUI/src/com/android/keyguard/ClockEventController.kt +52 −45 File changed.Preview size limit exceeded, changes collapsed. Show changes