Merge "Change clock animations to be per-clock face" into udc-dev

This commit is contained in:
Treehugger Robot
2023-04-17 20:46:43 +00:00
committed by Android (Google) Code Review
10 changed files with 118 additions and 86 deletions

View File

@@ -62,10 +62,7 @@ class DefaultClockController(
private val defaultLineSpacing = resources.getFloat(R.dimen.keyguard_clock_line_spacing_scale)
override val events: DefaultClockEvents
override lateinit var animations: DefaultClockAnimations
private set
override val config = ClockConfig(hasCustomPositionUpdatedAnimation = true)
override val config = ClockConfig()
init {
val parent = FrameLayout(ctx)
@@ -84,13 +81,13 @@ class DefaultClockController(
clocks = listOf(smallClock.view, largeClock.view)
events = DefaultClockEvents()
animations = DefaultClockAnimations(0f, 0f)
events.onLocaleChanged(Locale.getDefault())
}
override fun initialize(resources: Resources, dozeFraction: Float, foldFraction: Float) {
largeClock.recomputePadding(null)
animations = DefaultClockAnimations(dozeFraction, foldFraction)
largeClock.animations = LargeClockAnimations(largeClock.view, dozeFraction, foldFraction)
smallClock.animations = DefaultClockAnimations(smallClock.view, dozeFraction, foldFraction)
events.onColorPaletteChanged(resources)
events.onTimeZoneChanged(TimeZone.getDefault())
smallClock.events.onTimeTick()
@@ -115,6 +112,9 @@ class DefaultClockController(
view.logBuffer = value
}
override var animations: DefaultClockAnimations = DefaultClockAnimations(view, 0f, 0f)
internal set
init {
if (seedColor != null) {
currentColor = seedColor!!
@@ -170,6 +170,12 @@ class DefaultClockController(
view: AnimatableClockView,
seedColor: Int?,
) : DefaultClockFaceController(view, seedColor) {
override val config = ClockFaceConfig(hasCustomPositionUpdatedAnimation = true)
init {
animations = LargeClockAnimations(view, 0f, 0f)
}
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.
@@ -220,7 +226,8 @@ class DefaultClockController(
}
}
inner class DefaultClockAnimations(
open inner class DefaultClockAnimations(
val view: AnimatableClockView,
dozeFraction: Float,
foldFraction: Float,
) : ClockAnimations {
@@ -229,34 +236,40 @@ class DefaultClockController(
init {
if (foldState.isActive) {
clocks.forEach { it.animateFoldAppear(false) }
view.animateFoldAppear(false)
} else {
clocks.forEach { it.animateDoze(dozeState.isActive, false) }
view.animateDoze(dozeState.isActive, false)
}
}
override fun enter() {
if (!dozeState.isActive) {
clocks.forEach { it.animateAppearOnLockscreen() }
view.animateAppearOnLockscreen()
}
}
override fun charge() = clocks.forEach { it.animateCharge { dozeState.isActive } }
override fun charge() = view.animateCharge { dozeState.isActive }
override fun fold(fraction: Float) {
val (hasChanged, hasJumped) = foldState.update(fraction)
if (hasChanged) {
clocks.forEach { it.animateFoldAppear(!hasJumped) }
view.animateFoldAppear(!hasJumped)
}
}
override fun doze(fraction: Float) {
val (hasChanged, hasJumped) = dozeState.update(fraction)
if (hasChanged) {
clocks.forEach { it.animateDoze(dozeState.isActive, !hasJumped) }
view.animateDoze(dozeState.isActive, !hasJumped)
}
}
}
inner class LargeClockAnimations(
view: AnimatableClockView,
dozeFraction: Float,
foldFraction: Float,
) : DefaultClockAnimations(view, dozeFraction, foldFraction) {
override fun onPositionUpdated(fromRect: Rect, toRect: Rect, fraction: Float) {
largeClock.moveForSplitShade(fromRect, toRect, fraction)
}

View File

@@ -69,9 +69,6 @@ interface ClockController {
/** Events that clocks may need to respond to */
val events: ClockEvents
/** Triggers for various animations */
val animations: ClockAnimations
/** Initializes various rendering parameters. If never called, provides reasonable defaults. */
fun initialize(
resources: Resources,
@@ -79,8 +76,10 @@ interface ClockController {
foldFraction: Float,
) {
events.onColorPaletteChanged(resources)
animations.doze(dozeFraction)
animations.fold(foldFraction)
smallClock.animations.doze(dozeFraction)
largeClock.animations.doze(dozeFraction)
smallClock.animations.fold(foldFraction)
largeClock.animations.fold(foldFraction)
smallClock.events.onTimeTick()
largeClock.events.onTimeTick()
}
@@ -100,6 +99,9 @@ interface ClockFaceController {
/** Events specific to this clock face */
val events: ClockFaceEvents
/** Triggers for various animations */
val animations: ClockAnimations
/** Some clocks may log debug information */
var logBuffer: LogBuffer?
}
@@ -192,13 +194,6 @@ data class ClockMetadata(
/** Render configuration for the full clock. Modifies the way systemUI behaves with this clock. */
data class ClockConfig(
/**
* Whether this clock has a custom position update animation. If true, the keyguard will call
* `onPositionUpdated` to notify the clock of a position update animation. If false, a default
* animation will be used (e.g. a simple translation).
*/
val hasCustomPositionUpdatedAnimation: Boolean = false,
/** Transition to AOD should move smartspace like large clock instead of small clock */
val useAlternateSmartspaceAODTransition: Boolean = false,
@@ -213,6 +208,13 @@ data class ClockFaceConfig(
/** Call to check whether the clock consumes weather data */
val hasCustomWeatherDataDisplay: Boolean = false,
/**
* Whether this clock has a custom position update animation. If true, the keyguard will call
* `onPositionUpdated` to notify the clock of a position update animation. If false, a default
* animation will be used (e.g. a simple translation).
*/
val hasCustomPositionUpdatedAnimation: Boolean = false,
)
/** Structure for keeping clock-specific settings */

View File

@@ -99,8 +99,10 @@ constructor(
value.initialize(resources, dozeAmount, 0f)
if (regionSamplingEnabled) {
clock?.smallClock?.view?.addOnLayoutChangeListener(mLayoutChangedListener)
clock?.largeClock?.view?.addOnLayoutChangeListener(mLayoutChangedListener)
clock?.run {
smallClock.view.addOnLayoutChangeListener(mLayoutChangedListener)
largeClock.view.addOnLayoutChangeListener(mLayoutChangedListener)
}
} else {
updateColors()
}
@@ -175,15 +177,17 @@ constructor(
private fun updateColors() {
val wallpaperManager = WallpaperManager.getInstance(context)
if (regionSamplingEnabled && !wallpaperManager.lockScreenWallpaperExists()) {
if (regionSampler != null) {
if (regionSampler?.sampledView == clock?.smallClock?.view) {
smallClockIsDark = regionSampler!!.currentRegionDarkness().isDark
clock?.smallClock?.events?.onRegionDarknessChanged(smallClockIsDark)
return
} else if (regionSampler?.sampledView == clock?.largeClock?.view) {
largeClockIsDark = regionSampler!!.currentRegionDarkness().isDark
clock?.largeClock?.events?.onRegionDarknessChanged(largeClockIsDark)
return
regionSampler?.let { regionSampler ->
clock?.let { clock ->
if (regionSampler.sampledView == clock.smallClock.view) {
smallClockIsDark = regionSampler.currentRegionDarkness().isDark
clock.smallClock.events.onRegionDarknessChanged(smallClockIsDark)
return@updateColors
} else if (regionSampler.sampledView == clock.largeClock.view) {
largeClockIsDark = regionSampler.currentRegionDarkness().isDark
clock.largeClock.events.onRegionDarknessChanged(largeClockIsDark)
return@updateColors
}
}
}
}
@@ -193,8 +197,10 @@ constructor(
smallClockIsDark = isLightTheme.data == 0
largeClockIsDark = isLightTheme.data == 0
clock?.smallClock?.events?.onRegionDarknessChanged(smallClockIsDark)
clock?.largeClock?.events?.onRegionDarknessChanged(largeClockIsDark)
clock?.run {
smallClock.events.onRegionDarknessChanged(smallClockIsDark)
largeClock.events.onRegionDarknessChanged(largeClockIsDark)
}
}
private fun updateRegionSampler(sampledRegion: View) {
@@ -240,7 +246,7 @@ constructor(
private val configListener =
object : ConfigurationController.ConfigurationListener {
override fun onThemeChanged() {
clock?.events?.onColorPaletteChanged(resources)
clock?.run { events.onColorPaletteChanged(resources) }
updateColors()
}
@@ -253,7 +259,10 @@ constructor(
object : BatteryStateChangeCallback {
override fun onBatteryLevelChanged(level: Int, pluggedIn: Boolean, charging: Boolean) {
if (isKeyguardVisible && !isCharging && charging) {
clock?.animations?.charge()
clock?.run {
smallClock.animations.charge()
largeClock.animations.charge()
}
}
isCharging = charging
}
@@ -262,7 +271,7 @@ constructor(
private val localeBroadcastReceiver =
object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
clock?.events?.onLocaleChanged(Locale.getDefault())
clock?.run { events.onLocaleChanged(Locale.getDefault()) }
}
}
@@ -272,7 +281,10 @@ constructor(
isKeyguardVisible = visible
if (!featureFlags.isEnabled(DOZING_MIGRATION_1)) {
if (!isKeyguardVisible) {
clock?.animations?.doze(if (isDozing) 1f else 0f)
clock?.run {
smallClock.animations.doze(if (isDozing) 1f else 0f)
largeClock.animations.doze(if (isDozing) 1f else 0f)
}
}
}
@@ -281,19 +293,19 @@ constructor(
}
override fun onTimeFormatChanged(timeFormat: String?) {
clock?.events?.onTimeFormatChanged(DateFormat.is24HourFormat(context))
clock?.run { events.onTimeFormatChanged(DateFormat.is24HourFormat(context)) }
}
override fun onTimeZoneChanged(timeZone: TimeZone) {
clock?.events?.onTimeZoneChanged(timeZone)
clock?.run { events.onTimeZoneChanged(timeZone) }
}
override fun onUserSwitchComplete(userId: Int) {
clock?.events?.onTimeFormatChanged(DateFormat.is24HourFormat(context))
clock?.run { events.onTimeFormatChanged(DateFormat.is24HourFormat(context)) }
}
override fun onWeatherDataChanged(data: WeatherData) {
clock?.events?.onWeatherDataChanged(data)
clock?.run { events.onWeatherDataChanged(data) }
}
}
@@ -349,34 +361,33 @@ constructor(
smallTimeListener = null
largeTimeListener = null
clock?.smallClock?.let {
smallTimeListener = TimeListener(it, mainExecutor)
smallTimeListener?.update(shouldTimeListenerRun)
}
clock?.largeClock?.let {
largeTimeListener = TimeListener(it, mainExecutor)
largeTimeListener?.update(shouldTimeListenerRun)
clock?.let {
smallTimeListener = TimeListener(it.smallClock, mainExecutor).apply {
update(shouldTimeListenerRun)
}
largeTimeListener = TimeListener(it.largeClock, mainExecutor).apply {
update(shouldTimeListenerRun)
}
}
}
private fun updateFontSizes() {
clock
?.smallClock
?.events
?.onFontSettingChanged(
clock?.run {
smallClock.events.onFontSettingChanged(
resources.getDimensionPixelSize(R.dimen.small_clock_text_size).toFloat()
)
clock
?.largeClock
?.events
?.onFontSettingChanged(
largeClock.events.onFontSettingChanged(
resources.getDimensionPixelSize(R.dimen.large_clock_text_size).toFloat()
)
}
}
private fun handleDoze(doze: Float) {
dozeAmount = doze
clock?.animations?.doze(dozeAmount)
clock?.run {
smallClock.animations.doze(dozeAmount)
largeClock.animations.doze(dozeAmount)
}
smallTimeListener?.update(doze < DOZE_TICKRATE_THRESHOLD)
largeTimeListener?.update(doze < DOZE_TICKRATE_THRESHOLD)
}

View File

@@ -26,8 +26,6 @@ import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import kotlin.Unit;
/**
* Switch to show plugin clock when plugin is connected, otherwise it will show default clock.
*/
@@ -38,6 +36,8 @@ public class KeyguardClockSwitch extends RelativeLayout {
private static final long CLOCK_OUT_MILLIS = 150;
private static final long CLOCK_IN_MILLIS = 200;
public static final long CLOCK_IN_START_DELAY_MILLIS = CLOCK_OUT_MILLIS / 2;
private static final long STATUS_AREA_START_DELAY_MILLIS = 50;
private static final long STATUS_AREA_MOVE_MILLIS = 350;
@IntDef({LARGE, SMALL})
@@ -173,7 +173,7 @@ public class KeyguardClockSwitch extends RelativeLayout {
msg.setBool1(useLargeClock);
msg.setBool2(animate);
msg.setBool3(mChildrenAreLaidOut);
return Unit.INSTANCE;
return kotlin.Unit.INSTANCE;
}, (msg) -> "updateClockViews"
+ "; useLargeClock=" + msg.getBool1()
+ "; animate=" + msg.getBool2()
@@ -235,7 +235,7 @@ public class KeyguardClockSwitch extends RelativeLayout {
mClockInAnim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
mClockInAnim.playTogether(ObjectAnimator.ofFloat(in, View.ALPHA, 1f),
ObjectAnimator.ofFloat(in, View.TRANSLATION_Y, direction * mClockSwitchYAmount, 0));
mClockInAnim.setStartDelay(CLOCK_OUT_MILLIS / 2);
mClockInAnim.setStartDelay(CLOCK_IN_START_DELAY_MILLIS);
mClockInAnim.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator animation) {
mClockInAnim = null;
@@ -247,6 +247,7 @@ public class KeyguardClockSwitch extends RelativeLayout {
mStatusAreaAnim = ObjectAnimator.ofFloat(mStatusArea, View.TRANSLATION_Y,
statusAreaYTranslation);
mStatusAreaAnim.setStartDelay(useLargeClock ? STATUS_AREA_START_DELAY_MILLIS : 0L);
mStatusAreaAnim.setDuration(STATUS_AREA_MOVE_MILLIS);
mStatusAreaAnim.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
mStatusAreaAnim.addListener(new AnimatorListenerAdapter() {

View File

@@ -53,11 +53,11 @@ 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.util.ViewController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.settings.SecureSettings;
import java.io.PrintWriter;
import java.util.Locale;
import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -98,7 +98,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
private boolean mOnlyClock = false;
private final Executor mUiExecutor;
private final DelayableExecutor mUiExecutor;
private boolean mCanShowDoubleLineClock = true;
private final ContentObserver mDoubleLineClockObserver = new ContentObserver(null) {
@Override
@@ -133,7 +133,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
LockscreenSmartspaceController smartspaceController,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
SecureSettings secureSettings,
@Main Executor uiExecutor,
@Main DelayableExecutor uiExecutor,
DumpManager dumpManager,
ClockEventController clockEventController,
@KeyguardClockLog LogBuffer logBuffer) {
@@ -344,7 +344,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
ClockController clock = getClock();
boolean appeared = mView.switchToClock(clockSize, animate);
if (clock != null && animate && appeared && clockSize == LARGE) {
clock.getAnimations().enter();
mUiExecutor.executeDelayed(() -> clock.getLargeClock().getAnimations().enter(),
KeyguardClockSwitch.CLOCK_IN_START_DELAY_MILLIS);
}
}
@@ -354,7 +355,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
public void animateFoldToAod(float foldFraction) {
ClockController clock = getClock();
if (clock != null) {
clock.getAnimations().fold(foldFraction);
clock.getSmallClock().getAnimations().fold(foldFraction);
clock.getLargeClock().getAnimations().fold(foldFraction);
}
}

View File

@@ -349,7 +349,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
ClockController clock = mKeyguardClockSwitchController.getClock();
boolean customClockAnimation = clock != null
&& clock.getConfig().getHasCustomPositionUpdatedAnimation();
&& clock.getLargeClock().getConfig().getHasCustomPositionUpdatedAnimation();
if (mFeatureFlags.isEnabled(Flags.STEP_CLOCK_ANIMATION) && customClockAnimation) {
// Find the clock, so we can exclude it from this transition.
@@ -436,7 +436,8 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
return;
}
clock.getAnimations().onPositionUpdated(from, to, animation.getAnimatedFraction());
clock.getLargeClock().getAnimations()
.onPositionUpdated(from, to, animation.getAnimatedFraction());
});
return anim;

View File

@@ -101,7 +101,8 @@ class ClockEventControllerTest : SysuiTestCase() {
whenever(smallClockController.events).thenReturn(smallClockEvents)
whenever(largeClockController.events).thenReturn(largeClockEvents)
whenever(clock.events).thenReturn(events)
whenever(clock.animations).thenReturn(animations)
whenever(smallClockController.animations).thenReturn(animations)
whenever(largeClockController.animations).thenReturn(animations)
whenever(smallClockController.config)
.thenReturn(ClockFaceConfig(tickRate = ClockTickRate.PER_MINUTE))
whenever(largeClockController.config)
@@ -184,7 +185,7 @@ class ClockEventControllerTest : SysuiTestCase() {
keyguardCaptor.value.onKeyguardVisibilityChanged(true)
batteryCaptor.value.onBatteryLevelChanged(10, false, true)
verify(animations).charge()
verify(animations, times(2)).charge()
}
@Test
@@ -198,7 +199,7 @@ class ClockEventControllerTest : SysuiTestCase() {
batteryCaptor.value.onBatteryLevelChanged(10, false, true)
batteryCaptor.value.onBatteryLevelChanged(10, false, true)
verify(animations, times(1)).charge()
verify(animations, times(2)).charge()
}
@Test
@@ -246,7 +247,7 @@ class ClockEventControllerTest : SysuiTestCase() {
verify(animations, never()).doze(0f)
captor.value.onKeyguardVisibilityChanged(false)
verify(animations, times(1)).doze(0f)
verify(animations, times(2)).doze(0f)
}
@Test
@@ -284,7 +285,7 @@ class ClockEventControllerTest : SysuiTestCase() {
yield()
verify(animations).doze(0.4f)
verify(animations, times(2)).doze(0.4f)
job.cancel()
}

View File

@@ -184,13 +184,14 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
when(mClockController.getEvents()).thenReturn(mClockEvents);
when(mSmallClockController.getEvents()).thenReturn(mClockFaceEvents);
when(mLargeClockController.getEvents()).thenReturn(mClockFaceEvents);
when(mClockController.getAnimations()).thenReturn(mClockAnimations);
when(mLargeClockController.getAnimations()).thenReturn(mClockAnimations);
when(mSmallClockController.getAnimations()).thenReturn(mClockAnimations);
when(mClockRegistry.createCurrentClock()).thenReturn(mClockController);
when(mClockEventController.getClock()).thenReturn(mClockController);
when(mSmallClockController.getConfig())
.thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, false));
.thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, false, false));
when(mLargeClockController.getConfig())
.thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, false));
.thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, false, false));
mSliceView = new View(getContext());
when(mView.findViewById(R.id.keyguard_slice_view)).thenReturn(mSliceView);
@@ -384,9 +385,9 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
assertEquals(View.VISIBLE, mFakeDateView.getVisibility());
when(mSmallClockController.getConfig())
.thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, true));
.thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, true, false));
when(mLargeClockController.getConfig())
.thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, true));
.thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, true, false));
verify(mClockRegistry).registerClockChangeListener(listenerArgumentCaptor.capture());
listenerArgumentCaptor.getValue().onCurrentClockChanged();

View File

@@ -130,7 +130,7 @@ public class KeyguardStatusViewControllerTest extends SysuiTestCase {
public void updatePosition_primaryClockAnimation() {
ClockController mockClock = mock(ClockController.class);
when(mKeyguardClockSwitchController.getClock()).thenReturn(mockClock);
when(mockClock.getConfig()).thenReturn(new ClockConfig(false, false, true));
when(mockClock.getConfig()).thenReturn(new ClockConfig(false, true));
mController.updatePosition(10, 15, 20f, true);
@@ -145,7 +145,7 @@ public class KeyguardStatusViewControllerTest extends SysuiTestCase {
public void updatePosition_alternateClockAnimation() {
ClockController mockClock = mock(ClockController.class);
when(mKeyguardClockSwitchController.getClock()).thenReturn(mockClock);
when(mockClock.getConfig()).thenReturn(new ClockConfig(false, true, true));
when(mockClock.getConfig()).thenReturn(new ClockConfig(true, true));
mController.updatePosition(10, 15, 20f, true);

View File

@@ -201,7 +201,7 @@ class DefaultClockProviderTest : SysuiTestCase() {
@Test
fun test_aodClock_always_whiteColor() {
val clock = provider.createClock(DEFAULT_CLOCK_ID)
clock.animations.doze(0.9f) // set AOD mode to active
clock.smallClock.animations.doze(0.9f) // set AOD mode to active
clock.smallClock.events.onRegionDarknessChanged(true)
verify((clock.smallClock.view as AnimatableClockView), never()).animateAppearOnLockscreen()
}