Clock
diff --git a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/Clock.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/Clock.java
new file mode 100644
index 0000000..8ad1fb6
--- /dev/null
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/Clock.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.policy.statusbar.phone;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.BroadcastReceiver;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.text.format.DateFormat;
+import android.text.style.RelativeSizeSpan;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.TextView;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.TimeZone;
+
+import com.android.internal.R;
+
+/**
+ * This widget display an analogic clock with two hands for hours and
+ * minutes.
+ */
+public class Clock extends TextView {
+    private final Handler mHandler = new Handler();
+    private boolean mAttached;
+    private Calendar mCalendar;
+    private String mClockFormatString;
+    private SimpleDateFormat mClockFormat;
+
+    public Clock(Context context) {
+        this(context, null);
+    }
+
+    public Clock(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public Clock(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+
+        if (!mAttached) {
+            mAttached = true;
+            IntentFilter filter = new IntentFilter();
+
+            filter.addAction(Intent.ACTION_TIME_TICK);
+            filter.addAction(Intent.ACTION_TIME_CHANGED);
+            filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
+            filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
+
+            getContext().registerReceiver(mIntentReceiver, filter, null, mHandler);
+        }
+
+        // NOTE: It's safe to do these after registering the receiver since the receiver always runs
+        // in the main thread, therefore the receiver can't run before this method returns.
+
+        // The time zone may have changed while the receiver wasn't registered, so update the Time
+        mCalendar = Calendar.getInstance(TimeZone.getDefault());
+
+        // Make sure we update to the current time
+        updateClock();
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        if (mAttached) {
+            getContext().unregisterReceiver(mIntentReceiver);
+            mAttached = false;
+        }
+    }
+
+    private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(Intent.ACTION_TIMEZONE_CHANGED)) {
+                String tz = intent.getStringExtra("time-zone");
+                mCalendar = Calendar.getInstance(TimeZone.getTimeZone(tz));
+                if (mClockFormat != null) {
+                    mClockFormat.setTimeZone(mCalendar.getTimeZone());
+                }
+            }
+            updateClock();
+        }
+    };
+
+    final void updateClock() {
+        mCalendar.setTimeInMillis(System.currentTimeMillis());
+        setText(getSmallTime());
+    }
+
+    private final CharSequence getSmallTime() {
+        Context context = getContext();
+        boolean b24 = DateFormat.is24HourFormat(context);
+        int res;
+
+        if (b24) {
+            res = R.string.twenty_four_hour_time_format;
+        } else {
+            res = R.string.twelve_hour_time_format;
+        }
+
+        final char MAGIC1 = '\uEF00';
+        final char MAGIC2 = '\uEF01';
+
+        SimpleDateFormat sdf;
+        String format = context.getString(res);
+        if (!format.equals(mClockFormatString)) {
+            /*
+             * Search for an unquoted "a" in the format string, so we can
+             * add dummy characters around it to let us find it again after
+             * formatting and change its size.
+             */
+            int a = -1;
+            boolean quoted = false;
+            for (int i = 0; i < format.length(); i++) {
+                char c = format.charAt(i);
+
+                if (c == '\'') {
+                    quoted = !quoted;
+                }
+
+                if (!quoted && c == 'a') {
+                    a = i;
+                    break;
+                }
+            }
+
+            if (a >= 0) {
+                // Move a back so any whitespace before the AM/PM is also in the alternate size.
+                final int b = a;
+                while (a > 0 && Character.isWhitespace(format.charAt(a-1))) {
+                    a--;
+                }
+                format = format.substring(0, a) + MAGIC1 + format.substring(a, b)
+                        + "a" + MAGIC2 + format.substring(b + 1);
+            }
+
+            mClockFormat = sdf = new SimpleDateFormat(format);
+            mClockFormatString = format;
+        } else {
+            sdf = mClockFormat;
+        }
+        String result = sdf.format(mCalendar.getTime());
+
+        int magic1 = result.indexOf(MAGIC1);
+        int magic2 = result.indexOf(MAGIC2);
+
+        if (magic1 >= 0 && magic2 > magic1) {
+            SpannableStringBuilder formatted = new SpannableStringBuilder(result);
+
+            formatted.setSpan(new RelativeSizeSpan(0.7f), magic1, magic2,
+                              Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
+
+            formatted.delete(magic2, magic2 + 1);
+            formatted.delete(magic1, magic1 + 1);
+
+            return formatted;
+        } else {
+            return result;
+        }
+    }
+}
+