1/* 2 * Copyright (C) 2008 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 17package com.android.internal.widget; 18 19import com.android.internal.R; 20 21import android.content.BroadcastReceiver; 22import android.content.Context; 23import android.content.Intent; 24import android.content.IntentFilter; 25import android.database.ContentObserver; 26import android.graphics.Typeface; 27import android.os.Handler; 28import android.provider.Settings; 29import android.text.format.DateFormat; 30import android.util.AttributeSet; 31import android.view.View; 32import android.widget.LinearLayout; 33import android.widget.TextView; 34 35import java.lang.ref.WeakReference; 36import java.text.DateFormatSymbols; 37import java.util.Calendar; 38 39/** 40 * Displays the time 41 */ 42public class DigitalClock extends LinearLayout { 43 44 private final static String M12 = "h:mm"; 45 private final static String M24 = "kk:mm"; 46 47 private Calendar mCalendar; 48 private String mFormat; 49 private TextView mTimeDisplay; 50 private AmPm mAmPm; 51 private ContentObserver mFormatChangeObserver; 52 private int mAttached = 0; // for debugging - tells us whether attach/detach is unbalanced 53 54 /* called by system on minute ticks */ 55 private final Handler mHandler = new Handler(); 56 private BroadcastReceiver mIntentReceiver; 57 58 private static class TimeChangedReceiver extends BroadcastReceiver { 59 private WeakReference<DigitalClock> mClock; 60 private Context mContext; 61 62 public TimeChangedReceiver(DigitalClock clock) { 63 mClock = new WeakReference<DigitalClock>(clock); 64 mContext = clock.getContext(); 65 } 66 67 @Override 68 public void onReceive(Context context, Intent intent) { 69 // Post a runnable to avoid blocking the broadcast. 70 final boolean timezoneChanged = 71 intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED); 72 final DigitalClock clock = mClock.get(); 73 if (clock != null) { 74 clock.mHandler.post(new Runnable() { 75 public void run() { 76 if (timezoneChanged) { 77 clock.mCalendar = Calendar.getInstance(); 78 } 79 clock.updateTime(); 80 } 81 }); 82 } else { 83 try { 84 mContext.unregisterReceiver(this); 85 } catch (RuntimeException e) { 86 // Shouldn't happen 87 } 88 } 89 } 90 }; 91 92 static class AmPm { 93 private TextView mAmPm; 94 private String mAmString, mPmString; 95 96 AmPm(View parent, Typeface tf) { 97 mAmPm = (TextView) parent.findViewById(R.id.am_pm); 98 if (tf != null) { 99 mAmPm.setTypeface(tf); 100 } 101 102 String[] ampm = new DateFormatSymbols().getAmPmStrings(); 103 mAmString = ampm[0]; 104 mPmString = ampm[1]; 105 } 106 107 void setShowAmPm(boolean show) { 108 mAmPm.setVisibility(show ? View.VISIBLE : View.GONE); 109 } 110 111 void setIsMorning(boolean isMorning) { 112 mAmPm.setText(isMorning ? mAmString : mPmString); 113 } 114 } 115 116 private static class FormatChangeObserver extends ContentObserver { 117 private WeakReference<DigitalClock> mClock; 118 private Context mContext; 119 public FormatChangeObserver(DigitalClock clock) { 120 super(new Handler()); 121 mClock = new WeakReference<DigitalClock>(clock); 122 mContext = clock.getContext(); 123 } 124 @Override 125 public void onChange(boolean selfChange) { 126 DigitalClock digitalClock = mClock.get(); 127 if (digitalClock != null) { 128 digitalClock.setDateFormat(); 129 digitalClock.updateTime(); 130 } else { 131 try { 132 mContext.getContentResolver().unregisterContentObserver(this); 133 } catch (RuntimeException e) { 134 // Shouldn't happen 135 } 136 } 137 } 138 } 139 140 public DigitalClock(Context context) { 141 this(context, null); 142 } 143 144 public DigitalClock(Context context, AttributeSet attrs) { 145 super(context, attrs); 146 } 147 148 @Override 149 protected void onFinishInflate() { 150 super.onFinishInflate(); 151 152 mTimeDisplay = (TextView) findViewById(R.id.timeDisplay); 153 mTimeDisplay.setTypeface(Typeface.createFromFile("/system/fonts/Clockopia.ttf")); 154 mAmPm = new AmPm(this, Typeface.createFromFile("/system/fonts/DroidSans-Bold.ttf")); 155 mCalendar = Calendar.getInstance(); 156 157 setDateFormat(); 158 } 159 160 @Override 161 protected void onAttachedToWindow() { 162 super.onAttachedToWindow(); 163 164 mAttached++; 165 166 /* monitor time ticks, time changed, timezone */ 167 if (mIntentReceiver == null) { 168 mIntentReceiver = new TimeChangedReceiver(this); 169 IntentFilter filter = new IntentFilter(); 170 filter.addAction(Intent.ACTION_TIME_TICK); 171 filter.addAction(Intent.ACTION_TIME_CHANGED); 172 filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); 173 mContext.registerReceiver(mIntentReceiver, filter); 174 } 175 176 /* monitor 12/24-hour display preference */ 177 if (mFormatChangeObserver == null) { 178 mFormatChangeObserver = new FormatChangeObserver(this); 179 mContext.getContentResolver().registerContentObserver( 180 Settings.System.CONTENT_URI, true, mFormatChangeObserver); 181 } 182 183 updateTime(); 184 } 185 186 @Override 187 protected void onDetachedFromWindow() { 188 super.onDetachedFromWindow(); 189 190 mAttached--; 191 192 if (mIntentReceiver != null) { 193 mContext.unregisterReceiver(mIntentReceiver); 194 } 195 if (mFormatChangeObserver != null) { 196 mContext.getContentResolver().unregisterContentObserver( 197 mFormatChangeObserver); 198 } 199 200 mFormatChangeObserver = null; 201 mIntentReceiver = null; 202 } 203 204 void updateTime(Calendar c) { 205 mCalendar = c; 206 updateTime(); 207 } 208 209 private void updateTime() { 210 mCalendar.setTimeInMillis(System.currentTimeMillis()); 211 212 CharSequence newTime = DateFormat.format(mFormat, mCalendar); 213 mTimeDisplay.setText(newTime); 214 mAmPm.setIsMorning(mCalendar.get(Calendar.AM_PM) == 0); 215 } 216 217 private void setDateFormat() { 218 mFormat = android.text.format.DateFormat.is24HourFormat(getContext()) 219 ? M24 : M12; 220 mAmPm.setShowAmPm(mFormat.equals(M12)); 221 } 222} 223