Home | History | Annotate | Download | only in widget
      1 /*
      2  * Copyright (C) 2010 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 
     17 package android.widget;
     18 
     19 import android.content.Context;
     20 import android.content.Intent;
     21 import android.content.IntentFilter;
     22 import android.content.BroadcastReceiver;
     23 import android.database.ContentObserver;
     24 import android.net.Uri;
     25 import android.os.Handler;
     26 import android.text.format.Time;
     27 import android.util.AttributeSet;
     28 import android.util.Log;
     29 import android.provider.Settings;
     30 import android.widget.TextView;
     31 import android.widget.RemoteViews.RemoteView;
     32 
     33 import java.text.DateFormat;
     34 import java.text.SimpleDateFormat;
     35 import java.util.Date;
     36 
     37 //
     38 // TODO
     39 // - listen for the next threshold time to update the view.
     40 // - listen for date format pref changed
     41 // - put the AM/PM in a smaller font
     42 //
     43 
     44 /**
     45  * Displays a given time in a convenient human-readable foramt.
     46  *
     47  * @hide
     48  */
     49 @RemoteView
     50 public class DateTimeView extends TextView {
     51     private static final String TAG = "DateTimeView";
     52 
     53     private static final long TWELVE_HOURS_IN_MINUTES = 12 * 60;
     54     private static final long TWENTY_FOUR_HOURS_IN_MILLIS = 24 * 60 * 60 * 1000;
     55 
     56     private static final int SHOW_TIME = 0;
     57     private static final int SHOW_MONTH_DAY_YEAR = 1;
     58 
     59     Date mTime;
     60     long mTimeMillis;
     61 
     62     int mLastDisplay = -1;
     63     DateFormat mLastFormat;
     64 
     65     private boolean mAttachedToWindow;
     66     private long mUpdateTimeMillis;
     67 
     68     public DateTimeView(Context context) {
     69         super(context);
     70     }
     71 
     72     public DateTimeView(Context context, AttributeSet attrs) {
     73         super(context, attrs);
     74     }
     75 
     76     @Override
     77     protected void onAttachedToWindow() {
     78         super.onAttachedToWindow();
     79         registerReceivers();
     80         mAttachedToWindow = true;
     81     }
     82 
     83     @Override
     84     protected void onDetachedFromWindow() {
     85         super.onDetachedFromWindow();
     86         unregisterReceivers();
     87         mAttachedToWindow = false;
     88     }
     89 
     90     @android.view.RemotableViewMethod
     91     public void setTime(long time) {
     92         Time t = new Time();
     93         t.set(time);
     94         t.second = 0;
     95         mTimeMillis = t.toMillis(false);
     96         mTime = new Date(t.year-1900, t.month, t.monthDay, t.hour, t.minute, 0);
     97         update();
     98     }
     99 
    100     void update() {
    101         if (mTime == null) {
    102             return;
    103         }
    104 
    105         long start = System.nanoTime();
    106 
    107         int display;
    108         Date time = mTime;
    109 
    110         Time t = new Time();
    111         t.set(mTimeMillis);
    112         t.second = 0;
    113 
    114         t.hour -= 12;
    115         long twelveHoursBefore = t.toMillis(false);
    116         t.hour += 12;
    117         long twelveHoursAfter = t.toMillis(false);
    118         t.hour = 0;
    119         t.minute = 0;
    120         long midnightBefore = t.toMillis(false);
    121         t.monthDay++;
    122         long midnightAfter = t.toMillis(false);
    123 
    124         long nowMillis = System.currentTimeMillis();
    125         t.set(nowMillis);
    126         t.second = 0;
    127         nowMillis = t.normalize(false);
    128 
    129         // Choose the display mode
    130         choose_display: {
    131             if ((nowMillis >= midnightBefore && nowMillis < midnightAfter)
    132                     || (nowMillis >= twelveHoursBefore && nowMillis < twelveHoursAfter)) {
    133                 display = SHOW_TIME;
    134                 break choose_display;
    135             }
    136             // Else, show month day and year.
    137             display = SHOW_MONTH_DAY_YEAR;
    138             break choose_display;
    139         }
    140 
    141         // Choose the format
    142         DateFormat format;
    143         if (display == mLastDisplay && mLastFormat != null) {
    144             // use cached format
    145             format = mLastFormat;
    146         } else {
    147             switch (display) {
    148                 case SHOW_TIME:
    149                     format = getTimeFormat();
    150                     break;
    151                 case SHOW_MONTH_DAY_YEAR:
    152                     format = getDateFormat();
    153                     break;
    154                 default:
    155                     throw new RuntimeException("unknown display value: " + display);
    156             }
    157             mLastFormat = format;
    158         }
    159 
    160         // Set the text
    161         String text = format.format(mTime);
    162         setText(text);
    163 
    164         // Schedule the next update
    165         if (display == SHOW_TIME) {
    166             // Currently showing the time, update at the later of twelve hours after or midnight.
    167             mUpdateTimeMillis = twelveHoursAfter > midnightAfter ? twelveHoursAfter : midnightAfter;
    168         } else {
    169             // Currently showing the date
    170             if (mTimeMillis < nowMillis) {
    171                 // If the time is in the past, don't schedule an update
    172                 mUpdateTimeMillis = 0;
    173             } else {
    174                 // If hte time is in the future, schedule one at the earlier of twelve hours
    175                 // before or midnight before.
    176                 mUpdateTimeMillis = twelveHoursBefore < midnightBefore
    177                         ? twelveHoursBefore : midnightBefore;
    178             }
    179         }
    180         if (false) {
    181             Log.d(TAG, "update needed for '" + time + "' at '" + new Date(mUpdateTimeMillis)
    182                     + "' - text=" + text);
    183         }
    184 
    185         long finish = System.nanoTime();
    186     }
    187 
    188     private DateFormat getTimeFormat() {
    189         return android.text.format.DateFormat.getTimeFormat(getContext());
    190     }
    191 
    192     private DateFormat getDateFormat() {
    193         String format = Settings.System.getString(getContext().getContentResolver(),
    194                 Settings.System.DATE_FORMAT);
    195         if (format == null || "".equals(format)) {
    196             return DateFormat.getDateInstance(DateFormat.SHORT);
    197         } else {
    198             try {
    199                 return new SimpleDateFormat(format);
    200             } catch (IllegalArgumentException e) {
    201                 // If we tried to use a bad format string, fall back to a default.
    202                 return DateFormat.getDateInstance(DateFormat.SHORT);
    203             }
    204         }
    205     }
    206 
    207     private void registerReceivers() {
    208         Context context = getContext();
    209 
    210         IntentFilter filter = new IntentFilter();
    211         filter.addAction(Intent.ACTION_TIME_TICK);
    212         filter.addAction(Intent.ACTION_TIME_CHANGED);
    213         filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
    214         filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
    215         context.registerReceiver(mBroadcastReceiver, filter);
    216 
    217         Uri uri = Settings.System.getUriFor(Settings.System.DATE_FORMAT);
    218         context.getContentResolver().registerContentObserver(uri, true, mContentObserver);
    219     }
    220 
    221     private void unregisterReceivers() {
    222         Context context = getContext();
    223         context.unregisterReceiver(mBroadcastReceiver);
    224         context.getContentResolver().unregisterContentObserver(mContentObserver);
    225     }
    226 
    227     private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
    228         @Override
    229         public void onReceive(Context context, Intent intent) {
    230             String action = intent.getAction();
    231             if (Intent.ACTION_TIME_TICK.equals(action)) {
    232                 if (System.currentTimeMillis() < mUpdateTimeMillis) {
    233                     // The update() function takes a few milliseconds to run because of
    234                     // all of the time conversions it needs to do, so we can't do that
    235                     // every minute.
    236                     return;
    237                 }
    238             }
    239             // ACTION_TIME_CHANGED can also signal a change of 12/24 hr. format.
    240             mLastFormat = null;
    241             update();
    242         }
    243     };
    244 
    245     private ContentObserver mContentObserver = new ContentObserver(new Handler()) {
    246         @Override
    247         public void onChange(boolean selfChange) {
    248             mLastFormat = null;
    249             update();
    250         }
    251     };
    252 }
    253