Merge "Additional clock interface method for region target and font sizes" into tm-qpr-dev
diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt
index 9f211c9..553b86b 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/packages/SystemUI/ktfmt_includes.txt
@@ -16,7 +16,6 @@
 -packages/SystemUI/checks/tests/com/android/systemui/lint/RegisterReceiverViaContextDetectorTest.kt
 -packages/SystemUI/checks/tests/com/android/systemui/lint/SoftwareBitmapDetectorTest.kt
 -packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
--packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
 -packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSContainerController.kt
 -packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
 -packages/SystemUI/shared/src/com/android/systemui/flags/FlagListenable.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 89f5c2c..66e44b9 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
@@ -70,10 +70,10 @@
     }
 
     /** Optional method for dumping debug information */
-    fun dump(pw: PrintWriter) { }
+    fun dump(pw: PrintWriter) {}
 
     /** Optional method for debug logging */
-    fun setLogBuffer(logBuffer: LogBuffer) { }
+    fun setLogBuffer(logBuffer: LogBuffer) {}
 }
 
 /** Interface for a specific clock face version rendered by the clock */
@@ -88,40 +88,37 @@
 /** Events that should call when various rendering parameters change */
 interface ClockEvents {
     /** Call every time tick */
-    fun onTimeTick() { }
+    fun onTimeTick() {}
 
     /** Call whenever timezone changes */
-    fun onTimeZoneChanged(timeZone: TimeZone) { }
+    fun onTimeZoneChanged(timeZone: TimeZone) {}
 
     /** Call whenever the text time format changes (12hr vs 24hr) */
-    fun onTimeFormatChanged(is24Hr: Boolean) { }
+    fun onTimeFormatChanged(is24Hr: Boolean) {}
 
     /** Call whenever the locale changes */
-    fun onLocaleChanged(locale: Locale) { }
-
-    /** Call whenever font settings change */
-    fun onFontSettingChanged() { }
+    fun onLocaleChanged(locale: Locale) {}
 
     /** Call whenever the color palette should update */
-    fun onColorPaletteChanged(resources: Resources) { }
+    fun onColorPaletteChanged(resources: Resources) {}
 }
 
 /** Methods which trigger various clock animations */
 interface ClockAnimations {
     /** Runs an enter animation (if any) */
-    fun enter() { }
+    fun enter() {}
 
     /** Sets how far into AOD the device currently is. */
-    fun doze(fraction: Float) { }
+    fun doze(fraction: Float) {}
 
     /** Sets how far into the folding animation the device is. */
-    fun fold(fraction: Float) { }
+    fun fold(fraction: Float) {}
 
     /** Runs the battery animation (if any). */
-    fun charge() { }
+    fun charge() {}
 
     /** Move the clock, for example, if the notification tray appears in split-shade mode. */
-    fun onPositionUpdated(fromRect: Rect, toRect: Rect, fraction: Float) { }
+    fun onPositionUpdated(fromRect: Rect, toRect: Rect, fraction: Float) {}
 
     /**
      * Whether this clock has a custom position update animation. If true, the keyguard will call
@@ -135,11 +132,26 @@
 /** Events that have specific data about the related face */
 interface ClockFaceEvents {
     /** Region Darkness specific to the clock face */
-    fun onRegionDarknessChanged(isDark: Boolean) { }
+    fun onRegionDarknessChanged(isDark: Boolean) {}
+
+    /**
+     * Call whenever font settings change. Pass in a target font size in pixels. The specific clock
+     * design is allowed to ignore this target size on a case-by-case basis.
+     */
+    fun onFontSettingChanged(fontSizePx: Float) {}
+
+    /**
+     * Target region information for the clock face. For small clock, this will match the bounds of
+     * the parent view mostly, but have a target height based on the height of the default clock.
+     * For large clocks, the parent view is the entire device size, but most clocks will want to
+     * render within the centered targetRect to avoid obstructing other elements. The specified
+     * targetRegion is relative to the parent view.
+     */
+    fun onTargetRegionChanged(targetRegion: Rect?) {}
 }
 
 /** Some data about a clock design */
 data class ClockMetadata(
     val clockId: ClockId,
-    val name: String
+    val name: String,
 )
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index c297149..b49afee 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -37,7 +37,6 @@
         android:id="@+id/lockscreen_clock_view_large"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:layout_marginTop="@dimen/keyguard_large_clock_top_margin"
         android:clipChildren="false"
         android:visibility="gone" />
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index ca780c8..599cd23 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -20,6 +20,7 @@
 import android.icu.text.NumberFormat
 import android.util.TypedValue
 import android.view.LayoutInflater
+import android.view.View
 import android.widget.FrameLayout
 import androidx.annotation.VisibleForTesting
 import com.android.systemui.plugins.ClockAnimations
@@ -80,7 +81,7 @@
     }
 
     override fun initialize(resources: Resources, dozeFraction: Float, foldFraction: Float) {
-        largeClock.recomputePadding()
+        largeClock.recomputePadding(null)
         animations = DefaultClockAnimations(dozeFraction, foldFraction)
         events.onColorPaletteChanged(resources)
         events.onTimeZoneChanged(TimeZone.getDefault())
@@ -101,6 +102,7 @@
         // MAGENTA is a placeholder, and will be assigned correctly in initialize
         private var currentColor = Color.MAGENTA
         private var isRegionDark = false
+        protected var targetRegion: Rect? = null
 
         init {
             view.setColors(currentColor, currentColor)
@@ -112,8 +114,20 @@
                     this@DefaultClockFaceController.isRegionDark = isRegionDark
                     updateColor()
                 }
+
+                override fun onTargetRegionChanged(targetRegion: Rect?) {
+                    this@DefaultClockFaceController.targetRegion = targetRegion
+                    recomputePadding(targetRegion)
+                }
+
+                override fun onFontSettingChanged(fontSizePx: Float) {
+                    view.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSizePx)
+                    recomputePadding(targetRegion)
+                }
             }
 
+        open fun recomputePadding(targetRegion: Rect?) {}
+
         fun updateColor() {
             val color =
                 if (isRegionDark) {
@@ -135,9 +149,16 @@
     inner class LargeClockFaceController(
         view: AnimatableClockView,
     ) : DefaultClockFaceController(view) {
-        fun recomputePadding() {
+        override fun recomputePadding(targetRegion: Rect?) {
+            // We center the view within the targetRegion instead of within the parent
+            // view by computing the difference and adding that to the padding.
+            val parent = view.parent
+            val yDiff =
+                if (targetRegion != null && parent is View && parent.isLaidOut())
+                    targetRegion.centerY() - parent.height / 2f
+                else 0f
             val lp = view.getLayoutParams() as FrameLayout.LayoutParams
-            lp.topMargin = (-0.5f * view.bottom).toInt()
+            lp.topMargin = (-0.5f * view.bottom + yDiff).toInt()
             view.setLayoutParams(lp)
         }
 
@@ -155,18 +176,6 @@
         override fun onTimeZoneChanged(timeZone: TimeZone) =
             clocks.forEach { it.onTimeZoneChanged(timeZone) }
 
-        override fun onFontSettingChanged() {
-            smallClock.view.setTextSize(
-                TypedValue.COMPLEX_UNIT_PX,
-                resources.getDimensionPixelSize(R.dimen.small_clock_text_size).toFloat()
-            )
-            largeClock.view.setTextSize(
-                TypedValue.COMPLEX_UNIT_PX,
-                resources.getDimensionPixelSize(R.dimen.large_clock_text_size).toFloat()
-            )
-            largeClock.recomputePadding()
-        }
-
         override fun onColorPaletteChanged(resources: Resources) {
             largeClock.updateColor()
             smallClock.updateColor()
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index c9b8712..87e9d56 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -26,6 +26,7 @@
 import androidx.annotation.VisibleForTesting
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.R
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
@@ -43,6 +44,11 @@
 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 java.util.concurrent.Executor
+import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.DisposableHandle
 import kotlinx.coroutines.Job
@@ -50,11 +56,6 @@
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.launch
-import java.io.PrintWriter
-import java.util.Locale
-import java.util.TimeZone
-import java.util.concurrent.Executor
-import javax.inject.Inject
 
 /**
  * Controller for a Clock provided by the registry and used on the keyguard. Instantiated by
@@ -84,6 +85,7 @@
 
                 value.initialize(resources, dozeAmount, 0f)
                 updateRegionSamplers(value)
+                updateFontSizes()
             }
         }
 
@@ -150,7 +152,7 @@
             mainExecutor,
             bgExecutor,
             regionSamplingEnabled,
-            updateFun = { updateColors() } )
+            updateColors)
     }
 
     var smallRegionSampler: RegionSampler? = null
@@ -166,7 +168,7 @@
         }
 
         override fun onDensityOrFontScaleChanged() {
-            clock?.events?.onFontSettingChanged()
+            updateFontSizes()
         }
     }
 
@@ -251,6 +253,13 @@
         largeRegionSampler?.stopRegionSampler()
     }
 
+    private fun updateFontSizes() {
+        clock?.smallClock?.events?.onFontSettingChanged(
+            resources.getDimensionPixelSize(R.dimen.small_clock_text_size).toFloat())
+        clock?.largeClock?.events?.onFontSettingChanged(
+            resources.getDimensionPixelSize(R.dimen.large_clock_text_size).toFloat())
+    }
+
     /**
      * Dump information for debugging
      */
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 8ebad6c..40423cd 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -5,6 +5,7 @@
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.content.Context;
+import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.View;
@@ -22,6 +23,7 @@
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+
 /**
  * Switch to show plugin clock when plugin is connected, otherwise it will show default clock.
  */
@@ -46,6 +48,7 @@
      */
     private FrameLayout mSmallClockFrame;
     private FrameLayout mLargeClockFrame;
+    private ClockController mClock;
 
     private View mStatusArea;
     private int mSmartspaceTopOffset;
@@ -95,6 +98,8 @@
     }
 
     void setClock(ClockController clock, int statusBarState) {
+        mClock = clock;
+
         // Disconnect from existing plugin.
         mSmallClockFrame.removeAllViews();
         mLargeClockFrame.removeAllViews();
@@ -108,6 +113,35 @@
         Log.i(TAG, "Attached new clock views to switch");
         mSmallClockFrame.addView(clock.getSmallClock().getView());
         mLargeClockFrame.addView(clock.getLargeClock().getView());
+        updateClockTargetRegions();
+    }
+
+    void updateClockTargetRegions() {
+        if (mClock != null) {
+            if (mSmallClockFrame.isLaidOut()) {
+                int targetHeight =  getResources()
+                        .getDimensionPixelSize(R.dimen.small_clock_text_size);
+                mClock.getSmallClock().getEvents().onTargetRegionChanged(new Rect(
+                        mSmallClockFrame.getLeft(),
+                        mSmallClockFrame.getTop(),
+                        mSmallClockFrame.getRight(),
+                        mSmallClockFrame.getTop() + targetHeight));
+            }
+
+            if (mLargeClockFrame.isLaidOut()) {
+                int largeClockTopMargin = getResources()
+                        .getDimensionPixelSize(R.dimen.keyguard_large_clock_top_margin);
+                int targetHeight = getResources()
+                        .getDimensionPixelSize(R.dimen.large_clock_text_size) * 2;
+                int top = mLargeClockFrame.getHeight() / 2 - targetHeight / 2
+                        + largeClockTopMargin / 2;
+                mClock.getLargeClock().getEvents().onTargetRegionChanged(new Rect(
+                        mLargeClockFrame.getLeft(),
+                        top,
+                        mLargeClockFrame.getRight(),
+                        top + targetHeight));
+            }
+        }
     }
 
     private void updateClockViews(boolean useLargeClock, boolean animate) {
@@ -214,6 +248,10 @@
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
         super.onLayout(changed, l, t, r, b);
 
+        if (changed) {
+            post(() -> updateClockTargetRegions());
+        }
+
         if (mDisplayedClockSize != null && !mChildrenAreLaidOut) {
             post(() -> updateClockViews(mDisplayedClockSize == LARGE, mAnimateOnLayout));
         }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index d3cc7ed..789f621 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -77,7 +77,7 @@
     @KeyguardClockSwitch.ClockSize
     private int mCurrentClockSize = SMALL;
 
-    private int mKeyguardClockTopMargin = 0;
+    private int mKeyguardSmallClockTopMargin = 0;
     private final ClockRegistry.ClockChangeListener mClockChangedListener;
 
     private ViewGroup mStatusArea;
@@ -162,7 +162,7 @@
         mClockRegistry.registerClockChangeListener(mClockChangedListener);
         setClock(mClockRegistry.createCurrentClock());
         mClockEventController.registerListeners(mView);
-        mKeyguardClockTopMargin =
+        mKeyguardSmallClockTopMargin =
                 mView.getResources().getDimensionPixelSize(R.dimen.keyguard_clock_top_margin);
 
         if (mOnlyClock) {
@@ -244,10 +244,12 @@
      */
     public void onDensityOrFontScaleChanged() {
         mView.onDensityOrFontScaleChanged();
-        mKeyguardClockTopMargin =
+        mKeyguardSmallClockTopMargin =
                 mView.getResources().getDimensionPixelSize(R.dimen.keyguard_clock_top_margin);
+        mView.updateClockTargetRegions();
     }
 
+
     /**
      * Set which clock should be displayed on the keyguard. The other one will be automatically
      * hidden.
@@ -327,7 +329,7 @@
             return frameHeight / 2 + clockHeight / 2;
         } else {
             int clockHeight = clock.getSmallClock().getView().getHeight();
-            return clockHeight + statusBarHeaderHeight + mKeyguardClockTopMargin;
+            return clockHeight + statusBarHeaderHeight + mKeyguardSmallClockTopMargin;
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index 52b6b38..e8f8e25 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -155,7 +155,8 @@
         verify(configurationController).addCallback(capture(captor))
         captor.value.onDensityOrFontScaleChanged()
 
-        verify(events).onFontSettingChanged()
+        verify(smallClockEvents, times(2)).onFontSettingChanged(anyFloat())
+        verify(largeClockEvents, times(2)).onFontSettingChanged(anyFloat())
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
index 539a54b..f5bed79 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
@@ -139,12 +139,19 @@
     }
 
     @Test
-    fun defaultClock_events_onFontSettingChanged() {
+    fun defaultSmallClock_events_onFontSettingChanged() {
         val clock = provider.createClock(DEFAULT_CLOCK_ID)
-        clock.events.onFontSettingChanged()
+        clock.smallClock.events.onFontSettingChanged(100f)
 
-        verify(mockSmallClockView).setTextSize(eq(TypedValue.COMPLEX_UNIT_PX), anyFloat())
-        verify(mockLargeClockView).setTextSize(eq(TypedValue.COMPLEX_UNIT_PX), anyFloat())
+        verify(mockSmallClockView).setTextSize(eq(TypedValue.COMPLEX_UNIT_PX), eq(100f))
+    }
+
+    @Test
+    fun defaultLargeClock_events_onFontSettingChanged() {
+        val clock = provider.createClock(DEFAULT_CLOCK_ID)
+        clock.largeClock.events.onFontSettingChanged(200f)
+
+        verify(mockLargeClockView).setTextSize(eq(TypedValue.COMPLEX_UNIT_PX), eq(200f))
         verify(mockLargeClockView).setLayoutParams(any())
     }