blob: dba3b923db4f09abe24bdde883f04ed0e4d89158 [file] [log] [blame]
Joe Onorato263700d2010-05-14 11:54:53 -07001/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Joe Onoratofd52b182010-11-10 18:00:52 -080017package com.android.systemui.statusbar.policy;
Joe Onorato263700d2010-05-14 11:54:53 -070018
Jason Monkaa573e92017-01-27 17:00:29 -050019import android.app.StatusBarManager;
Daniel Sandler87937db2010-05-27 13:44:11 -040020import android.content.BroadcastReceiver;
Joe Onorato263700d2010-05-14 11:54:53 -070021import android.content.Context;
22import android.content.Intent;
23import android.content.IntentFilter;
Jorim Jaggi740beb52014-05-11 18:53:19 +020024import android.content.res.TypedArray;
Jason Monkaa573e92017-01-27 17:00:29 -050025import android.graphics.Rect;
John Spurlock3c875662013-08-31 15:07:25 -040026import android.os.Bundle;
Jason Monkfe7c91b2015-08-13 15:42:28 -040027import android.os.Handler;
felkachange6c03a02018-05-24 15:38:04 +080028import android.os.Parcelable;
Jason Monkfe7c91b2015-08-13 15:42:28 -040029import android.os.SystemClock;
Selim Cinek9c4a7072014-11-21 17:44:34 +010030import android.os.UserHandle;
Joe Onorato263700d2010-05-14 11:54:53 -070031import android.text.Spannable;
32import android.text.SpannableStringBuilder;
Daniel Sandler87937db2010-05-27 13:44:11 -040033import android.text.format.DateFormat;
34import android.text.style.CharacterStyle;
Daniel Sandler87937db2010-05-27 13:44:11 -040035import android.text.style.RelativeSizeSpan;
Joe Onorato263700d2010-05-14 11:54:53 -070036import android.util.AttributeSet;
Jason Monkfe7c91b2015-08-13 15:42:28 -040037import android.view.Display;
Jason Monkaa573e92017-01-27 17:00:29 -050038import android.view.View;
Joe Onorato263700d2010-05-14 11:54:53 -070039import android.widget.TextView;
Winsonc0d70582016-01-29 10:24:39 -080040
Rohan Shahcc3d1d82018-03-30 21:24:17 +000041import com.android.settingslib.Utils;
John Spurlock3c875662013-08-31 15:07:25 -040042import com.android.systemui.DemoMode;
Jason Monk9c7844c2017-01-18 15:21:53 -050043import com.android.systemui.Dependency;
Jason Monkaa573e92017-01-27 17:00:29 -050044import com.android.systemui.FontSizeUtils;
Jorim Jaggi740beb52014-05-11 18:53:19 +020045import com.android.systemui.R;
Fabian Kozynskiff5e91f2019-09-24 15:38:08 -040046import com.android.systemui.broadcast.BroadcastDispatcher;
Beverly1be62f42018-12-19 17:17:48 -050047import com.android.systemui.plugins.DarkIconDispatcher;
48import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
Lucas Dupin4eae8812018-04-09 22:44:17 -070049import com.android.systemui.settings.CurrentUserTracker;
Jason Monkaa573e92017-01-27 17:00:29 -050050import com.android.systemui.statusbar.CommandQueue;
Jason Monk3e189872016-01-12 09:10:34 -050051import com.android.systemui.statusbar.phone.StatusBarIconController;
Jason Monkaa573e92017-01-27 17:00:29 -050052import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
Jason Monkfe7c91b2015-08-13 15:42:28 -040053import com.android.systemui.tuner.TunerService;
54import com.android.systemui.tuner.TunerService.Tunable;
John Spurlock3c875662013-08-31 15:07:25 -040055
Lucas Dupin4eae8812018-04-09 22:44:17 -070056import libcore.icu.LocaleData;
57
Joe Onorato263700d2010-05-14 11:54:53 -070058import java.text.SimpleDateFormat;
59import java.util.Calendar;
Daniel Sandler43b23c62012-11-29 11:35:02 -050060import java.util.Locale;
Joe Onorato263700d2010-05-14 11:54:53 -070061import java.util.TimeZone;
62
Joe Onorato263700d2010-05-14 11:54:53 -070063/**
Daniel Sandlerce4f5e52012-11-19 14:47:18 -050064 * Digital clock for the status bar.
Joe Onorato263700d2010-05-14 11:54:53 -070065 */
Jason Monkaa573e92017-01-27 17:00:29 -050066public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.Callbacks,
67 DarkReceiver, ConfigurationListener {
Jason Monkfe7c91b2015-08-13 15:42:28 -040068
69 public static final String CLOCK_SECONDS = "clock_seconds";
felkachange6c03a02018-05-24 15:38:04 +080070 private static final String CLOCK_SUPER_PARCELABLE = "clock_super_parcelable";
71 private static final String CURRENT_USER_ID = "current_user_id";
72 private static final String VISIBLE_BY_POLICY = "visible_by_policy";
73 private static final String VISIBLE_BY_USER = "visible_by_user";
74 private static final String SHOW_SECONDS = "show_seconds";
75 private static final String VISIBILITY = "visibility";
Jason Monkfe7c91b2015-08-13 15:42:28 -040076
Lucas Dupin4eae8812018-04-09 22:44:17 -070077 private final CurrentUserTracker mCurrentUserTracker;
Dave Mankoffbcaca8a2019-10-31 18:04:08 -040078 private final CommandQueue mCommandQueue;
Lucas Dupin4eae8812018-04-09 22:44:17 -070079 private int mCurrentUserId;
80
Jason Monkaa573e92017-01-27 17:00:29 -050081 private boolean mClockVisibleByPolicy = true;
82 private boolean mClockVisibleByUser = true;
83
Joe Onorato263700d2010-05-14 11:54:53 -070084 private boolean mAttached;
85 private Calendar mCalendar;
86 private String mClockFormatString;
87 private SimpleDateFormat mClockFormat;
Adrian Roos70dcf832016-04-20 15:51:42 -070088 private SimpleDateFormat mContentDescriptionFormat;
Daniel Sandler43b23c62012-11-29 11:35:02 -050089 private Locale mLocale;
Joe Onorato263700d2010-05-14 11:54:53 -070090
Daniel Sandler87937db2010-05-27 13:44:11 -040091 private static final int AM_PM_STYLE_NORMAL = 0;
92 private static final int AM_PM_STYLE_SMALL = 1;
93 private static final int AM_PM_STYLE_GONE = 2;
94
Jorim Jaggi740beb52014-05-11 18:53:19 +020095 private final int mAmPmStyle;
Jason Monke5b770e2017-03-03 21:49:29 -050096 private final boolean mShowDark;
Jason Monkfe7c91b2015-08-13 15:42:28 -040097 private boolean mShowSeconds;
98 private Handler mSecondsHandler;
Daniel Sandler87937db2010-05-27 13:44:11 -040099
Rohan Shahcc3d1d82018-03-30 21:24:17 +0000100 /**
101 * Whether we should use colors that adapt based on wallpaper/the scrim behind quick settings
102 * for text.
103 */
104 private boolean mUseWallpaperTextColor;
105
106 /**
107 * Color to be set on this {@link TextView}, when wallpaperTextColor is <b>not</b> utilized.
108 */
109 private int mNonAdaptedColor;
110
Fabian Kozynskiff5e91f2019-09-24 15:38:08 -0400111 private final BroadcastDispatcher mBroadcastDispatcher;
Joe Onorato263700d2010-05-14 11:54:53 -0700112
113 public Clock(Context context, AttributeSet attrs) {
Fabian Kozynski5ca7a512019-10-16 19:56:11 +0000114 this(context, attrs, 0);
Joe Onorato263700d2010-05-14 11:54:53 -0700115 }
116
Fabian Kozynski5ca7a512019-10-16 19:56:11 +0000117 public Clock(Context context, AttributeSet attrs, int defStyle) {
Joe Onorato263700d2010-05-14 11:54:53 -0700118 super(context, attrs, defStyle);
Fabian Kozynski5ca7a512019-10-16 19:56:11 +0000119 mCommandQueue = Dependency.get(CommandQueue.class);
Jorim Jaggi740beb52014-05-11 18:53:19 +0200120 TypedArray a = context.getTheme().obtainStyledAttributes(
121 attrs,
122 R.styleable.Clock,
123 0, 0);
124 try {
125 mAmPmStyle = a.getInt(R.styleable.Clock_amPmStyle, AM_PM_STYLE_GONE);
Jason Monke5b770e2017-03-03 21:49:29 -0500126 mShowDark = a.getBoolean(R.styleable.Clock_showDark, true);
Rohan Shahcc3d1d82018-03-30 21:24:17 +0000127 mNonAdaptedColor = getCurrentTextColor();
Jorim Jaggi740beb52014-05-11 18:53:19 +0200128 } finally {
129 a.recycle();
130 }
Fabian Kozynski5ca7a512019-10-16 19:56:11 +0000131 mBroadcastDispatcher = Dependency.get(BroadcastDispatcher.class);
132 mCurrentUserTracker = new CurrentUserTracker(mBroadcastDispatcher) {
Lucas Dupin4eae8812018-04-09 22:44:17 -0700133 @Override
134 public void onUserSwitched(int newUserId) {
135 mCurrentUserId = newUserId;
136 }
137 };
Joe Onorato263700d2010-05-14 11:54:53 -0700138 }
139
140 @Override
felkachange6c03a02018-05-24 15:38:04 +0800141 public Parcelable onSaveInstanceState() {
142 Bundle bundle = new Bundle();
143 bundle.putParcelable(CLOCK_SUPER_PARCELABLE, super.onSaveInstanceState());
144 bundle.putInt(CURRENT_USER_ID, mCurrentUserId);
145 bundle.putBoolean(VISIBLE_BY_POLICY, mClockVisibleByPolicy);
146 bundle.putBoolean(VISIBLE_BY_USER, mClockVisibleByUser);
147 bundle.putBoolean(SHOW_SECONDS, mShowSeconds);
148 bundle.putInt(VISIBILITY, getVisibility());
149
150 return bundle;
151 }
152
153 @Override
154 public void onRestoreInstanceState(Parcelable state) {
155 if (state == null || !(state instanceof Bundle)) {
156 super.onRestoreInstanceState(state);
157 return;
158 }
159
160 Bundle bundle = (Bundle) state;
161 Parcelable superState = bundle.getParcelable(CLOCK_SUPER_PARCELABLE);
162 super.onRestoreInstanceState(superState);
163 if (bundle.containsKey(CURRENT_USER_ID)) {
164 mCurrentUserId = bundle.getInt(CURRENT_USER_ID);
165 }
166 mClockVisibleByPolicy = bundle.getBoolean(VISIBLE_BY_POLICY, true);
167 mClockVisibleByUser = bundle.getBoolean(VISIBLE_BY_USER, true);
168 mShowSeconds = bundle.getBoolean(SHOW_SECONDS, false);
169 if (bundle.containsKey(VISIBILITY)) {
Hector Dearmanad4506a2018-11-09 18:11:07 +0000170 super.setVisibility(bundle.getInt(VISIBILITY));
felkachange6c03a02018-05-24 15:38:04 +0800171 }
172 }
173
174 @Override
Joe Onorato263700d2010-05-14 11:54:53 -0700175 protected void onAttachedToWindow() {
176 super.onAttachedToWindow();
177
178 if (!mAttached) {
179 mAttached = true;
180 IntentFilter filter = new IntentFilter();
181
182 filter.addAction(Intent.ACTION_TIME_TICK);
183 filter.addAction(Intent.ACTION_TIME_CHANGED);
184 filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
185 filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
Daniel Sandlerce4f5e52012-11-19 14:47:18 -0500186 filter.addAction(Intent.ACTION_USER_SWITCHED);
Joe Onorato263700d2010-05-14 11:54:53 -0700187
Fabian Kozynski5ca7a512019-10-16 19:56:11 +0000188 mBroadcastDispatcher.registerReceiver(mIntentReceiver, filter,
189 Dependency.get(Dependency.TIME_TICK_HANDLER), UserHandle.ALL);
Jason Monkde850bb2017-02-01 19:26:30 -0500190 Dependency.get(TunerService.class).addTunable(this, CLOCK_SECONDS,
Jason Monk3e189872016-01-12 09:10:34 -0500191 StatusBarIconController.ICON_BLACKLIST);
Dave Mankoffbcaca8a2019-10-31 18:04:08 -0400192 mCommandQueue.addCallback(this);
Jason Monke5b770e2017-03-03 21:49:29 -0500193 if (mShowDark) {
194 Dependency.get(DarkIconDispatcher.class).addDarkReceiver(this);
195 }
Lucas Dupin4eae8812018-04-09 22:44:17 -0700196 mCurrentUserTracker.startTracking();
197 mCurrentUserId = mCurrentUserTracker.getCurrentUserId();
Joe Onorato263700d2010-05-14 11:54:53 -0700198 }
199
200 // NOTE: It's safe to do these after registering the receiver since the receiver always runs
201 // in the main thread, therefore the receiver can't run before this method returns.
202
203 // The time zone may have changed while the receiver wasn't registered, so update the Time
204 mCalendar = Calendar.getInstance(TimeZone.getDefault());
205
206 // Make sure we update to the current time
207 updateClock();
Hector Dearmanad4506a2018-11-09 18:11:07 +0000208 updateClockVisibility();
Jason Monkfe7c91b2015-08-13 15:42:28 -0400209 updateShowSeconds();
Joe Onorato263700d2010-05-14 11:54:53 -0700210 }
211
212 @Override
213 protected void onDetachedFromWindow() {
214 super.onDetachedFromWindow();
215 if (mAttached) {
Fabian Kozynski5ca7a512019-10-16 19:56:11 +0000216 mBroadcastDispatcher.unregisterReceiver(mIntentReceiver);
Joe Onorato263700d2010-05-14 11:54:53 -0700217 mAttached = false;
Jason Monkde850bb2017-02-01 19:26:30 -0500218 Dependency.get(TunerService.class).removeTunable(this);
Dave Mankoffbcaca8a2019-10-31 18:04:08 -0400219 mCommandQueue.removeCallback(this);
Jason Monke5b770e2017-03-03 21:49:29 -0500220 if (mShowDark) {
221 Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(this);
222 }
Lucas Dupin4eae8812018-04-09 22:44:17 -0700223 mCurrentUserTracker.stopTracking();
Joe Onorato263700d2010-05-14 11:54:53 -0700224 }
225 }
226
227 private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
228 @Override
229 public void onReceive(Context context, Intent intent) {
230 String action = intent.getAction();
231 if (action.equals(Intent.ACTION_TIMEZONE_CHANGED)) {
232 String tz = intent.getStringExtra("time-zone");
Jason Monkcd26af72017-01-11 14:32:58 -0500233 getHandler().post(() -> {
234 mCalendar = Calendar.getInstance(TimeZone.getTimeZone(tz));
235 if (mClockFormat != null) {
236 mClockFormat.setTimeZone(mCalendar.getTimeZone());
237 }
238 });
Daniel Sandler43b23c62012-11-29 11:35:02 -0500239 } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
240 final Locale newLocale = getResources().getConfiguration().locale;
Jason Monkcd26af72017-01-11 14:32:58 -0500241 getHandler().post(() -> {
242 if (!newLocale.equals(mLocale)) {
243 mLocale = newLocale;
244 mClockFormatString = ""; // force refresh
245 }
246 });
Joe Onorato263700d2010-05-14 11:54:53 -0700247 }
Jason Monkcd26af72017-01-11 14:32:58 -0500248 getHandler().post(() -> updateClock());
Joe Onorato263700d2010-05-14 11:54:53 -0700249 }
250 };
251
Hector Dearmanad4506a2018-11-09 18:11:07 +0000252 @Override
253 public void setVisibility(int visibility) {
254 if (visibility == View.VISIBLE && !shouldBeVisible()) {
255 return;
256 }
257
258 super.setVisibility(visibility);
259 }
260
Jason Monkaa573e92017-01-27 17:00:29 -0500261 public void setClockVisibleByUser(boolean visible) {
262 mClockVisibleByUser = visible;
263 updateClockVisibility();
264 }
265
266 public void setClockVisibilityByPolicy(boolean visible) {
267 mClockVisibleByPolicy = visible;
268 updateClockVisibility();
269 }
270
Hector Dearmanad4506a2018-11-09 18:11:07 +0000271 private boolean shouldBeVisible() {
272 return mClockVisibleByPolicy && mClockVisibleByUser;
273 }
274
Jason Monkaa573e92017-01-27 17:00:29 -0500275 private void updateClockVisibility() {
Hector Dearmanad4506a2018-11-09 18:11:07 +0000276 boolean visible = shouldBeVisible();
Jason Monkf8c2f7b2017-09-06 09:22:29 -0400277 int visibility = visible ? View.VISIBLE : View.GONE;
Hector Dearmanad4506a2018-11-09 18:11:07 +0000278 super.setVisibility(visibility);
Jason Monkaa573e92017-01-27 17:00:29 -0500279 }
280
Joe Onorato263700d2010-05-14 11:54:53 -0700281 final void updateClock() {
John Spurlock3c875662013-08-31 15:07:25 -0400282 if (mDemoMode) return;
Joe Onorato263700d2010-05-14 11:54:53 -0700283 mCalendar.setTimeInMillis(System.currentTimeMillis());
284 setText(getSmallTime());
Adrian Roos70dcf832016-04-20 15:51:42 -0700285 setContentDescription(mContentDescriptionFormat.format(mCalendar.getTime()));
Joe Onorato263700d2010-05-14 11:54:53 -0700286 }
287
Jason Monkfe7c91b2015-08-13 15:42:28 -0400288 @Override
289 public void onTuningChanged(String key, String newValue) {
Jason Monk3e189872016-01-12 09:10:34 -0500290 if (CLOCK_SECONDS.equals(key)) {
Henrik Baarda2c86e82019-03-14 12:23:04 +0100291 mShowSeconds = TunerService.parseIntegerSwitch(newValue, false);
Jason Monk3e189872016-01-12 09:10:34 -0500292 updateShowSeconds();
Jason Monkaa573e92017-01-27 17:00:29 -0500293 } else {
294 setClockVisibleByUser(!StatusBarIconController.getIconBlacklist(newValue)
295 .contains("clock"));
296 updateClockVisibility();
Jason Monk3e189872016-01-12 09:10:34 -0500297 }
Jason Monkfe7c91b2015-08-13 15:42:28 -0400298 }
299
Jason Monkaa573e92017-01-27 17:00:29 -0500300 @Override
Charles Chenf3d295c2018-11-30 18:15:21 +0800301 public void disable(int displayId, int state1, int state2, boolean animate) {
302 if (displayId != getDisplay().getDisplayId()) {
303 return;
304 }
Jason Monkaa573e92017-01-27 17:00:29 -0500305 boolean clockVisibleByPolicy = (state1 & StatusBarManager.DISABLE_CLOCK) == 0;
306 if (clockVisibleByPolicy != mClockVisibleByPolicy) {
307 setClockVisibilityByPolicy(clockVisibleByPolicy);
308 }
309 }
310
311 @Override
312 public void onDarkChanged(Rect area, float darkIntensity, int tint) {
Rohan Shahcc3d1d82018-03-30 21:24:17 +0000313 mNonAdaptedColor = DarkIconDispatcher.getTint(area, this, tint);
314 if (!mUseWallpaperTextColor) {
315 setTextColor(mNonAdaptedColor);
316 }
Jason Monkaa573e92017-01-27 17:00:29 -0500317 }
318
319 @Override
320 public void onDensityOrFontScaleChanged() {
321 FontSizeUtils.updateFontSize(this, R.dimen.status_bar_clock_size);
322 setPaddingRelative(
323 mContext.getResources().getDimensionPixelSize(
324 R.dimen.status_bar_clock_starting_padding),
325 0,
326 mContext.getResources().getDimensionPixelSize(
327 R.dimen.status_bar_clock_end_padding),
328 0);
329 }
330
Rohan Shahcc3d1d82018-03-30 21:24:17 +0000331 /**
332 * Sets whether the clock uses the wallpaperTextColor. If we're not using it, we'll revert back
333 * to dark-mode-based/tinted colors.
334 *
335 * @param shouldUseWallpaperTextColor whether we should use wallpaperTextColor for text color
336 */
337 public void useWallpaperTextColor(boolean shouldUseWallpaperTextColor) {
338 if (shouldUseWallpaperTextColor == mUseWallpaperTextColor) {
339 return;
340 }
341 mUseWallpaperTextColor = shouldUseWallpaperTextColor;
342
343 if (mUseWallpaperTextColor) {
344 setTextColor(Utils.getColorAttr(mContext, R.attr.wallpaperTextColor));
345 } else {
346 setTextColor(mNonAdaptedColor);
347 }
348 }
349
Jason Monkfe7c91b2015-08-13 15:42:28 -0400350 private void updateShowSeconds() {
351 if (mShowSeconds) {
352 // Wait until we have a display to start trying to show seconds.
353 if (mSecondsHandler == null && getDisplay() != null) {
354 mSecondsHandler = new Handler();
355 if (getDisplay().getState() == Display.STATE_ON) {
356 mSecondsHandler.postAtTime(mSecondTick,
357 SystemClock.uptimeMillis() / 1000 * 1000 + 1000);
358 }
359 IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
360 filter.addAction(Intent.ACTION_SCREEN_ON);
Fabian Kozynskiff5e91f2019-09-24 15:38:08 -0400361 mBroadcastDispatcher.registerReceiver(mScreenReceiver, filter);
Jason Monkfe7c91b2015-08-13 15:42:28 -0400362 }
363 } else {
364 if (mSecondsHandler != null) {
Fabian Kozynskiff5e91f2019-09-24 15:38:08 -0400365 mBroadcastDispatcher.unregisterReceiver(mScreenReceiver);
Jason Monkfe7c91b2015-08-13 15:42:28 -0400366 mSecondsHandler.removeCallbacks(mSecondTick);
367 mSecondsHandler = null;
368 updateClock();
369 }
370 }
371 }
372
Joe Onorato263700d2010-05-14 11:54:53 -0700373 private final CharSequence getSmallTime() {
374 Context context = getContext();
Lucas Dupin4eae8812018-04-09 22:44:17 -0700375 boolean is24 = DateFormat.is24HourFormat(context, mCurrentUserId);
Elliott Hughes4caba612013-01-14 15:48:27 -0800376 LocaleData d = LocaleData.get(context.getResources().getConfiguration().locale);
Joe Onorato263700d2010-05-14 11:54:53 -0700377
378 final char MAGIC1 = '\uEF00';
379 final char MAGIC2 = '\uEF01';
380
381 SimpleDateFormat sdf;
Jason Monkfe7c91b2015-08-13 15:42:28 -0400382 String format = mShowSeconds
383 ? is24 ? d.timeFormat_Hms : d.timeFormat_hms
384 : is24 ? d.timeFormat_Hm : d.timeFormat_hm;
Joe Onorato263700d2010-05-14 11:54:53 -0700385 if (!format.equals(mClockFormatString)) {
Adrian Roos70dcf832016-04-20 15:51:42 -0700386 mContentDescriptionFormat = new SimpleDateFormat(format);
Joe Onorato263700d2010-05-14 11:54:53 -0700387 /*
388 * Search for an unquoted "a" in the format string, so we can
389 * add dummy characters around it to let us find it again after
390 * formatting and change its size.
391 */
Jorim Jaggi740beb52014-05-11 18:53:19 +0200392 if (mAmPmStyle != AM_PM_STYLE_NORMAL) {
Daniel Sandler87937db2010-05-27 13:44:11 -0400393 int a = -1;
394 boolean quoted = false;
395 for (int i = 0; i < format.length(); i++) {
396 char c = format.charAt(i);
Joe Onorato263700d2010-05-14 11:54:53 -0700397
Daniel Sandler87937db2010-05-27 13:44:11 -0400398 if (c == '\'') {
399 quoted = !quoted;
400 }
401 if (!quoted && c == 'a') {
402 a = i;
403 break;
404 }
Joe Onorato263700d2010-05-14 11:54:53 -0700405 }
406
Daniel Sandler87937db2010-05-27 13:44:11 -0400407 if (a >= 0) {
408 // Move a back so any whitespace before AM/PM is also in the alternate size.
409 final int b = a;
410 while (a > 0 && Character.isWhitespace(format.charAt(a-1))) {
411 a--;
412 }
413 format = format.substring(0, a) + MAGIC1 + format.substring(a, b)
Joe Onorato263700d2010-05-14 11:54:53 -0700414 + "a" + MAGIC2 + format.substring(b + 1);
Daniel Sandler87937db2010-05-27 13:44:11 -0400415 }
Joe Onorato263700d2010-05-14 11:54:53 -0700416 }
Joe Onorato263700d2010-05-14 11:54:53 -0700417 mClockFormat = sdf = new SimpleDateFormat(format);
418 mClockFormatString = format;
419 } else {
420 sdf = mClockFormat;
421 }
422 String result = sdf.format(mCalendar.getTime());
423
Jorim Jaggi740beb52014-05-11 18:53:19 +0200424 if (mAmPmStyle != AM_PM_STYLE_NORMAL) {
Daniel Sandler87937db2010-05-27 13:44:11 -0400425 int magic1 = result.indexOf(MAGIC1);
426 int magic2 = result.indexOf(MAGIC2);
427 if (magic1 >= 0 && magic2 > magic1) {
428 SpannableStringBuilder formatted = new SpannableStringBuilder(result);
Jorim Jaggi740beb52014-05-11 18:53:19 +0200429 if (mAmPmStyle == AM_PM_STYLE_GONE) {
Daniel Sandler87937db2010-05-27 13:44:11 -0400430 formatted.delete(magic1, magic2+1);
431 } else {
Jorim Jaggi740beb52014-05-11 18:53:19 +0200432 if (mAmPmStyle == AM_PM_STYLE_SMALL) {
Daniel Sandler87937db2010-05-27 13:44:11 -0400433 CharacterStyle style = new RelativeSizeSpan(0.7f);
434 formatted.setSpan(style, magic1, magic2,
435 Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
436 }
437 formatted.delete(magic2, magic2 + 1);
438 formatted.delete(magic1, magic1 + 1);
439 }
440 return formatted;
441 }
Joe Onorato263700d2010-05-14 11:54:53 -0700442 }
John Spurlock209bede2013-07-17 12:23:27 -0400443
Daniel Sandler87937db2010-05-27 13:44:11 -0400444 return result;
445
Joe Onorato263700d2010-05-14 11:54:53 -0700446 }
John Spurlock3c875662013-08-31 15:07:25 -0400447
448 private boolean mDemoMode;
449
450 @Override
451 public void dispatchDemoCommand(String command, Bundle args) {
452 if (!mDemoMode && command.equals(COMMAND_ENTER)) {
453 mDemoMode = true;
454 } else if (mDemoMode && command.equals(COMMAND_EXIT)) {
455 mDemoMode = false;
456 updateClock();
457 } else if (mDemoMode && command.equals(COMMAND_CLOCK)) {
458 String millis = args.getString("millis");
459 String hhmm = args.getString("hhmm");
460 if (millis != null) {
461 mCalendar.setTimeInMillis(Long.parseLong(millis));
462 } else if (hhmm != null && hhmm.length() == 4) {
463 int hh = Integer.parseInt(hhmm.substring(0, 2));
464 int mm = Integer.parseInt(hhmm.substring(2));
Lucas Dupin4eae8812018-04-09 22:44:17 -0700465 boolean is24 = DateFormat.is24HourFormat(getContext(), mCurrentUserId);
Julia Reynoldsc11d8382015-07-16 14:40:37 -0400466 if (is24) {
467 mCalendar.set(Calendar.HOUR_OF_DAY, hh);
468 } else {
469 mCalendar.set(Calendar.HOUR, hh);
470 }
John Spurlock3c875662013-08-31 15:07:25 -0400471 mCalendar.set(Calendar.MINUTE, mm);
472 }
473 setText(getSmallTime());
Adrian Roos70dcf832016-04-20 15:51:42 -0700474 setContentDescription(mContentDescriptionFormat.format(mCalendar.getTime()));
John Spurlock3c875662013-08-31 15:07:25 -0400475 }
476 }
Jason Monkfe7c91b2015-08-13 15:42:28 -0400477
478 private final BroadcastReceiver mScreenReceiver = new BroadcastReceiver() {
479 @Override
480 public void onReceive(Context context, Intent intent) {
481 String action = intent.getAction();
482 if (Intent.ACTION_SCREEN_OFF.equals(action)) {
483 if (mSecondsHandler != null) {
484 mSecondsHandler.removeCallbacks(mSecondTick);
485 }
486 } else if (Intent.ACTION_SCREEN_ON.equals(action)) {
487 if (mSecondsHandler != null) {
488 mSecondsHandler.postAtTime(mSecondTick,
489 SystemClock.uptimeMillis() / 1000 * 1000 + 1000);
490 }
491 }
492 }
493 };
494
495 private final Runnable mSecondTick = new Runnable() {
496 @Override
497 public void run() {
498 if (mCalendar != null) {
499 updateClock();
500 }
501 mSecondsHandler.postAtTime(this, SystemClock.uptimeMillis() / 1000 * 1000 + 1000);
502 }
503 };
Joe Onorato263700d2010-05-14 11:54:53 -0700504}
505