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