[DO NOT MERGE] Revert "[DO NOT MERGE]Revert "Migrate KeyguardClockSwitch to new..."

Revert "[DO NOT MERGE]Revert "Expose the clock version of google..."

Revert "[DO NOT MERGE]Revert "Changes to SystemUIGoogle to suppo..."

Revert submission 19508951-revert-19451291-cherrypick-clock_migration-tvip0m638i-BKWSUCNJAV

Reason for revert: Double Revert w/ fixes
Reverted Changes:
Iccd8d2e19:[DO NOT MERGE]Revert "Add clock version of google-...
I432d3d814:[DO NOT MERGE]Revert "Migrate KeyguardClockSwitch ...
Ifd0aa59df:[DO NOT MERGE]Revert "Expose the clock version of ...
I9a6ca6349:[DO NOT MERGE]Revert "Changes to SystemUIGoogle to...

To minimize merge issues, this topic consists of just cherry-picks from
master. Fixes for bugs are provided as downstream commits and will be
merged back to master as those bugs exist there as well. The commits
in this topic weren't tested in isolation beyond automated testing.

Additionally, I've updated the ktfmt exclude list to include new files
from this cherry-pick instead of reformatting those files.

Test: Screenshot test and manually validated each downstream fix mostly by
comparing reference images from known good builds to test builds.

Change-Id: Iff1d0ee8c6d09c77185be1ca5bbbe1aa5870e0c7
diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt
index 3ca8dfe..51cc195 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/packages/SystemUI/ktfmt_includes.txt
@@ -39,6 +39,7 @@
 -packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt
 -packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
 -packages/SystemUI/src/com/android/keyguard/BouncerPanelExpansionCalculator.kt
+-packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
 -packages/SystemUI/src/com/android/keyguard/KeyguardBiometricLockoutLogger.kt
 -packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
 -packages/SystemUI/src/com/android/keyguard/KeyguardListenQueue.kt
@@ -156,6 +157,8 @@
 -packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
 -packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt
 -packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+-packages/SystemUI/src/com/android/systemui/decor/CutoutDecorProviderFactory.kt
+-packages/SystemUI/src/com/android/systemui/decor/CutoutDecorProviderImpl.kt
 -packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt
 -packages/SystemUI/src/com/android/systemui/decor/DecorProviderFactory.kt
 -packages/SystemUI/src/com/android/systemui/decor/FaceScanningProviderFactory.kt
@@ -246,6 +249,7 @@
 -packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
 -packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
 -packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
+-packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipRootView.kt
 -packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt
 -packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderUiEventLogger.kt
 -packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt
@@ -575,6 +579,7 @@
 -packages/SystemUI/src/com/android/systemui/volume/VolumePanelFactory.kt
 -packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
 -packages/SystemUI/tests/src/com/android/keyguard/BouncerPanelExpansionCalculatorTest.kt
+-packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
 -packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt
 -packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
 -packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
@@ -633,6 +638,7 @@
 -packages/SystemUI/tests/src/com/android/systemui/controls/management/TestControlsRequestDialog.kt
 -packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/decor/CutoutDecorProviderFactoryTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt
@@ -720,6 +726,7 @@
 -packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordDialogTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageCaptureImplTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotPolicyImplTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
index 0c191607..cafdc86 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
@@ -13,9 +13,9 @@
  */
 package com.android.systemui.plugins
 
+import android.content.res.Resources
 import android.graphics.drawable.Drawable
 import android.view.View
-import com.android.internal.colorextraction.ColorExtractor
 import com.android.systemui.plugins.annotations.ProvidesInterface
 import java.io.PrintWriter
 import java.util.Locale
@@ -57,7 +57,15 @@
     val events: ClockEvents
 
     /** Triggers for various animations */
-    val animation: ClockAnimation
+    val animations: ClockAnimations
+
+    /** Initializes various rendering parameters. If never called, provides reasonable defaults. */
+    fun initialize(resources: Resources, dozeFraction: Float, foldFraction: Float) {
+        events.onColorPaletteChanged(resources)
+        animations.doze(dozeFraction)
+        animations.fold(foldFraction)
+        events.onTimeTick()
+    }
 
     /** Optional method for dumping debug information */
     fun dump(pw: PrintWriter) { }
@@ -80,15 +88,12 @@
     /** Call whenever font settings change */
     fun onFontSettingChanged() { }
 
-    /** Call whenever the color pallete should update */
-    fun onColorPaletteChanged(palette: ColorExtractor.GradientColors) { }
+    /** Call whenever the color palette should update */
+    fun onColorPaletteChanged(resources: Resources) { }
 }
 
 /** Methods which trigger various clock animations */
-interface ClockAnimation {
-    /** Initializes the doze & fold animation positions. Defaults to neither folded nor dozing. */
-    fun initialize(dozeFraction: Float, foldFraction: Float) { }
-
+interface ClockAnimations {
     /** Runs an enter animation (if any) */
     fun enter() { }
 
diff --git a/packages/SystemUI/res-keyguard/font/clock.xml b/packages/SystemUI/res-keyguard/font/clock.xml
deleted file mode 100644
index 0137dc3..0000000
--- a/packages/SystemUI/res-keyguard/font/clock.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2020, 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.
-*/
--->
-
-<!--
-** AOD/LockScreen Clock font.
-** Should include all numeric glyphs in all supported locales.
-** Recommended: font with variable width to support AOD => LS animations
--->
-<!-- TODO: Remove when clock migration complete -->
-<font-family xmlns:android="http://schemas.android.com/apk/res/android">
-    <font android:typeface="monospace"/>
-</font-family>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index 6a38507..8b8ebf0 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -31,42 +31,14 @@
         android:layout_alignParentStart="true"
         android:layout_alignParentTop="true"
         android:paddingStart="@dimen/clock_padding_start">
-        <com.android.systemui.shared.clocks.AnimatableClockView
-            android:id="@+id/animatable_clock_view"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="start"
-            android:gravity="start"
-            android:textSize="@dimen/clock_text_size"
-            android:fontFamily="@font/clock"
-            android:elegantTextHeight="false"
-            android:singleLine="true"
-            android:fontFeatureSettings="pnum"
-            chargeAnimationDelay="350"
-            dozeWeight="200"
-            lockScreenWeight="400"
-        />
     </FrameLayout>
     <FrameLayout
         android:id="@+id/lockscreen_clock_view_large"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
+        android:layout_height="match_parent"
         android:layout_below="@id/keyguard_slice_view"
+        android:paddingTop="@dimen/keyguard_large_clock_top_padding"
         android:visibility="gone">
-        <com.android.systemui.shared.clocks.AnimatableClockView
-            android:id="@+id/animatable_clock_view_large"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center"
-            android:gravity="center_horizontal"
-            android:textSize="@dimen/large_clock_text_size"
-            android:fontFamily="@font/clock"
-            android:typeface="monospace"
-            android:elegantTextHeight="false"
-            chargeAnimationDelay="200"
-            dozeWeight="200"
-            lockScreenWeight="400"
-        />
     </FrameLayout>
 
     <!-- Not quite optimal but needed to translate these items as a group. The
diff --git a/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml b/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml
index 6ba88a6..acb47f7 100644
--- a/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml
@@ -19,7 +19,7 @@
     android:id="@+id/time_view"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:fontFamily="@font/clock"
+    android:fontFamily="@*android:string/config_clockFontFamily"
     android:textColor="@android:color/white"
     android:format12Hour="@string/dream_time_complication_12_hr_time_format"
     android:format24Hour="@string/dream_time_complication_24_hr_time_format"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 9820237..a775754 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -669,13 +669,7 @@
     <!-- When large clock is showing, offset the smartspace by this amount -->
     <dimen name="keyguard_smartspace_top_offset">12dp</dimen>
     <!-- With the large clock, move up slightly from the center -->
-    <dimen name="keyguard_large_clock_top_margin">-60dp</dimen>
-
-    <!-- TODO: Remove during migration -->
-    <!-- Default line spacing multiplier between hours and minutes of the keyguard clock -->
-    <item name="keyguard_clock_line_spacing_scale" type="dimen" format="float">.7</item>
-    <!-- Burmese line spacing multiplier between hours and minutes of the keyguard clock -->
-    <item name="keyguard_clock_line_spacing_scale_burmese" type="dimen" format="float">1</item>
+    <dimen name="keyguard_large_clock_top_padding">100dp</dimen>
 
     <dimen name="notification_scrim_corner_radius">32dp</dimen>
 
@@ -895,11 +889,6 @@
          burn-in on AOD. -->
     <dimen name="burn_in_prevention_offset_y_clock">42dp</dimen>
 
-    <!-- Clock maximum font size (dp is intentional, to prevent any further scaling) -->
-     <!-- TODO: Remove when clock migration complete -->
-    <dimen name="large_clock_text_size">150dp</dimen>
-    <dimen name="clock_text_size">86dp</dimen>
-
     <!-- The maximum offset in either direction that icons move to prevent burn-in on AOD. -->
     <dimen name="default_burn_in_prevention_offset">15dp</dimen>
 
diff --git a/packages/SystemUI/shared/res/layout/clock_default_large.xml b/packages/SystemUI/shared/res/layout/clock_default_large.xml
index 8510a0a..0139d50 100644
--- a/packages/SystemUI/shared/res/layout/clock_default_large.xml
+++ b/packages/SystemUI/shared/res/layout/clock_default_large.xml
@@ -18,7 +18,6 @@
 -->
 <com.android.systemui.shared.clocks.AnimatableClockView
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/animatable_clock_view_large"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:layout_gravity="center"
diff --git a/packages/SystemUI/shared/res/layout/clock_default_small.xml b/packages/SystemUI/shared/res/layout/clock_default_small.xml
index ec0e427..390ff5e 100644
--- a/packages/SystemUI/shared/res/layout/clock_default_small.xml
+++ b/packages/SystemUI/shared/res/layout/clock_default_small.xml
@@ -18,7 +18,6 @@
 -->
 <com.android.systemui.shared.clocks.AnimatableClockView
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/animatable_clock_view"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:layout_gravity="start"
@@ -26,6 +25,7 @@
     android:textSize="@dimen/small_clock_text_size"
     android:fontFamily="@*android:string/config_clockFontFamily"
     android:elegantTextHeight="false"
+    android:ellipsize="none"
     android:singleLine="true"
     android:fontFeatureSettings="pnum"
     chargeAnimationDelay="350"
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
index c2fda82..580d453 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
@@ -20,12 +20,15 @@
 import android.annotation.FloatRange
 import android.annotation.IntRange
 import android.annotation.SuppressLint
+import android.app.compat.ChangeIdStateCache.invalidate
 import android.content.Context
 import android.graphics.Canvas
 import android.text.TextUtils
 import android.text.format.DateFormat
 import android.util.AttributeSet
 import android.widget.TextView
+import com.android.internal.R.attr.contentDescription
+import com.android.internal.R.attr.format
 import com.android.systemui.animation.GlyphCallback
 import com.android.systemui.animation.Interpolators
 import com.android.systemui.animation.TextAnimator
@@ -82,6 +85,12 @@
     val lockScreenWeight: Int
         get() = if (useBoldedVersion()) lockScreenWeightInternal + 100 else lockScreenWeightInternal
 
+    /**
+     * The number of pixels below the baseline. For fonts that support languages such as
+     * Burmese, this space can be significant and should be accounted for when computing layout.
+     */
+    val bottom get() = paint?.fontMetrics?.bottom ?: 0f
+
     init {
         val animatableClockViewAttributes = context.obtainStyledAttributes(
             attrs, R.styleable.AnimatableClockView, defStyleAttr, defStyleRes
@@ -141,6 +150,15 @@
         if (!TextUtils.equals(text, formattedText)) {
             text = formattedText
             lastTextUpdate = getTimestamp()
+
+            // Because the TextLayout may mutate under the hood as a result of the new text, we
+            // notify the TextAnimator that it may have changed and request a measure/layout. A
+            // crash will occur on the next invocation of setTextStyle if the layout is mutated
+            // without being notified TextInterpolator being notified.
+            if (layout != null) {
+                textAnimator?.updateLayout(layout)
+            }
+            requestLayout()
         }
     }
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index e707d4d..835d6e9 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -35,8 +35,6 @@
 private val TAG = ClockRegistry::class.simpleName
 private val DEBUG = true
 
-typealias ClockChangeListener = () -> Unit
-
 /** ClockRegistry aggregates providers and plugins */
 open class ClockRegistry(
     val context: Context,
@@ -51,6 +49,11 @@
         defaultClockProvider: DefaultClockProvider
     ) : this(context, pluginManager, handler, defaultClockProvider as ClockProvider) { }
 
+    // Usually this would be a typealias, but a SAM provides better java interop
+    fun interface ClockChangeListener {
+        fun onClockChanged()
+    }
+
     var isEnabled: Boolean = false
 
     private val gson = Gson()
@@ -58,7 +61,7 @@
     private val clockChangeListeners = mutableListOf<ClockChangeListener>()
     private val settingObserver = object : ContentObserver(handler) {
         override fun onChange(selfChange: Boolean, uris: Collection<Uri>, flags: Int, userId: Int) =
-            clockChangeListeners.forEach { it() }
+            clockChangeListeners.forEach { it.onClockChanged() }
     }
 
     private val pluginListener = object : PluginListener<ClockProviderPlugin> {
@@ -117,8 +120,11 @@
             val id = clock.clockId
             val current = availableClocks[id]
             if (current != null) {
-                Log.e(TAG, "Clock Id conflict: $id is registered by both " +
-                    "${provider::class.simpleName} and ${current.provider::class.simpleName}")
+                Log.e(
+                    TAG,
+                    "Clock Id conflict: $id is registered by both " +
+                        "${provider::class.simpleName} and ${current.provider::class.simpleName}"
+                )
                 return
             }
 
@@ -127,7 +133,7 @@
                 if (DEBUG) {
                     Log.i(TAG, "Current clock ($currentId) was connected")
                 }
-                clockChangeListeners.forEach { it() }
+                clockChangeListeners.forEach { it.onClockChanged() }
             }
         }
     }
@@ -139,7 +145,7 @@
 
             if (currentId == clock.clockId) {
                 Log.w(TAG, "Current clock ($currentId) was disconnected")
-                clockChangeListeners.forEach { it() }
+                clockChangeListeners.forEach { it.onClockChanged() }
             }
         }
     }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
index 5d8da59..1d8abe3 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
@@ -19,10 +19,9 @@
 import android.icu.text.NumberFormat
 import android.util.TypedValue
 import android.view.LayoutInflater
-import com.android.internal.colorextraction.ColorExtractor
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.Clock
-import com.android.systemui.plugins.ClockAnimation
+import com.android.systemui.plugins.ClockAnimations
 import com.android.systemui.plugins.ClockEvents
 import com.android.systemui.plugins.ClockId
 import com.android.systemui.plugins.ClockMetadata
@@ -102,10 +101,13 @@
                 TypedValue.COMPLEX_UNIT_PX,
                 resources.getDimensionPixelSize(R.dimen.large_clock_text_size).toFloat()
             )
+            recomputePadding()
         }
 
-        override fun onColorPaletteChanged(palette: ColorExtractor.GradientColors) =
-            clocks.forEach { it.setColors(DOZE_COLOR, palette.mainColor) }
+        override fun onColorPaletteChanged(resources: Resources) {
+            val color = resources.getColor(android.R.color.system_accent1_100)
+            clocks.forEach { it.setColors(DOZE_COLOR, color) }
+        }
 
         override fun onLocaleChanged(locale: Locale) {
             val nf = NumberFormat.getInstance(locale)
@@ -119,8 +121,17 @@
         }
     }
 
-    override val animation = object : ClockAnimation {
-        override fun initialize(dozeFraction: Float, foldFraction: Float) {
+    override var animations = DefaultClockAnimations(0f, 0f)
+        private set
+
+    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)
 
@@ -132,14 +143,13 @@
         }
 
         override fun enter() {
-            if (dozeState.isActive) {
+            if (!dozeState.isActive) {
                 clocks.forEach { it.animateAppearOnLockscreen() }
             }
         }
 
         override fun charge() = clocks.forEach { it.animateCharge { dozeState.isActive } }
 
-        private var foldState = AnimationState(0f)
         override fun fold(fraction: Float) {
             val (hasChanged, hasJumped) = foldState.update(fraction)
             if (hasChanged) {
@@ -147,7 +157,6 @@
             }
         }
 
-        private var dozeState = AnimationState(0f)
         override fun doze(fraction: Float) {
             val (hasChanged, hasJumped) = dozeState.update(fraction)
             if (hasChanged) {
@@ -172,6 +181,19 @@
 
     init {
         events.onLocaleChanged(Locale.getDefault())
+        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)
+        events.onTimeTick()
+    }
+
+    private fun recomputePadding() {
+        val topPadding = -1 * (largeClock.bottom.toInt() - 180)
+        largeClock.setPadding(0, topPadding, 0, 0)
     }
 
     override fun dump(pw: PrintWriter) = clocks.forEach { it.dump(pw) }
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
index 3c68c0d..dd69f33 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
@@ -182,18 +182,6 @@
         mStatusBarStateController.removeCallback(mStatusBarStateListener);
     }
 
-    /**
-     * @return the number of pixels below the baseline. For fonts that support languages such as
-     * Burmese, this space can be significant.
-     */
-    public float getBottom() {
-        if (mView.getPaint() != null && mView.getPaint().getFontMetrics() != null) {
-            return mView.getPaint().getFontMetrics().bottom;
-        }
-
-        return 0f;
-    }
-
     /** Animate the clock appearance */
     public void animateAppear() {
         if (!mIsDozing) mView.animateAppearOnLockscreen();
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
new file mode 100644
index 0000000..efd7bcf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -0,0 +1,153 @@
+/*
+ * 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.keyguard
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.content.res.Resources
+import android.text.format.DateFormat
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.plugins.Clock
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.policy.BatteryController
+import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback
+import com.android.systemui.statusbar.policy.ConfigurationController
+import java.io.PrintWriter
+import java.util.Locale
+import java.util.TimeZone
+import javax.inject.Inject
+
+/**
+ * Controller for a Clock provided by the registry and used on the keyguard. Instantiated by
+ * [KeyguardClockSwitchController]. Functionality is forked from [AnimatableClockController].
+ */
+class ClockEventController @Inject constructor(
+    private val statusBarStateController: StatusBarStateController,
+    private val broadcastDispatcher: BroadcastDispatcher,
+    private val batteryController: BatteryController,
+    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+    private val configurationController: ConfigurationController,
+    @Main private val resources: Resources,
+    private val context: Context
+) {
+    var clock: Clock? = null
+        set(value) {
+            field = value
+            if (value != null) {
+                value.initialize(resources, dozeAmount, 0f)
+            }
+        }
+
+    private var isDozing = false
+        private set
+
+    private var isCharging = false
+    private var dozeAmount = 0f
+    private var isKeyguardShowing = false
+
+    private val configListener = object : ConfigurationController.ConfigurationListener {
+        override fun onThemeChanged() {
+            clock?.events?.onColorPaletteChanged(resources)
+        }
+    }
+
+    private val batteryCallback = object : BatteryStateChangeCallback {
+        override fun onBatteryLevelChanged(level: Int, pluggedIn: Boolean, charging: Boolean) {
+            if (isKeyguardShowing && !isCharging && charging) {
+                clock?.animations?.charge()
+            }
+            isCharging = charging
+        }
+    }
+
+    private val localeBroadcastReceiver = object : BroadcastReceiver() {
+        override fun onReceive(context: Context, intent: Intent) {
+            clock?.events?.onLocaleChanged(Locale.getDefault())
+        }
+    }
+
+    private val statusBarStateListener = object : StatusBarStateController.StateListener {
+        override fun onDozeAmountChanged(linear: Float, eased: Float) {
+            clock?.animations?.doze(linear)
+
+            isDozing = linear > dozeAmount
+            dozeAmount = linear
+        }
+    }
+
+    private val keyguardUpdateMonitorCallback = object : KeyguardUpdateMonitorCallback() {
+        override fun onKeyguardVisibilityChanged(showing: Boolean) {
+            isKeyguardShowing = showing
+            if (!isKeyguardShowing) {
+                clock?.animations?.doze(if (isDozing) 1f else 0f)
+            }
+        }
+
+        override fun onTimeFormatChanged(timeFormat: String) {
+            clock?.events?.onTimeFormatChanged(DateFormat.is24HourFormat(context))
+        }
+
+        override fun onTimeZoneChanged(timeZone: TimeZone) {
+            clock?.events?.onTimeZoneChanged(timeZone)
+        }
+
+        override fun onUserSwitchComplete(userId: Int) {
+            clock?.events?.onTimeFormatChanged(DateFormat.is24HourFormat(context))
+        }
+    }
+
+    init {
+        isDozing = statusBarStateController.isDozing
+    }
+
+    fun registerListeners() {
+        dozeAmount = statusBarStateController.dozeAmount
+        isDozing = statusBarStateController.isDozing || dozeAmount != 0f
+
+        broadcastDispatcher.registerReceiver(
+            localeBroadcastReceiver,
+            IntentFilter(Intent.ACTION_LOCALE_CHANGED)
+        )
+        configurationController.addCallback(configListener)
+        batteryController.addCallback(batteryCallback)
+        keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
+        statusBarStateController.addCallback(statusBarStateListener)
+    }
+
+    fun unregisterListeners() {
+        broadcastDispatcher.unregisterReceiver(localeBroadcastReceiver)
+        configurationController.removeCallback(configListener)
+        batteryController.removeCallback(batteryCallback)
+        keyguardUpdateMonitor.removeCallback(keyguardUpdateMonitorCallback)
+        statusBarStateController.removeCallback(statusBarStateListener)
+    }
+
+    /**
+     * Dump information for debugging
+     */
+    fun dump(pw: PrintWriter) {
+        pw.println(this)
+        clock?.dump(pw)
+    }
+
+    companion object {
+        private val TAG = ClockEventController::class.simpleName
+        private const val FORMAT_NUMBER = 1234567890
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 9164108..10d6d9f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -5,10 +5,8 @@
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.content.Context;
-import android.graphics.Paint;
-import android.graphics.Paint.Style;
 import android.util.AttributeSet;
-import android.util.TypedValue;
+import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
@@ -17,19 +15,14 @@
 import androidx.annotation.IntDef;
 import androidx.annotation.VisibleForTesting;
 
-import com.android.internal.colorextraction.ColorExtractor;
 import com.android.keyguard.dagger.KeyguardStatusViewScope;
 import com.android.systemui.R;
 import com.android.systemui.animation.Interpolators;
-import com.android.systemui.plugins.ClockPlugin;
-import com.android.systemui.shared.clocks.AnimatableClockView;
+import com.android.systemui.plugins.Clock;
 
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.Arrays;
-import java.util.TimeZone;
-
 /**
  * Switch to show plugin clock when plugin is connected, otherwise it will show default clock.
  */
@@ -50,17 +43,10 @@
     public static final int SMALL = 1;
 
     /**
-     * Optional/alternative clock injected via plugin.
-     */
-    private ClockPlugin mClockPlugin;
-
-    /**
      * Frame for small/large clocks
      */
-    private FrameLayout mClockFrame;
+    private FrameLayout mSmallClockFrame;
     private FrameLayout mLargeClockFrame;
-    private AnimatableClockView mClockView;
-    private AnimatableClockView mLargeClockView;
 
     private View mStatusArea;
     private int mSmartspaceTopOffset;
@@ -80,12 +66,6 @@
     @VisibleForTesting AnimatorSet mClockOutAnim = null;
     private ObjectAnimator mStatusAreaAnim = null;
 
-    /**
-     * If the Keyguard Slice has a header (big center-aligned text.)
-     */
-    private boolean mSupportsDarkText;
-    private int[] mColorPalette;
-
     private int mClockSwitchYAmount;
     @VisibleForTesting boolean mChildrenAreLaidOut = false;
     @VisibleForTesting boolean mAnimateOnLayout = true;
@@ -98,97 +78,38 @@
      * Apply dp changes on font/scale change
      */
     public void onDensityOrFontScaleChanged() {
-        mLargeClockView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mContext.getResources()
-                .getDimensionPixelSize(R.dimen.large_clock_text_size));
-        mClockView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mContext.getResources()
-                .getDimensionPixelSize(R.dimen.clock_text_size));
-
         mClockSwitchYAmount = mContext.getResources().getDimensionPixelSize(
                 R.dimen.keyguard_clock_switch_y_shift);
-
         mSmartspaceTopOffset = mContext.getResources().getDimensionPixelSize(
                 R.dimen.keyguard_smartspace_top_offset);
     }
 
-    /**
-     * Returns if this view is presenting a custom clock, or the default implementation.
-     */
-    public boolean hasCustomClock() {
-        return mClockPlugin != null;
-    }
-
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
 
-        mClockFrame = findViewById(R.id.lockscreen_clock_view);
-        mClockView = findViewById(R.id.animatable_clock_view);
+        mSmallClockFrame = findViewById(R.id.lockscreen_clock_view);
         mLargeClockFrame = findViewById(R.id.lockscreen_clock_view_large);
-        mLargeClockView = findViewById(R.id.animatable_clock_view_large);
         mStatusArea = findViewById(R.id.keyguard_status_area);
 
         onDensityOrFontScaleChanged();
     }
 
-    void setClockPlugin(ClockPlugin plugin, int statusBarState) {
+    void setClock(Clock clock, int statusBarState) {
         // Disconnect from existing plugin.
-        if (mClockPlugin != null) {
-            View smallClockView = mClockPlugin.getView();
-            if (smallClockView != null && smallClockView.getParent() == mClockFrame) {
-                mClockFrame.removeView(smallClockView);
-            }
-            View bigClockView = mClockPlugin.getBigClockView();
-            if (bigClockView != null && bigClockView.getParent() == mLargeClockFrame) {
-                mLargeClockFrame.removeView(bigClockView);
-            }
-            mClockPlugin.onDestroyView();
-            mClockPlugin = null;
-        }
-        if (plugin == null) {
-            mClockView.setVisibility(View.VISIBLE);
-            mLargeClockView.setVisibility(View.VISIBLE);
+        mSmallClockFrame.removeAllViews();
+        mLargeClockFrame.removeAllViews();
+
+        if (clock == null) {
+            Log.e(TAG, "No clock being shown");
             return;
         }
+
         // Attach small and big clock views to hierarchy.
-        View smallClockView = plugin.getView();
-        if (smallClockView != null) {
-            mClockFrame.addView(smallClockView, -1,
-                    new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
-                            ViewGroup.LayoutParams.WRAP_CONTENT));
-            mClockView.setVisibility(View.GONE);
-        }
-        View bigClockView = plugin.getBigClockView();
-        if (bigClockView != null) {
-            mLargeClockFrame.addView(bigClockView);
-            mLargeClockView.setVisibility(View.GONE);
-        }
-
-        // Initialize plugin parameters.
-        mClockPlugin = plugin;
-        mClockPlugin.setStyle(getPaint().getStyle());
-        mClockPlugin.setTextColor(getCurrentTextColor());
-        mClockPlugin.setDarkAmount(mDarkAmount);
-        if (mColorPalette != null) {
-            mClockPlugin.setColorPalette(mSupportsDarkText, mColorPalette);
-        }
-    }
-
-    /**
-     * It will also update plugin setStyle if plugin is connected.
-     */
-    public void setStyle(Style style) {
-        if (mClockPlugin != null) {
-            mClockPlugin.setStyle(style);
-        }
-    }
-
-    /**
-     * It will also update plugin setTextColor if plugin is connected.
-     */
-    public void setTextColor(int color) {
-        if (mClockPlugin != null) {
-            mClockPlugin.setTextColor(color);
-        }
+        mSmallClockFrame.addView(clock.getSmallClock(), -1,
+                new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                        ViewGroup.LayoutParams.WRAP_CONTENT));
+        mLargeClockFrame.addView(clock.getLargeClock());
     }
 
     private void updateClockViews(boolean useLargeClock, boolean animate) {
@@ -204,14 +125,14 @@
         int direction = 1;
         float statusAreaYTranslation;
         if (useLargeClock) {
-            out = mClockFrame;
+            out = mSmallClockFrame;
             in = mLargeClockFrame;
             if (indexOfChild(in) == -1) addView(in);
             direction = -1;
-            statusAreaYTranslation = mClockFrame.getTop() - mStatusArea.getTop()
+            statusAreaYTranslation = mSmallClockFrame.getTop() - mStatusArea.getTop()
                     + mSmartspaceTopOffset;
         } else {
-            in = mClockFrame;
+            in = mSmallClockFrame;
             out = mLargeClockFrame;
             statusAreaYTranslation = 0f;
 
@@ -270,18 +191,6 @@
     }
 
     /**
-     * Set the amount (ratio) that the device has transitioned to doze.
-     *
-     * @param darkAmount Amount of transition to doze: 1f for doze and 0f for awake.
-     */
-    public void setDarkAmount(float darkAmount) {
-        mDarkAmount = darkAmount;
-        if (mClockPlugin != null) {
-            mClockPlugin.setDarkAmount(darkAmount);
-        }
-    }
-
-    /**
      * Display the desired clock and hide the other one
      *
      * @return true if desired clock appeared and false if it was already visible
@@ -312,64 +221,11 @@
         mChildrenAreLaidOut = true;
     }
 
-    public Paint getPaint() {
-        return mClockView.getPaint();
-    }
-
-    public int getCurrentTextColor() {
-        return mClockView.getCurrentTextColor();
-    }
-
-    public float getTextSize() {
-        return mClockView.getTextSize();
-    }
-
-    /**
-     * Refresh the time of the clock, due to either time tick broadcast or doze time tick alarm.
-     */
-    public void refresh() {
-        if (mClockPlugin != null) {
-            mClockPlugin.onTimeTick();
-        }
-    }
-
-    /**
-     * Notifies that the time zone has changed.
-     */
-    public void onTimeZoneChanged(TimeZone timeZone) {
-        if (mClockPlugin != null) {
-            mClockPlugin.onTimeZoneChanged(timeZone);
-        }
-    }
-
-    /**
-     * Notifies that the time format has changed.
-     *
-     * @param timeFormat "12" for 12-hour format, "24" for 24-hour format
-     */
-    public void onTimeFormatChanged(String timeFormat) {
-        if (mClockPlugin != null) {
-            mClockPlugin.onTimeFormatChanged(timeFormat);
-        }
-    }
-
-    void updateColors(ColorExtractor.GradientColors colors) {
-        mSupportsDarkText = colors.supportsDarkText();
-        mColorPalette = colors.getColorPalette();
-        if (mClockPlugin != null) {
-            mClockPlugin.setColorPalette(mSupportsDarkText, mColorPalette);
-        }
-    }
-
     public void dump(PrintWriter pw, String[] args) {
         pw.println("KeyguardClockSwitch:");
-        pw.println("  mClockPlugin: " + mClockPlugin);
-        pw.println("  mClockFrame: " + mClockFrame);
+        pw.println("  mClockFrame: " + mSmallClockFrame);
         pw.println("  mLargeClockFrame: " + mLargeClockFrame);
         pw.println("  mStatusArea: " + mStatusArea);
-        pw.println("  mDarkAmount: " + mDarkAmount);
-        pw.println("  mSupportsDarkText: " + mSupportsDarkText);
-        pw.println("  mColorPalette: " + Arrays.toString(mColorPalette));
         pw.println("  mDisplayedClockSize: " + mDisplayedClockSize);
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index ad06e05..dd78f1c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -22,8 +22,6 @@
 import static com.android.keyguard.KeyguardClockSwitch.LARGE;
 import static com.android.keyguard.KeyguardClockSwitch.SMALL;
 
-import android.app.WallpaperManager;
-import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -32,36 +30,30 @@
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 import android.widget.LinearLayout;
-import android.widget.RelativeLayout;
 
 import androidx.annotation.NonNull;
 
-import com.android.internal.colorextraction.ColorExtractor;
-import com.android.keyguard.clock.ClockManager;
 import com.android.systemui.Dumpable;
 import com.android.systemui.R;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
-import com.android.systemui.plugins.ClockPlugin;
+import com.android.systemui.plugins.Clock;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shared.clocks.ClockRegistry;
 import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
 import com.android.systemui.statusbar.notification.AnimatableProperty;
 import com.android.systemui.statusbar.notification.PropertyAnimator;
 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
 import com.android.systemui.statusbar.phone.NotificationIconContainer;
-import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.util.ViewController;
 import com.android.systemui.util.settings.SecureSettings;
 
 import java.io.PrintWriter;
 import java.util.Locale;
-import java.util.TimeZone;
 import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
@@ -71,48 +63,24 @@
  */
 public class KeyguardClockSwitchController extends ViewController<KeyguardClockSwitch>
         implements Dumpable {
-    private static final boolean CUSTOM_CLOCKS_ENABLED = true;
-
     private final StatusBarStateController mStatusBarStateController;
-    private final SysuiColorExtractor mColorExtractor;
-    private final ClockManager mClockManager;
+    private final ClockRegistry mClockRegistry;
     private final KeyguardSliceViewController mKeyguardSliceViewController;
     private final NotificationIconAreaController mNotificationIconAreaController;
-    private final BroadcastDispatcher mBroadcastDispatcher;
-    private final BatteryController mBatteryController;
     private final LockscreenSmartspaceController mSmartspaceController;
-    private final Resources mResources;
     private final SecureSettings mSecureSettings;
     private final DumpManager mDumpManager;
+    private final ClockEventController mClockEventController;
 
-    /**
-     * Clock for both small and large sizes
-     */
-    private AnimatableClockController mClockViewController;
-    private FrameLayout mClockFrame; // top aligned clock
-    private AnimatableClockController mLargeClockViewController;
+    /** Clock frames for both small and large sizes */
+    private FrameLayout mSmallClockFrame; // top aligned clock
     private FrameLayout mLargeClockFrame; // centered clock
 
     @KeyguardClockSwitch.ClockSize
     private int mCurrentClockSize = SMALL;
 
-    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-
     private int mKeyguardClockTopMargin = 0;
-
-    /**
-     * Listener for changes to the color palette.
-     *
-     * The color palette changes when the wallpaper is changed.
-     */
-    private final ColorExtractor.OnColorsChangedListener mColorsListener =
-            (extractor, which) -> {
-                if ((which & WallpaperManager.FLAG_LOCK) != 0) {
-                    mView.updateColors(getGradientColors());
-                }
-            };
-
-    private final ClockManager.ClockChangedListener mClockChangedListener = this::setClockPlugin;
+    private final ClockRegistry.ClockChangeListener mClockChangedListener;
 
     private ViewGroup mStatusArea;
     // If set will replace keyguard_slice_view
@@ -121,9 +89,9 @@
     private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
 
     private boolean mOnlyClock = false;
-    private Executor mUiExecutor;
+    private final Executor mUiExecutor;
     private boolean mCanShowDoubleLineClock = true;
-    private ContentObserver mDoubleLineClockObserver = new ContentObserver(null) {
+    private final ContentObserver mDoubleLineClockObserver = new ContentObserver(null) {
         @Override
         public void onChange(boolean change) {
             updateDoubleLineClock();
@@ -144,34 +112,32 @@
     public KeyguardClockSwitchController(
             KeyguardClockSwitch keyguardClockSwitch,
             StatusBarStateController statusBarStateController,
-            SysuiColorExtractor colorExtractor,
-            ClockManager clockManager,
+            ClockRegistry clockRegistry,
             KeyguardSliceViewController keyguardSliceViewController,
             NotificationIconAreaController notificationIconAreaController,
-            BroadcastDispatcher broadcastDispatcher,
-            BatteryController batteryController,
-            KeyguardUpdateMonitor keyguardUpdateMonitor,
             LockscreenSmartspaceController smartspaceController,
             KeyguardUnlockAnimationController keyguardUnlockAnimationController,
             SecureSettings secureSettings,
             @Main Executor uiExecutor,
-            @Main Resources resources,
-            DumpManager dumpManager) {
+            DumpManager dumpManager,
+            ClockEventController clockEventController,
+            FeatureFlags featureFlags) {
         super(keyguardClockSwitch);
         mStatusBarStateController = statusBarStateController;
-        mColorExtractor = colorExtractor;
-        mClockManager = clockManager;
+        mClockRegistry = clockRegistry;
         mKeyguardSliceViewController = keyguardSliceViewController;
         mNotificationIconAreaController = notificationIconAreaController;
-        mBroadcastDispatcher = broadcastDispatcher;
-        mBatteryController = batteryController;
-        mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mSmartspaceController = smartspaceController;
-        mResources = resources;
         mSecureSettings = secureSettings;
         mUiExecutor = uiExecutor;
         mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
         mDumpManager = dumpManager;
+        mClockEventController = clockEventController;
+
+        mClockRegistry.setEnabled(featureFlags.isEnabled(Flags.LOCKSCREEN_CUSTOM_CLOCKS));
+        mClockChangedListener = () -> {
+            setClock(mClockRegistry.createCurrentClock());
+        };
     }
 
     /**
@@ -188,40 +154,18 @@
     public void onInit() {
         mKeyguardSliceViewController.init();
 
-        mClockFrame = mView.findViewById(R.id.lockscreen_clock_view);
+        mSmallClockFrame = mView.findViewById(R.id.lockscreen_clock_view);
         mLargeClockFrame = mView.findViewById(R.id.lockscreen_clock_view_large);
 
-        mClockViewController =
-                new AnimatableClockController(
-                        mView.findViewById(R.id.animatable_clock_view),
-                        mStatusBarStateController,
-                        mBroadcastDispatcher,
-                        mBatteryController,
-                        mKeyguardUpdateMonitor,
-                        mResources);
-        mClockViewController.init();
-
-        mLargeClockViewController =
-                new AnimatableClockController(
-                        mView.findViewById(R.id.animatable_clock_view_large),
-                        mStatusBarStateController,
-                        mBroadcastDispatcher,
-                        mBatteryController,
-                        mKeyguardUpdateMonitor,
-                        mResources);
-        mLargeClockViewController.init();
-
         mDumpManager.unregisterDumpable(getClass().toString()); // unregister previous clocks
         mDumpManager.registerDumpable(getClass().toString(), this);
     }
 
     @Override
     protected void onViewAttached() {
-        if (CUSTOM_CLOCKS_ENABLED) {
-            mClockManager.addOnClockChangedListener(mClockChangedListener);
-        }
-        mColorExtractor.addOnColorsChangedListener(mColorsListener);
-        mView.updateColors(getGradientColors());
+        mClockRegistry.registerClockChangeListener(mClockChangedListener);
+        setClock(mClockRegistry.createCurrentClock());
+        mClockEventController.registerListeners();
         mKeyguardClockTopMargin =
                 mView.getResources().getDimensionPixelSize(R.dimen.keyguard_clock_top_margin);
 
@@ -244,7 +188,6 @@
             ksv.setVisibility(View.GONE);
 
             addSmartspaceView(ksvIndex);
-            updateClockLayout();
         }
 
         mSecureSettings.registerContentObserverForUser(
@@ -266,11 +209,9 @@
 
     @Override
     protected void onViewDetached() {
-        if (CUSTOM_CLOCKS_ENABLED) {
-            mClockManager.removeOnClockChangedListener(mClockChangedListener);
-        }
-        mColorExtractor.removeOnColorsChangedListener(mColorsListener);
-        mView.setClockPlugin(null, mStatusBarStateController.getState());
+        mClockRegistry.unregisterClockChangeListener(mClockChangedListener);
+        mClockEventController.unregisterListeners();
+        setClock(null);
 
         mSecureSettings.unregisterContentObserver(mDoubleLineClockObserver);
 
@@ -309,18 +250,6 @@
         mView.onDensityOrFontScaleChanged();
         mKeyguardClockTopMargin =
                 mView.getResources().getDimensionPixelSize(R.dimen.keyguard_clock_top_margin);
-
-        updateClockLayout();
-    }
-
-    private void updateClockLayout() {
-        int largeClockTopMargin = getContext().getResources().getDimensionPixelSize(
-                R.dimen.keyguard_large_clock_top_margin)
-                - (int) mLargeClockViewController.getBottom();
-        RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(MATCH_PARENT,
-                MATCH_PARENT);
-        lp.topMargin = largeClockTopMargin;
-        mLargeClockFrame.setLayoutParams(lp);
     }
 
     /**
@@ -334,46 +263,34 @@
 
         mCurrentClockSize = clockSize;
 
+        Clock clock = getClock();
         boolean appeared = mView.switchToClock(clockSize, animate);
-        if (animate && appeared && clockSize == LARGE) {
-            mLargeClockViewController.animateAppear();
-        }
-    }
-
-    public void animateFoldToAod() {
-        if (mClockViewController != null) {
-            mClockViewController.animateFoldAppear();
-            mLargeClockViewController.animateFoldAppear();
+        if (clock != null && animate && appeared && clockSize == LARGE) {
+            clock.getAnimations().enter();
         }
     }
 
     /**
-     * If we're presenting a custom clock of just the default one.
+     * Animates the clock view between folded and unfolded states
      */
-    public boolean hasCustomClock() {
-        return mView.hasCustomClock();
-    }
-
-    /**
-     * Get the clock text size.
-     */
-    public float getClockTextSize() {
-        return mView.getTextSize();
+    public void animateFoldToAod(float foldFraction) {
+        Clock clock = getClock();
+        if (clock != null) {
+            clock.getAnimations().fold(foldFraction);
+        }
     }
 
     /**
      * Refresh clock. Called in response to TIME_TICK broadcasts.
      */
     void refresh() {
-        if (mClockViewController != null) {
-            mClockViewController.refreshTime();
-            mLargeClockViewController.refreshTime();
-        }
         if (mSmartspaceController != null) {
             mSmartspaceController.requestSmartspaceUpdate();
         }
-
-        mView.refresh();
+        Clock clock = getClock();
+        if (clock != null) {
+            clock.getEvents().onTimeTick();
+        }
     }
 
     /**
@@ -385,7 +302,7 @@
     void updatePosition(int x, float scale, AnimationProperties props, boolean animate) {
         x = getCurrentLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? -x : x;
 
-        PropertyAnimator.setProperty(mClockFrame, AnimatableProperty.TRANSLATION_X,
+        PropertyAnimator.setProperty(mSmallClockFrame, AnimatableProperty.TRANSLATION_X,
                 x, props, animate);
         PropertyAnimator.setProperty(mLargeClockFrame, AnimatableProperty.SCALE_X,
                 scale, props, animate);
@@ -398,25 +315,39 @@
         }
     }
 
-    void updateTimeZone(TimeZone timeZone) {
-        mView.onTimeZoneChanged(timeZone);
-    }
-
     /**
      * Get y-bottom position of the currently visible clock on the keyguard.
      * We can't directly getBottom() because clock changes positions in AOD for burn-in
      */
     int getClockBottom(int statusBarHeaderHeight) {
+        Clock clock = getClock();
+        if (clock == null) {
+            return 0;
+        }
+
         if (mLargeClockFrame.getVisibility() == View.VISIBLE) {
-            View clock = mLargeClockFrame.findViewById(
-                    com.android.systemui.R.id.animatable_clock_view_large);
             int frameHeight = mLargeClockFrame.getHeight();
-            int clockHeight = clock.getHeight();
+            int clockHeight = clock.getLargeClock().getHeight();
             return frameHeight / 2 + clockHeight / 2;
         } else {
-            return mClockFrame.findViewById(
-                    com.android.systemui.R.id.animatable_clock_view).getHeight()
-                    + statusBarHeaderHeight + mKeyguardClockTopMargin;
+            int clockHeight = clock.getSmallClock().getHeight();
+            return clockHeight + statusBarHeaderHeight + mKeyguardClockTopMargin;
+        }
+    }
+
+    /**
+     * Get the height of the currently visible clock on the keyguard.
+     */
+    int getClockHeight() {
+        Clock clock = getClock();
+        if (clock == null) {
+            return 0;
+        }
+
+        if (mLargeClockFrame.getVisibility() == View.VISIBLE) {
+            return clock.getLargeClock().getHeight();
+        } else {
+            return clock.getSmallClock().getHeight();
         }
     }
 
@@ -431,12 +362,13 @@
         mNotificationIconAreaController.setupAodIcons(nic);
     }
 
-    private void setClockPlugin(ClockPlugin plugin) {
-        mView.setClockPlugin(plugin, mStatusBarStateController.getState());
+    private void setClock(Clock clock) {
+        mClockEventController.setClock(clock);
+        mView.setClock(clock, mStatusBarStateController.getState());
     }
 
-    private ColorExtractor.GradientColors getGradientColors() {
-        return mColorExtractor.getColors(WallpaperManager.FLAG_LOCK);
+    private Clock getClock() {
+        return mClockEventController.getClock();
     }
 
     private int getCurrentLayoutDirection() {
@@ -469,8 +401,10 @@
     public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
         pw.println("currentClockSizeLarge=" + (mCurrentClockSize == LARGE));
         pw.println("mCanShowDoubleLineClock=" + mCanShowDoubleLineClock);
-        mClockViewController.dump(pw);
-        mLargeClockViewController.dump(pw);
+        Clock clock = getClock();
+        if (clock != null) {
+            clock.dump(pw);
+        }
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index cb3172d..83e23bd 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -17,14 +17,11 @@
 package com.android.keyguard;
 
 import android.content.Context;
-import android.graphics.Color;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.GridLayout;
 
-import androidx.core.graphics.ColorUtils;
-
 import com.android.systemui.R;
 import com.android.systemui.statusbar.CrossFadeHelper;
 
@@ -46,7 +43,6 @@
     private View mMediaHostContainer;
 
     private float mDarkAmount = 0;
-    private int mTextColor;
 
     public KeyguardStatusView(Context context) {
         this(context, null, 0);
@@ -71,7 +67,6 @@
         }
 
         mKeyguardSlice = findViewById(R.id.keyguard_slice_view);
-        mTextColor = mClockView.getCurrentTextColor();
 
         mMediaHostContainer = findViewById(R.id.status_view_media_container);
 
@@ -83,15 +78,12 @@
             return;
         }
         mDarkAmount = darkAmount;
-        mClockView.setDarkAmount(darkAmount);
         CrossFadeHelper.fadeOut(mMediaHostContainer, darkAmount);
         updateDark();
     }
 
     void updateDark() {
-        final int blendedTextColor = ColorUtils.blendARGB(mTextColor, Color.WHITE, mDarkAmount);
         mKeyguardSlice.setDarkAmount(mDarkAmount);
-        mClockView.setTextColor(blendedTextColor);
     }
 
     /** Sets a translationY value on every child view except for the media view. */
@@ -113,7 +105,6 @@
     public void dump(PrintWriter pw, String[] args) {
         pw.println("KeyguardStatusView:");
         pw.println("  mDarkAmount: " + mDarkAmount);
-        pw.println("  mTextColor: " + Integer.toHexString(mTextColor));
         if (mClockView != null) {
             mClockView.dump(pw, args);
         }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 014d082..c715a4e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -30,8 +30,6 @@
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.ViewController;
 
-import java.util.TimeZone;
-
 import javax.inject.Inject;
 
 /**
@@ -96,13 +94,6 @@
     }
 
     /**
-     * The amount we're in doze.
-     */
-    public void setDarkAmount(float darkAmount) {
-        mView.setDarkAmount(darkAmount);
-    }
-
-    /**
      * Set which clock should be displayed on the keyguard. The other one will be automatically
      * hidden.
      */
@@ -114,16 +105,10 @@
      * Performs fold to aod animation of the clocks (changes font weight from bold to thin).
      * This animation is played when AOD is enabled and foldable device is fully folded, it is
      * displayed on the outer screen
+     * @param foldFraction current fraction of fold animation complete
      */
-    public void animateFoldToAod() {
-        mKeyguardClockSwitchController.animateFoldToAod();
-    }
-
-    /**
-     * If we're presenting a custom clock of just the default one.
-     */
-    public boolean hasCustomClock() {
-        return mKeyguardClockSwitchController.hasCustomClock();
+    public void animateFoldToAod(float foldFraction) {
+        mKeyguardClockSwitchController.animateFoldToAod(foldFraction);
     }
 
     /**
@@ -143,24 +128,11 @@
     }
 
     /**
-     * Set pivot x.
+     * Update the pivot position based on the parent view
      */
-    public void setPivotX(float pivot) {
-        mView.setPivotX(pivot);
-    }
-
-    /**
-     * Set pivot y.
-     */
-    public void setPivotY(float pivot) {
-        mView.setPivotY(pivot);
-    }
-
-    /**
-     * Get the clock text size.
-     */
-    public float getClockTextSize() {
-        return mKeyguardClockSwitchController.getClockTextSize();
+    public void updatePivot(float parentWidth, float parentHeight) {
+        mView.setPivotX(parentWidth / 2f);
+        mView.setPivotY(mKeyguardClockSwitchController.getClockHeight() / 2f);
     }
 
     /**
@@ -240,11 +212,6 @@
         }
 
         @Override
-        public void onTimeZoneChanged(TimeZone timeZone) {
-            mKeyguardClockSwitchController.updateTimeZone(timeZone);
-        }
-
-        @Override
         public void onKeyguardVisibilityChanged(boolean showing) {
             if (showing) {
                 if (DEBUG) Slog.v(TAG, "refresh statusview showing:" + showing);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index efdeba5..9a8395c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -327,11 +327,6 @@
     private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
     private final NotificationIconAreaController mNotificationIconAreaController;
 
-    // Cap and total height of Roboto font. Needs to be adjusted when font for the big clock is
-    // changed.
-    private static final int CAP_HEIGHT = 1456;
-    private static final int FONT_HEIGHT = 2163;
-
     /**
      * Maximum time before which we will expand the panel even for slow motions when getting a
      * touch passed over from launcher.
@@ -1261,13 +1256,6 @@
         }
     }
 
-    /**
-     * Returns if there's a custom clock being presented.
-     */
-    public boolean hasCustomClock() {
-        return mKeyguardStatusViewController.hasCustomClock();
-    }
-
     private void setCentralSurfaces(CentralSurfaces centralSurfaces) {
         // TODO: this can be injected.
         mCentralSurfaces = centralSurfaces;
@@ -4287,9 +4275,9 @@
                 public void onAnimationEnd(Animator animation) {
                     endAction.run();
                 }
+            }).setUpdateListener(anim -> {
+                mKeyguardStatusViewController.animateFoldToAod(anim.getAnimatedFraction());
             }).start();
-
-        mKeyguardStatusViewController.animateFoldToAod();
     }
 
     /**
@@ -5814,7 +5802,6 @@
         public void onDozeAmountChanged(float linearAmount, float amount) {
             mInterpolatedDarkAmount = amount;
             mLinearDarkAmount = linearAmount;
-            mKeyguardStatusViewController.setDarkAmount(mInterpolatedDarkAmount);
             mKeyguardBottomArea.setDarkAmount(mInterpolatedDarkAmount);
             positionClockAndNotifications();
         }
@@ -5930,11 +5917,8 @@
             updateMaxDisplayedNotifications(!shouldAvoidChangingNotificationsCount());
             setIsFullWidth(mNotificationStackScrollLayoutController.getWidth() == mView.getWidth());
 
-            // Update Clock Pivot
-            mKeyguardStatusViewController.setPivotX(((float) mView.getWidth()) / 2f);
-            mKeyguardStatusViewController.setPivotY(
-                    (FONT_HEIGHT - CAP_HEIGHT) / 2048f
-                            * mKeyguardStatusViewController.getClockTextSize());
+            // Update Clock Pivot (used by anti-burnin transformations)
+            mKeyguardStatusViewController.updatePivot(mView.getWidth(), mView.getHeight());
 
             // Calculate quick setting heights.
             int oldMaxHeight = mQsMaxExpansionHeight;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index 84bb82b..ce04fb5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -442,7 +442,6 @@
             state |= DISABLE_CLOCK;
         }
 
-
         if (mNetworkController != null && EncryptionHelper.IS_DATA_ENCRYPTED) {
             if (mNetworkController.hasEmergencyCryptKeeperText()) {
                 state |= DISABLE_NOTIFICATION_ICONS;
@@ -452,13 +451,6 @@
             }
         }
 
-        // The shelf will be hidden when dozing with a custom clock, we must show notification
-        // icons in this occasion.
-        if (mStatusBarStateController.isDozing()
-                && mNotificationPanelViewController.hasCustomClock()) {
-            state |= DISABLE_CLOCK | DISABLE_SYSTEM_INFO;
-        }
-
         if (mOngoingCallController.hasOngoingCall()) {
             state &= ~DISABLE_ONGOING_CALL_CHIP;
         } else {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
new file mode 100644
index 0000000..4f39952
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -0,0 +1,259 @@
+/*
+ * 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.keyguard
+
+import android.content.BroadcastReceiver
+import android.testing.AndroidTestingRunner
+import android.widget.TextView
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.plugins.Clock
+import com.android.systemui.plugins.ClockAnimations
+import com.android.systemui.plugins.ClockEvents
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.policy.BatteryController
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
+import java.util.TimeZone
+import org.junit.Assert.assertEquals
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyFloat
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.junit.MockitoJUnit
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class ClockEventControllerTest : SysuiTestCase() {
+
+    @JvmField @Rule val mockito = MockitoJUnit.rule()
+    @Mock private lateinit var statusBarStateController: StatusBarStateController
+    @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
+    @Mock private lateinit var batteryController: BatteryController
+    @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+    @Mock private lateinit var configurationController: ConfigurationController
+    @Mock private lateinit var animations: ClockAnimations
+    @Mock private lateinit var events: ClockEvents
+    @Mock private lateinit var clock: Clock
+
+    private lateinit var clockEventController: ClockEventController
+
+    @Before
+    fun setUp() {
+        whenever(clock.smallClock).thenReturn(TextView(context))
+        whenever(clock.largeClock).thenReturn(TextView(context))
+        whenever(clock.events).thenReturn(events)
+        whenever(clock.animations).thenReturn(animations)
+
+        clockEventController = ClockEventController(
+            statusBarStateController,
+            broadcastDispatcher,
+            batteryController,
+            keyguardUpdateMonitor,
+            configurationController,
+            context.resources,
+            context
+        )
+    }
+
+    @Test
+    fun clockSet_validateInitialization() {
+        clockEventController.clock = clock
+
+        verify(clock).initialize(any(), anyFloat(), anyFloat())
+    }
+
+    @Test
+    fun clockUnset_validateState() {
+        clockEventController.clock = clock
+        clockEventController.clock = null
+
+        assertEquals(clockEventController.clock, null)
+    }
+
+    @Test
+    fun themeChanged_verifyClockPaletteUpdated() {
+        clockEventController.clock = clock
+        clockEventController.registerListeners()
+
+        val captor = argumentCaptor<ConfigurationController.ConfigurationListener>()
+        verify(configurationController).addCallback(capture(captor))
+        captor.value.onThemeChanged()
+
+        verify(events).onColorPaletteChanged(any())
+    }
+
+    @Test
+    fun batteryCallback_keyguardShowingCharging_verifyChargeAnimation() {
+        clockEventController.clock = clock
+        clockEventController.registerListeners()
+
+        val batteryCaptor = argumentCaptor<BatteryController.BatteryStateChangeCallback>()
+        verify(batteryController).addCallback(capture(batteryCaptor))
+        val keyguardCaptor = argumentCaptor<KeyguardUpdateMonitorCallback>()
+        verify(keyguardUpdateMonitor).registerCallback(capture(keyguardCaptor))
+        keyguardCaptor.value.onKeyguardVisibilityChanged(true)
+        batteryCaptor.value.onBatteryLevelChanged(10, false, true)
+
+        verify(animations).charge()
+    }
+
+    @Test
+    fun batteryCallback_keyguardShowingCharging_Duplicate_verifyChargeAnimation() {
+        clockEventController.clock = clock
+        clockEventController.registerListeners()
+
+        val batteryCaptor = argumentCaptor<BatteryController.BatteryStateChangeCallback>()
+        verify(batteryController).addCallback(capture(batteryCaptor))
+        val keyguardCaptor = argumentCaptor<KeyguardUpdateMonitorCallback>()
+        verify(keyguardUpdateMonitor).registerCallback(capture(keyguardCaptor))
+        keyguardCaptor.value.onKeyguardVisibilityChanged(true)
+        batteryCaptor.value.onBatteryLevelChanged(10, false, true)
+        batteryCaptor.value.onBatteryLevelChanged(10, false, true)
+
+        verify(animations, times(1)).charge()
+    }
+
+    @Test
+    fun batteryCallback_keyguardHiddenCharging_verifyChargeAnimation() {
+        clockEventController.clock = clock
+        clockEventController.registerListeners()
+
+        val batteryCaptor = argumentCaptor<BatteryController.BatteryStateChangeCallback>()
+        verify(batteryController).addCallback(capture(batteryCaptor))
+        val keyguardCaptor = argumentCaptor<KeyguardUpdateMonitorCallback>()
+        verify(keyguardUpdateMonitor).registerCallback(capture(keyguardCaptor))
+        keyguardCaptor.value.onKeyguardVisibilityChanged(false)
+        batteryCaptor.value.onBatteryLevelChanged(10, false, true)
+
+        verify(animations, never()).charge()
+    }
+
+    @Test
+    fun batteryCallback_keyguardShowingNotCharging_verifyChargeAnimation() {
+        clockEventController.clock = clock
+        clockEventController.registerListeners()
+
+        val batteryCaptor = argumentCaptor<BatteryController.BatteryStateChangeCallback>()
+        verify(batteryController).addCallback(capture(batteryCaptor))
+        val keyguardCaptor = argumentCaptor<KeyguardUpdateMonitorCallback>()
+        verify(keyguardUpdateMonitor).registerCallback(capture(keyguardCaptor))
+        keyguardCaptor.value.onKeyguardVisibilityChanged(true)
+        batteryCaptor.value.onBatteryLevelChanged(10, false, false)
+
+        verify(animations, never()).charge()
+    }
+
+    @Test
+    fun localeCallback_verifyClockNotified() {
+        clockEventController.clock = clock
+        clockEventController.registerListeners()
+
+        val captor = argumentCaptor<BroadcastReceiver>()
+        verify(broadcastDispatcher).registerReceiver(
+            capture(captor), any(), eq(null), eq(null), anyInt(), eq(null)
+        )
+        captor.value.onReceive(context, mock())
+
+        verify(events).onLocaleChanged(any())
+    }
+
+    @Test
+    fun keyguardCallback_visibilityChanged_clockDozeCalled() {
+        clockEventController.clock = clock
+        clockEventController.registerListeners()
+
+        val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
+        verify(keyguardUpdateMonitor).registerCallback(capture(captor))
+
+        captor.value.onKeyguardVisibilityChanged(true)
+        verify(animations, never()).doze(0f)
+
+        captor.value.onKeyguardVisibilityChanged(false)
+        verify(animations, times(1)).doze(0f)
+    }
+
+    @Test
+    fun keyguardCallback_timeFormat_clockNotified() {
+        clockEventController.clock = clock
+        clockEventController.registerListeners()
+
+        val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
+        verify(keyguardUpdateMonitor).registerCallback(capture(captor))
+        captor.value.onTimeFormatChanged("12h")
+
+        verify(events).onTimeFormatChanged(false)
+    }
+
+    @Test
+    fun keyguardCallback_timezoneChanged_clockNotified() {
+        val mockTimeZone = mock<TimeZone>()
+        clockEventController.clock = clock
+        clockEventController.registerListeners()
+
+        val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
+        verify(keyguardUpdateMonitor).registerCallback(capture(captor))
+        captor.value.onTimeZoneChanged(mockTimeZone)
+
+        verify(events).onTimeZoneChanged(mockTimeZone)
+    }
+
+    @Test
+    fun keyguardCallback_userSwitched_clockNotified() {
+        clockEventController.clock = clock
+        clockEventController.registerListeners()
+
+        val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
+        verify(keyguardUpdateMonitor).registerCallback(capture(captor))
+        captor.value.onUserSwitchComplete(10)
+
+        verify(events).onTimeFormatChanged(false)
+    }
+
+    @Test
+    fun keyguardCallback_verifyKeyguardChanged() {
+        clockEventController.clock = clock
+        clockEventController.registerListeners()
+
+        val captor = argumentCaptor<StatusBarStateController.StateListener>()
+        verify(statusBarStateController).addCallback(capture(captor))
+        captor.value.onDozeAmountChanged(0.4f, 0.6f)
+
+        verify(animations).doze(0.4f)
+    }
+
+    @Test
+    fun unregisterListeners_validate() {
+        clockEventController.unregisterListeners()
+        verify(broadcastDispatcher).unregisterReceiver(any())
+        verify(configurationController).removeCallback(any())
+        verify(batteryController).removeCallback(any())
+        verify(keyguardUpdateMonitor).removeCallback(any())
+        verify(statusBarStateController).removeCallback(any())
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index b2d9219..635ee9e 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -19,7 +19,6 @@
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -41,24 +40,19 @@
 
 import androidx.test.filters.SmallTest;
 
-import com.android.internal.colorextraction.ColorExtractor;
-import com.android.keyguard.clock.ClockManager;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
-import com.android.systemui.plugins.ClockPlugin;
+import com.android.systemui.plugins.Clock;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.shared.clocks.AnimatableClockView;
+import com.android.systemui.shared.clocks.ClockRegistry;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
 import com.android.systemui.statusbar.phone.NotificationIconContainer;
-import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.settings.SecureSettings;
 import com.android.systemui.util.time.FakeSystemClock;
@@ -80,22 +74,12 @@
     @Mock
     private StatusBarStateController mStatusBarStateController;
     @Mock
-    private SysuiColorExtractor mColorExtractor;
-    @Mock
-    private ClockManager mClockManager;
+    private ClockRegistry mClockRegistry;
     @Mock
     KeyguardSliceViewController mKeyguardSliceViewController;
     @Mock
     NotificationIconAreaController mNotificationIconAreaController;
     @Mock
-    BroadcastDispatcher mBroadcastDispatcher;
-    @Mock
-    BatteryController mBatteryController;
-    @Mock
-    KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-    @Mock
-    KeyguardBypassController mBypassController;
-    @Mock
     LockscreenSmartspaceController mSmartspaceController;
 
     @Mock
@@ -103,11 +87,11 @@
     @Mock
     KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
     @Mock
-    private ClockPlugin mClockPlugin;
-    @Mock
-    ColorExtractor.GradientColors mGradientColors;
+    private Clock mClock;
     @Mock
     DumpManager mDumpManager;
+    @Mock
+    ClockEventController mClockEventController;
 
     @Mock
     private NotificationIconContainer mNotificationIcons;
@@ -139,8 +123,6 @@
         when(mView.getContext()).thenReturn(getContext());
         when(mView.getResources()).thenReturn(mResources);
 
-        when(mView.findViewById(R.id.animatable_clock_view)).thenReturn(mClockView);
-        when(mView.findViewById(R.id.animatable_clock_view_large)).thenReturn(mLargeClockView);
         when(mView.findViewById(R.id.lockscreen_clock_view_large)).thenReturn(mLargeClockFrame);
         when(mClockView.getContext()).thenReturn(getContext());
         when(mLargeClockView.getContext()).thenReturn(getContext());
@@ -151,23 +133,20 @@
         mController = new KeyguardClockSwitchController(
                 mView,
                 mStatusBarStateController,
-                mColorExtractor,
-                mClockManager,
+                mClockRegistry,
                 mKeyguardSliceViewController,
                 mNotificationIconAreaController,
-                mBroadcastDispatcher,
-                mBatteryController,
-                mKeyguardUpdateMonitor,
                 mSmartspaceController,
                 mKeyguardUnlockAnimationController,
                 mSecureSettings,
                 mExecutor,
-                mResources,
-                mDumpManager
+                mDumpManager,
+                mClockEventController,
+                mFeatureFlags
         );
 
         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
-        when(mColorExtractor.getColors(anyInt())).thenReturn(mGradientColors);
+        when(mClockRegistry.createCurrentClock()).thenReturn(mClock);
 
         mSliceView = new View(getContext());
         when(mView.findViewById(R.id.keyguard_slice_view)).thenReturn(mSliceView);
@@ -214,20 +193,20 @@
         verifyAttachment(times(1));
 
         listenerArgumentCaptor.getValue().onViewDetachedFromWindow(mView);
-        verify(mColorExtractor).removeOnColorsChangedListener(
-                any(ColorExtractor.OnColorsChangedListener.class));
+        verify(mClockEventController).unregisterListeners();
     }
 
     @Test
     public void testPluginPassesStatusBarState() {
-        ArgumentCaptor<ClockManager.ClockChangedListener> listenerArgumentCaptor =
-                ArgumentCaptor.forClass(ClockManager.ClockChangedListener.class);
+        ArgumentCaptor<ClockRegistry.ClockChangeListener> listenerArgumentCaptor =
+                ArgumentCaptor.forClass(ClockRegistry.ClockChangeListener.class);
 
         mController.init();
-        verify(mClockManager).addOnClockChangedListener(listenerArgumentCaptor.capture());
+        verify(mClockRegistry).registerClockChangeListener(listenerArgumentCaptor.capture());
 
-        listenerArgumentCaptor.getValue().onClockChanged(mClockPlugin);
-        verify(mView).setClockPlugin(mClockPlugin, StatusBarState.SHADE);
+        listenerArgumentCaptor.getValue().onClockChanged();
+        verify(mView, times(2)).setClock(mClock, StatusBarState.SHADE);
+        verify(mClockEventController, times(2)).setClock(mClock);
     }
 
     @Test
@@ -284,10 +263,8 @@
     }
 
     private void verifyAttachment(VerificationMode times) {
-        verify(mClockManager, times).addOnClockChangedListener(
-                any(ClockManager.ClockChangedListener.class));
-        verify(mColorExtractor, times).addOnColorsChangedListener(
-                any(ColorExtractor.OnColorsChangedListener.class));
-        verify(mView, times).updateColors(mGradientColors);
+        verify(mClockRegistry, times).registerClockChangeListener(
+                any(ClockRegistry.ClockChangeListener.class));
+        verify(mClockEventController, times).registerListeners();
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index 6c6f0ac..a0295d0 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -16,7 +16,6 @@
 
 package com.android.keyguard;
 
-import static android.view.View.GONE;
 import static android.view.View.VISIBLE;
 
 import static com.android.keyguard.KeyguardClockSwitch.LARGE;
@@ -24,56 +23,61 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static junit.framework.TestCase.assertEquals;
+
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
-import android.graphics.Color;
-import android.graphics.Paint.Style;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.FrameLayout;
-import android.widget.TextClock;
+import android.widget.TextView;
 
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.ClockPlugin;
-import com.android.systemui.shared.clocks.AnimatableClockView;
+import com.android.systemui.plugins.Clock;
 import com.android.systemui.statusbar.StatusBarState;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 // Need to run on the main thread because KeyguardSliceView$Row init checks for
 // the main thread before acquiring a wake lock. This class is constructed when
-// the keyguard_clcok_switch layout is inflated.
+// the keyguard_clock_switch layout is inflated.
 @RunWithLooper(setAsMainLooper = true)
 public class KeyguardClockSwitchTest extends SysuiTestCase {
-    private FrameLayout mClockFrame;
-    private FrameLayout mLargeClockFrame;
-    private TextClock mBigClock;
+    @Mock
+    ViewGroup mMockKeyguardSliceView;
 
-    private AnimatableClockView mClockView;
-    private AnimatableClockView mLargeClockView;
-    View mMockKeyguardSliceView;
+    @Mock
+    Clock mClock;
+
+    private FrameLayout mSmallClockFrame;
+    private FrameLayout mLargeClockFrame;
+
     KeyguardClockSwitch mKeyguardClockSwitch;
 
     @Before
     public void setUp() {
-        mMockKeyguardSliceView = mock(KeyguardSliceView.class);
+        MockitoAnnotations.initMocks(this);
         when(mMockKeyguardSliceView.getContext()).thenReturn(mContext);
         when(mMockKeyguardSliceView.findViewById(R.id.keyguard_status_area))
                 .thenReturn(mMockKeyguardSliceView);
 
+        when(mClock.getSmallClock()).thenReturn(new TextView(getContext()));
+        when(mClock.getLargeClock()).thenReturn(new TextView(getContext()));
+
         LayoutInflater layoutInflater = LayoutInflater.from(getContext());
         layoutInflater.setPrivateFactory(new LayoutInflater.Factory2() {
 
@@ -93,164 +97,68 @@
         });
         mKeyguardClockSwitch =
                 (KeyguardClockSwitch) layoutInflater.inflate(R.layout.keyguard_clock_switch, null);
-        mClockFrame = mKeyguardClockSwitch.findViewById(R.id.lockscreen_clock_view);
-        mClockView = mKeyguardClockSwitch.findViewById(R.id.animatable_clock_view);
+        mSmallClockFrame = mKeyguardClockSwitch.findViewById(R.id.lockscreen_clock_view);
         mLargeClockFrame = mKeyguardClockSwitch.findViewById(R.id.lockscreen_clock_view_large);
-        mLargeClockView = mKeyguardClockSwitch.findViewById(R.id.animatable_clock_view_large);
-        mBigClock = new TextClock(getContext());
         mKeyguardClockSwitch.mChildrenAreLaidOut = true;
-        MockitoAnnotations.initMocks(this);
     }
 
     @Test
-    public void onPluginConnected_showPluginClock() {
-        ClockPlugin plugin = mock(ClockPlugin.class);
-        TextClock pluginView = new TextClock(getContext());
-        when(plugin.getView()).thenReturn(pluginView);
-
-        mKeyguardClockSwitch.setClockPlugin(plugin, StatusBarState.KEYGUARD);
-
-        assertThat(mClockView.getVisibility()).isEqualTo(GONE);
-        assertThat(plugin.getView().getParent()).isEqualTo(mClockFrame);
+    public void noPluginConnected_showNothing() {
+        mKeyguardClockSwitch.setClock(null, StatusBarState.KEYGUARD);
+        assertEquals(mLargeClockFrame.getChildCount(), 0);
+        assertEquals(mSmallClockFrame.getChildCount(), 0);
     }
 
     @Test
-    public void onPluginConnected_showPluginBigClock() {
-        // GIVEN the plugin returns a view for the big clock
-        ClockPlugin plugin = mock(ClockPlugin.class);
-        when(plugin.getBigClockView()).thenReturn(mBigClock);
-        // WHEN the plugin is connected
-        mKeyguardClockSwitch.setClockPlugin(plugin, StatusBarState.KEYGUARD);
-        // THEN the big clock container is visible and it is the parent of the
-        // big clock view.
-        assertThat(mLargeClockView.getVisibility()).isEqualTo(View.GONE);
-        assertThat(mBigClock.getParent()).isEqualTo(mLargeClockFrame);
+    public void pluginConnectedThenDisconnected_showNothing() {
+        mKeyguardClockSwitch.setClock(mClock, StatusBarState.KEYGUARD);
+        assertEquals(mLargeClockFrame.getChildCount(), 1);
+        assertEquals(mSmallClockFrame.getChildCount(), 1);
+
+        mKeyguardClockSwitch.setClock(null, StatusBarState.KEYGUARD);
+        assertEquals(mLargeClockFrame.getChildCount(), 0);
+        assertEquals(mSmallClockFrame.getChildCount(), 0);
     }
 
     @Test
-    public void onPluginConnected_nullView() {
-        ClockPlugin plugin = mock(ClockPlugin.class);
-        mKeyguardClockSwitch.setClockPlugin(plugin, StatusBarState.KEYGUARD);
-        assertThat(mClockView.getVisibility()).isEqualTo(VISIBLE);
+    public void onPluginConnected_showClock() {
+        mKeyguardClockSwitch.setClock(mClock, StatusBarState.KEYGUARD);
+
+        assertEquals(mClock.getSmallClock().getParent(), mSmallClockFrame);
+        assertEquals(mClock.getLargeClock().getParent(), mLargeClockFrame);
     }
 
     @Test
     public void onPluginConnected_showSecondPluginClock() {
         // GIVEN a plugin has already connected
-        ClockPlugin plugin1 = mock(ClockPlugin.class);
-        when(plugin1.getView()).thenReturn(new TextClock(getContext()));
-        mKeyguardClockSwitch.setClockPlugin(plugin1, StatusBarState.KEYGUARD);
-        // WHEN a second plugin is connected
-        ClockPlugin plugin2 = mock(ClockPlugin.class);
-        when(plugin2.getView()).thenReturn(new TextClock(getContext()));
-        mKeyguardClockSwitch.setClockPlugin(plugin2, StatusBarState.KEYGUARD);
+        Clock otherClock = mock(Clock.class);
+        when(otherClock.getSmallClock()).thenReturn(new TextView(getContext()));
+        when(otherClock.getLargeClock()).thenReturn(new TextView(getContext()));
+        mKeyguardClockSwitch.setClock(mClock, StatusBarState.KEYGUARD);
+        mKeyguardClockSwitch.setClock(otherClock, StatusBarState.KEYGUARD);
+
         // THEN only the view from the second plugin should be a child of KeyguardClockSwitch.
-        assertThat(plugin2.getView().getParent()).isEqualTo(mClockFrame);
-        assertThat(plugin1.getView().getParent()).isNull();
-    }
-
-    @Test
-    public void onPluginConnected_darkAmountInitialized() {
-        // GIVEN that the dark amount has already been set
-        mKeyguardClockSwitch.setDarkAmount(0.5f);
-        // WHEN a plugin is connected
-        ClockPlugin plugin = mock(ClockPlugin.class);
-        mKeyguardClockSwitch.setClockPlugin(plugin, StatusBarState.KEYGUARD);
-        // THEN dark amount should be initalized on the plugin.
-        verify(plugin).setDarkAmount(0.5f);
-    }
-
-    @Test
-    public void onPluginDisconnected_showDefaultClock() {
-        ClockPlugin plugin = mock(ClockPlugin.class);
-        TextClock pluginView = new TextClock(getContext());
-        when(plugin.getView()).thenReturn(pluginView);
-
-        mKeyguardClockSwitch.setClockPlugin(plugin, StatusBarState.KEYGUARD);
-        assertThat(mClockView.getVisibility()).isEqualTo(GONE);
-
-        mKeyguardClockSwitch.setClockPlugin(null, StatusBarState.KEYGUARD);
-        assertThat(mClockView.getVisibility()).isEqualTo(VISIBLE);
-
-        assertThat(plugin.getView().getParent()).isNull();
-    }
-
-    @Test
-    public void onPluginDisconnected_hidePluginBigClock() {
-        // GIVEN the plugin returns a view for the big clock
-        ClockPlugin plugin = mock(ClockPlugin.class);
-        TextClock pluginView = new TextClock(getContext());
-        when(plugin.getBigClockView()).thenReturn(pluginView);
-        // WHEN the plugin is connected and then disconnected
-        mKeyguardClockSwitch.setClockPlugin(plugin, StatusBarState.KEYGUARD);
-        mKeyguardClockSwitch.setClockPlugin(null, StatusBarState.KEYGUARD);
-        // THEN the big lock container is GONE and the big clock view doesn't have
-        // a parent.
-        assertThat(mLargeClockView.getVisibility()).isEqualTo(VISIBLE);
-        assertThat(pluginView.getParent()).isNull();
-    }
-
-    @Test
-    public void onPluginDisconnected_nullView() {
-        ClockPlugin plugin = mock(ClockPlugin.class);
-        mKeyguardClockSwitch.setClockPlugin(plugin, StatusBarState.KEYGUARD);
-        mKeyguardClockSwitch.setClockPlugin(null, StatusBarState.KEYGUARD);
-        assertThat(mClockView.getVisibility()).isEqualTo(VISIBLE);
+        assertThat(otherClock.getSmallClock().getParent()).isEqualTo(mSmallClockFrame);
+        assertThat(otherClock.getLargeClock().getParent()).isEqualTo(mLargeClockFrame);
+        assertThat(mClock.getSmallClock().getParent()).isNull();
+        assertThat(mClock.getLargeClock().getParent()).isNull();
     }
 
     @Test
     public void onPluginDisconnected_secondOfTwoDisconnected() {
         // GIVEN two plugins are connected
-        ClockPlugin plugin1 = mock(ClockPlugin.class);
-        when(plugin1.getView()).thenReturn(new TextClock(getContext()));
-        mKeyguardClockSwitch.setClockPlugin(plugin1, StatusBarState.KEYGUARD);
-        ClockPlugin plugin2 = mock(ClockPlugin.class);
-        when(plugin2.getView()).thenReturn(new TextClock(getContext()));
-        mKeyguardClockSwitch.setClockPlugin(plugin2, StatusBarState.KEYGUARD);
+        Clock otherClock = mock(Clock.class);
+        when(otherClock.getSmallClock()).thenReturn(new TextView(getContext()));
+        when(otherClock.getLargeClock()).thenReturn(new TextView(getContext()));
+        mKeyguardClockSwitch.setClock(otherClock, StatusBarState.KEYGUARD);
+        mKeyguardClockSwitch.setClock(mClock, StatusBarState.KEYGUARD);
         // WHEN the second plugin is disconnected
-        mKeyguardClockSwitch.setClockPlugin(null, StatusBarState.KEYGUARD);
-        // THEN the default clock should be shown.
-        assertThat(mClockView.getVisibility()).isEqualTo(VISIBLE);
-        assertThat(plugin1.getView().getParent()).isNull();
-        assertThat(plugin2.getView().getParent()).isNull();
-    }
-
-    @Test
-    public void onPluginDisconnected_onDestroyView() {
-        // GIVEN a plugin is connected
-        ClockPlugin clockPlugin = mock(ClockPlugin.class);
-        when(clockPlugin.getView()).thenReturn(new TextClock(getContext()));
-        mKeyguardClockSwitch.setClockPlugin(clockPlugin, StatusBarState.KEYGUARD);
-        // WHEN the plugin is disconnected
-        mKeyguardClockSwitch.setClockPlugin(null, StatusBarState.KEYGUARD);
-        // THEN onDestroyView is called on the plugin
-        verify(clockPlugin).onDestroyView();
-    }
-
-    @Test
-    public void setTextColor_pluginClockSetTextColor() {
-        ClockPlugin plugin = mock(ClockPlugin.class);
-        TextClock pluginView = new TextClock(getContext());
-        when(plugin.getView()).thenReturn(pluginView);
-        mKeyguardClockSwitch.setClockPlugin(plugin, StatusBarState.KEYGUARD);
-
-        mKeyguardClockSwitch.setTextColor(Color.WHITE);
-
-        verify(plugin).setTextColor(Color.WHITE);
-    }
-
-
-    @Test
-    public void setStyle_pluginClockSetStyle() {
-        ClockPlugin plugin = mock(ClockPlugin.class);
-        TextClock pluginView = new TextClock(getContext());
-        when(plugin.getView()).thenReturn(pluginView);
-        Style style = mock(Style.class);
-        mKeyguardClockSwitch.setClockPlugin(plugin, StatusBarState.KEYGUARD);
-
-        mKeyguardClockSwitch.setStyle(style);
-
-        verify(plugin).setStyle(style);
+        mKeyguardClockSwitch.setClock(null, StatusBarState.KEYGUARD);
+        // THEN nothing should be shown
+        assertThat(otherClock.getSmallClock().getParent()).isNull();
+        assertThat(otherClock.getLargeClock().getParent()).isNull();
+        assertThat(mClock.getSmallClock().getParent()).isNull();
+        assertThat(mClock.getLargeClock().getParent()).isNull();
     }
 
     @Test
@@ -262,7 +170,7 @@
 
         assertThat(mLargeClockFrame.getAlpha()).isEqualTo(1);
         assertThat(mLargeClockFrame.getVisibility()).isEqualTo(VISIBLE);
-        assertThat(mClockFrame.getAlpha()).isEqualTo(0);
+        assertThat(mSmallClockFrame.getAlpha()).isEqualTo(0);
     }
 
     @Test
@@ -271,7 +179,7 @@
 
         assertThat(mLargeClockFrame.getAlpha()).isEqualTo(1);
         assertThat(mLargeClockFrame.getVisibility()).isEqualTo(VISIBLE);
-        assertThat(mClockFrame.getAlpha()).isEqualTo(0);
+        assertThat(mSmallClockFrame.getAlpha()).isEqualTo(0);
     }
 
     @Test
@@ -281,8 +189,8 @@
         mKeyguardClockSwitch.mClockInAnim.end();
         mKeyguardClockSwitch.mClockOutAnim.end();
 
-        assertThat(mClockFrame.getAlpha()).isEqualTo(1);
-        assertThat(mClockFrame.getVisibility()).isEqualTo(VISIBLE);
+        assertThat(mSmallClockFrame.getAlpha()).isEqualTo(1);
+        assertThat(mSmallClockFrame.getVisibility()).isEqualTo(VISIBLE);
         // only big clock is removed at switch
         assertThat(mLargeClockFrame.getParent()).isNull();
         assertThat(mLargeClockFrame.getAlpha()).isEqualTo(0);
@@ -292,8 +200,8 @@
     public void switchingToSmallClockNoAnimation_makesBigClockDisappear() {
         mKeyguardClockSwitch.switchToClock(SMALL, false);
 
-        assertThat(mClockFrame.getAlpha()).isEqualTo(1);
-        assertThat(mClockFrame.getVisibility()).isEqualTo(VISIBLE);
+        assertThat(mSmallClockFrame.getAlpha()).isEqualTo(1);
+        assertThat(mSmallClockFrame.getVisibility()).isEqualTo(VISIBLE);
         // only big clock is removed at switch
         assertThat(mLargeClockFrame.getParent()).isNull();
         assertThat(mLargeClockFrame.getAlpha()).isEqualTo(0);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
index 1cbb8d0..131eac6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
@@ -130,13 +130,16 @@
         pluginListener.onPluginConnected(plugin1, mockContext)
         pluginListener.onPluginConnected(plugin2, mockContext)
         val list = registry.getClocks()
-        assertEquals(list, listOf(
-            ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME),
-            ClockMetadata("clock_1", "clock 1"),
-            ClockMetadata("clock_2", "clock 2"),
-            ClockMetadata("clock_3", "clock 3"),
-            ClockMetadata("clock_4", "clock 4")
-        ))
+        assertEquals(
+            list,
+            listOf(
+                ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME),
+                ClockMetadata("clock_1", "clock 1"),
+                ClockMetadata("clock_2", "clock 2"),
+                ClockMetadata("clock_3", "clock 3"),
+                ClockMetadata("clock_4", "clock 4")
+            )
+        )
     }
 
     @Test
@@ -158,11 +161,14 @@
         pluginListener.onPluginConnected(plugin1, mockContext)
         pluginListener.onPluginConnected(plugin2, mockContext)
         val list = registry.getClocks()
-        assertEquals(list, listOf(
-            ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME),
-            ClockMetadata("clock_1", "clock 1"),
-            ClockMetadata("clock_2", "clock 2")
-        ))
+        assertEquals(
+            list,
+            listOf(
+                ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME),
+                ClockMetadata("clock_1", "clock 1"),
+                ClockMetadata("clock_2", "clock 2")
+            )
+        )
 
         assertEquals(registry.createExampleClock("clock_1"), mockClock)
         assertEquals(registry.createExampleClock("clock_2"), mockClock)
@@ -222,7 +228,7 @@
         pluginListener.onPluginConnected(plugin2, mockContext)
 
         var changeCallCount = 0
-        registry.registerClockChangeListener({ changeCallCount++ })
+        registry.registerClockChangeListener { changeCallCount++ }
 
         pluginListener.onPluginDisconnected(plugin1)
         assertEquals(0, changeCallCount)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index 20bf50e..37c8f62 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -333,10 +333,9 @@
     }
 
     @Test
-    public void disable_isDozingButNoCustomClock_clockAndSystemInfoVisible() {
+    public void disable_isDozing_clockAndSystemInfoVisible() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
         when(mStatusBarStateController.isDozing()).thenReturn(true);
-        when(mNotificationPanelViewController.hasCustomClock()).thenReturn(false);
 
         fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
 
@@ -345,10 +344,9 @@
     }
 
     @Test
-    public void disable_customClockButNotDozing_clockAndSystemInfoVisible() {
+    public void disable_NotDozing_clockAndSystemInfoVisible() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
         when(mStatusBarStateController.isDozing()).thenReturn(false);
-        when(mNotificationPanelViewController.hasCustomClock()).thenReturn(true);
 
         fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
 
@@ -357,40 +355,6 @@
     }
 
     @Test
-    public void disable_dozingAndCustomClock_clockAndSystemInfoHidden() {
-        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
-        when(mStatusBarStateController.isDozing()).thenReturn(true);
-        when(mNotificationPanelViewController.hasCustomClock()).thenReturn(true);
-
-        // Make sure they start out as visible
-        assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
-        assertEquals(View.VISIBLE, getClockView().getVisibility());
-
-        fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
-
-        assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
-        assertEquals(View.GONE, getClockView().getVisibility());
-    }
-
-    @Test
-    public void onDozingChanged_clockAndSystemInfoVisibilitiesUpdated() {
-        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
-        when(mStatusBarStateController.isDozing()).thenReturn(true);
-        when(mNotificationPanelViewController.hasCustomClock()).thenReturn(true);
-
-        // Make sure they start out as visible
-        assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
-        assertEquals(View.VISIBLE, getClockView().getVisibility());
-
-        fragment.onDozingChanged(true);
-
-        // When this callback is triggered, we want to make sure the clock and system info
-        // visibilities are recalculated. Since dozing=true, they shouldn't be visible.
-        assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
-        assertEquals(View.GONE, getClockView().getVisibility());
-    }
-
-    @Test
     public void disable_headsUpShouldBeVisibleTrue_clockDisabled() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
         when(mHeadsUpAppearanceController.shouldBeVisible()).thenReturn(true);