From 7556f48861d03abcc33bc37401733864ab282afc Mon Sep 17 00:00:00 2001 From: Steven Kideckel Date: Fri, 29 Jan 2021 13:05:28 +0000 Subject: [PATCH] Add support for seconds hand to AnalogClock Bug: 177997338 Test: manual - wrote app to use runtime apis Change-Id: I05b3ebb19598a4f1d605f9ba62005aaab733666d Merged-In: I05b3ebb19598a4f1d605f9ba62005aaab733666d --- core/api/current.txt | 5 + core/java/android/widget/AnalogClock.java | 116 ++++++++++++++++++++-- core/res/res/values/attrs.xml | 1 + core/res/res/values/public.xml | 1 + 4 files changed, 112 insertions(+), 11 deletions(-) diff --git a/core/api/current.txt b/core/api/current.txt index 6eb3ff88bec81..3c7fe101a312e 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -722,6 +722,7 @@ package android { field public static final int gwpAsanMode = 16844310; // 0x1010616 field public static final int hand_hour = 16843011; // 0x1010103 field public static final int hand_minute = 16843012; // 0x1010104 + field public static final int hand_second = 16844323; // 0x1010623 field public static final int handle = 16843354; // 0x101025a field public static final int handleProfiling = 16842786; // 0x1010022 field public static final int hapticFeedbackEnabled = 16843358; // 0x101025e @@ -53058,6 +53059,10 @@ package android.widget { ctor @Deprecated public AnalogClock(android.content.Context, android.util.AttributeSet); ctor @Deprecated public AnalogClock(android.content.Context, android.util.AttributeSet, int); ctor @Deprecated public AnalogClock(android.content.Context, android.util.AttributeSet, int, int); + method @Deprecated public void setDial(@NonNull android.graphics.drawable.Icon); + method @Deprecated public void setHourHand(@NonNull android.graphics.drawable.Icon); + method @Deprecated public void setMinuteHand(@NonNull android.graphics.drawable.Icon); + method @Deprecated public void setSecondHand(@Nullable android.graphics.drawable.Icon); } public class ArrayAdapter extends android.widget.BaseAdapter implements android.widget.Filterable android.widget.ThemedSpinnerAdapter { diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java index ffdb89de5f594..98738eff83c30 100644 --- a/core/java/android/widget/AnalogClock.java +++ b/core/java/android/widget/AnalogClock.java @@ -16,6 +16,8 @@ package android.widget; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.content.BroadcastReceiver; import android.content.Context; @@ -25,8 +27,10 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; import android.text.format.DateUtils; import android.util.AttributeSet; +import android.view.RemotableViewMethod; import android.view.View; import android.widget.RemoteViews.RemoteView; @@ -42,25 +46,32 @@ import java.time.ZoneId; * @attr ref android.R.styleable#AnalogClock_dial * @attr ref android.R.styleable#AnalogClock_hand_hour * @attr ref android.R.styleable#AnalogClock_hand_minute + * @attr ref android.R.styleable#AnalogClock_hand_second * @deprecated This widget is no longer supported. */ @RemoteView @Deprecated public class AnalogClock extends View { + /** How often the clock should refresh to make the seconds hand advance at ~15 FPS. */ + private static final long SECONDS_TICK_FREQUENCY_MS = 1000 / 15; + private Clock mClock; @UnsupportedAppUsage private Drawable mHourHand; @UnsupportedAppUsage private Drawable mMinuteHand; + @Nullable + private Drawable mSecondHand; @UnsupportedAppUsage private Drawable mDial; private int mDialWidth; private int mDialHeight; - private boolean mAttached; + private boolean mVisible; + private float mSeconds; private float mMinutes; private float mHour; private boolean mChanged; @@ -101,18 +112,70 @@ public class AnalogClock extends View { mMinuteHand = context.getDrawable(com.android.internal.R.drawable.clock_hand_minute); } + mSecondHand = a.getDrawable(com.android.internal.R.styleable.AnalogClock_hand_second); + mClock = Clock.systemDefaultZone(); mDialWidth = mDial.getIntrinsicWidth(); mDialHeight = mDial.getIntrinsicHeight(); } - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); + /** Sets the dial of the clock to the specified Icon. */ + @RemotableViewMethod + public void setDial(@NonNull Icon icon) { + mDial = icon.loadDrawable(getContext()); + mDialWidth = mDial.getIntrinsicWidth(); + mDialHeight = mDial.getIntrinsicHeight(); - if (!mAttached) { - mAttached = true; + mChanged = true; + invalidate(); + } + + /** Sets the hour hand of the clock to the specified Icon. */ + @RemotableViewMethod + public void setHourHand(@NonNull Icon icon) { + mHourHand = icon.loadDrawable(getContext()); + + mChanged = true; + invalidate(); + } + + /** Sets the minute hand of the clock to the specified Icon. */ + @RemotableViewMethod + public void setMinuteHand(@NonNull Icon icon) { + mMinuteHand = icon.loadDrawable(getContext()); + + mChanged = true; + invalidate(); + } + + /** + * Sets the second hand of the clock to the specified Icon, or hides the second hand if it is + * null. + */ + @RemotableViewMethod + public void setSecondHand(@Nullable Icon icon) { + mSecondHand = icon == null ? null : icon.loadDrawable(getContext()); + mSecondsTick.run(); + + mChanged = true; + invalidate(); + } + + @Override + public void onVisibilityAggregated(boolean isVisible) { + super.onVisibilityAggregated(isVisible); + + if (isVisible) { + onVisible(); + } else { + onInvisible(); + } + } + + private void onVisible() { + if (!mVisible) { + mVisible = true; IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_TIME_TICK); @@ -128,6 +191,8 @@ public class AnalogClock extends View { // user not the one the context is for. getContext().registerReceiverAsUser(mIntentReceiver, android.os.Process.myUserHandle(), filter, null, getHandler()); + + mSecondsTick.run(); } // NOTE: It's safe to do these after registering the receiver since the receiver always runs @@ -140,12 +205,11 @@ public class AnalogClock extends View { onTimeChanged(); } - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - if (mAttached) { + private void onInvisible() { + if (mVisible) { getContext().unregisterReceiver(mIntentReceiver); - mAttached = false; + removeCallbacks(mSecondsTick); + mVisible = false; } } @@ -237,6 +301,20 @@ public class AnalogClock extends View { minuteHand.draw(canvas); canvas.restore(); + final Drawable secondHand = mSecondHand; + if (secondHand != null) { + canvas.save(); + canvas.rotate(mSeconds / 60.0f * 360.0f, x, y); + + if (changed) { + w = secondHand.getIntrinsicWidth(); + h = secondHand.getIntrinsicHeight(); + secondHand.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y + (h / 2)); + } + secondHand.draw(canvas); + canvas.restore(); + } + if (scaled) { canvas.restore(); } @@ -250,6 +328,7 @@ public class AnalogClock extends View { int minute = localDateTime.getMinute(); int second = localDateTime.getSecond(); + mSeconds = second + localDateTime.getNano() / 1_000_000_000f; mMinutes = minute + second / 60.0f; mHour = hour + mMinutes / 60.0f; mChanged = true; @@ -271,6 +350,21 @@ public class AnalogClock extends View { } }; + private final Runnable mSecondsTick = new Runnable() { + @Override + public void run() { + if (!mVisible || mSecondHand == null) { + return; + } + + onTimeChanged(); + + invalidate(); + + postDelayed(this, SECONDS_TICK_FREQUENCY_MS); + } + }; + private void updateContentDescription(long timeMillis) { final int flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_24HOUR; String contentDescription = DateUtils.formatDateTime(mContext, timeMillis, flags); diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 14f1e0e20ef68..07c3adfb4c698 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -4069,6 +4069,7 @@ + diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 9c1c51cdd48e9..b76ab3a22a202 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3059,6 +3059,7 @@ +