diff options
| author | 2021-06-07 16:27:36 +0000 | |
|---|---|---|
| committer | 2021-06-08 08:05:36 +0000 | |
| commit | c81323e3902e39f622941f29c08c037a3ce6b006 (patch) | |
| tree | 497be56a3db4feb25fc41927808cf66cd2241024 | |
| parent | ebcaa39d6b87e25e2f87319943186ea006405af5 (diff) | |
Schedule time ticks for the next minute when no seconds hand is present
The TIME_TICK broadcast was removed as it's bad for performance to use
it while the view is not visible and adds unneeded overhead.
Instead we can use the internal handler callback scheduling method.
Bug: 190274204
Test: verified locally with app, including across DST time skip
Change-Id: I4eb5e64b16c953baf473331dc7d498de19cfdbc9
| -rw-r--r-- | core/java/android/widget/AnalogClock.java | 70 |
1 files changed, 47 insertions, 23 deletions
diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java index 40cce7c34049..d5966269a753 100644 --- a/core/java/android/widget/AnalogClock.java +++ b/core/java/android/widget/AnalogClock.java @@ -44,6 +44,7 @@ import java.time.Duration; import java.time.Instant; import java.time.LocalTime; import java.time.ZoneId; +import java.time.ZonedDateTime; import java.util.Formatter; import java.util.Locale; @@ -451,7 +452,9 @@ public class AnalogClock extends View { if (mSecondHandTintInfo.mHasTintList || mSecondHandTintInfo.mHasTintBlendMode) { mSecondHand = mSecondHandTintInfo.apply(mSecondHand); } - mSecondsTick.run(); + // Re-run the tick runnable immediately as the presence or absence of a seconds hand affects + // the next time we need to tick the clock. + mTick.run(); mChanged = true; invalidate(); @@ -583,10 +586,10 @@ public class AnalogClock extends View { filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); // OK, this is gross but needed. This class is supported by the - // remote views machanism and as a part of that the remote views + // remote views mechanism and as a part of that the remote views // can be inflated by a context for another user without the app // having interact users permission - just for loading resources. - // For exmaple, when adding widgets from a user profile to the + // For example, when adding widgets from a user profile to the // home screen. Therefore, we register the receiver as the current // user not the one the context is for. getContext().registerReceiverAsUser(mIntentReceiver, @@ -616,14 +619,14 @@ public class AnalogClock extends View { private void onVisible() { if (!mVisible) { mVisible = true; - mSecondsTick.run(); + mTick.run(); } } private void onInvisible() { if (mVisible) { - removeCallbacks(mSecondsTick); + removeCallbacks(mTick); mVisible = false; } } @@ -760,6 +763,7 @@ public class AnalogClock extends View { } } + /** Intent receiver for the time or time zone changing. */ private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -767,36 +771,56 @@ public class AnalogClock extends View { createClock(); } - onTimeChanged(); - - invalidate(); + mTick.run(); } }; private boolean mReceiverAttached; - private final Runnable mSecondsTick = new Runnable() { + private final Runnable mTick = new Runnable() { @Override public void run() { removeCallbacks(this); - if (!mVisible || mSecondHand == null) { + if (!mVisible) { return; } Instant now = mClock.instant(); - LocalTime localTime = now.atZone(mClock.getZone()).toLocalTime(); - // How many milliseconds through the second we currently are. - long millisOfSecond = Duration.ofNanos(localTime.getNano()).toMillis(); - // How many milliseconds there are between tick positions for the seconds hand. - double millisPerTick = 1000 / (double) mSecondsHandFps; - // How many milliseconds we are past the last tick position. - long millisPastLastTick = Math.round(millisOfSecond % millisPerTick); - // How many milliseconds there are until the next tick position. - long millisUntilNextTick = Math.round(millisPerTick - millisPastLastTick); - // If we are exactly at the tick position, this could be 0 milliseconds due to rounding. - // In this case, advance by the full amount of millis to the next position. - if (millisUntilNextTick <= 0) { - millisUntilNextTick = Math.round(millisPerTick); + ZonedDateTime zonedDateTime = now.atZone(mClock.getZone()); + LocalTime localTime = zonedDateTime.toLocalTime(); + + long millisUntilNextTick; + if (mSecondHand == null) { + // If there's no second hand, then tick at the start of the next minute. + // + // This must be done with ZonedDateTime as opposed to LocalDateTime to ensure proper + // handling of DST. Also note that because of leap seconds, it should not be assumed + // that one minute == 60 seconds. + Instant startOfNextMinute = zonedDateTime.plusMinutes(1).withSecond(0).toInstant(); + millisUntilNextTick = Duration.between(now, startOfNextMinute).toMillis(); + if (millisUntilNextTick <= 0) { + // This should never occur, but if it does, then just check the tick again in + // one minute to ensure we're always moving forward. + millisUntilNextTick = Duration.ofMinutes(1).toMillis(); + } + } else { + // If there is a seconds hand, then determine the next tick point based on the fps. + // + // How many milliseconds through the second we currently are. + long millisOfSecond = Duration.ofNanos(localTime.getNano()).toMillis(); + // How many milliseconds there are between tick positions for the seconds hand. + double millisPerTick = 1000 / (double) mSecondsHandFps; + // How many milliseconds we are past the last tick position. + long millisPastLastTick = Math.round(millisOfSecond % millisPerTick); + // How many milliseconds there are until the next tick position. + millisUntilNextTick = Math.round(millisPerTick - millisPastLastTick); + // If we are exactly at the tick position, this could be 0 milliseconds due to + // rounding. In this case, advance by the full amount of millis to the next + // position. + if (millisUntilNextTick <= 0) { + millisUntilNextTick = Math.round(millisPerTick); + } } + // Schedule a callback for when the next tick should occur. postDelayed(this, millisUntilNextTick); |