Home | History | Annotate | Download | only in widget
      1 /*
      2  * Copyright (C) 2007 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.annotation.Widget;
     20 import android.content.Context;
     21 import android.os.Parcel;
     22 import android.os.Parcelable;
     23 import android.util.AttributeSet;
     24 import android.view.LayoutInflater;
     25 import android.view.View;
     26 import android.widget.NumberPicker;
     27 
     28 import com.android.internal.R;
     29 
     30 import java.text.DateFormatSymbols;
     31 import java.util.Calendar;
     32 
     33 /**
     34  * A view for selecting the time of day, in either 24 hour or AM/PM mode.
     35  *
     36  * The hour, each minute digit, and AM/PM (if applicable) can be conrolled by
     37  * vertical spinners.
     38  *
     39  * The hour can be entered by keyboard input.  Entering in two digit hours
     40  * can be accomplished by hitting two digits within a timeout of about a
     41  * second (e.g. '1' then '2' to select 12).
     42  *
     43  * The minutes can be entered by entering single digits.
     44  *
     45  * Under AM/PM mode, the user can hit 'a', 'A", 'p' or 'P' to pick.
     46  *
     47  * For a dialog using this view, see {@link android.app.TimePickerDialog}.
     48  */
     49 @Widget
     50 public class TimePicker extends FrameLayout {
     51 
     52     /**
     53      * A no-op callback used in the constructor to avoid null checks
     54      * later in the code.
     55      */
     56     private static final OnTimeChangedListener NO_OP_CHANGE_LISTENER = new OnTimeChangedListener() {
     57         public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
     58         }
     59     };
     60 
     61     // state
     62     private int mCurrentHour = 0; // 0-23
     63     private int mCurrentMinute = 0; // 0-59
     64     private Boolean mIs24HourView = false;
     65     private boolean mIsAm;
     66 
     67     // ui components
     68     private final NumberPicker mHourPicker;
     69     private final NumberPicker mMinutePicker;
     70     private final Button mAmPmButton;
     71     private final String mAmText;
     72     private final String mPmText;
     73 
     74     // callbacks
     75     private OnTimeChangedListener mOnTimeChangedListener;
     76 
     77     /**
     78      * The callback interface used to indicate the time has been adjusted.
     79      */
     80     public interface OnTimeChangedListener {
     81 
     82         /**
     83          * @param view The view associated with this listener.
     84          * @param hourOfDay The current hour.
     85          * @param minute The current minute.
     86          */
     87         void onTimeChanged(TimePicker view, int hourOfDay, int minute);
     88     }
     89 
     90     public TimePicker(Context context) {
     91         this(context, null);
     92     }
     93 
     94     public TimePicker(Context context, AttributeSet attrs) {
     95         this(context, attrs, 0);
     96     }
     97 
     98     public TimePicker(Context context, AttributeSet attrs, int defStyle) {
     99         super(context, attrs, defStyle);
    100 
    101         LayoutInflater inflater =
    102                 (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    103         inflater.inflate(R.layout.time_picker,
    104             this, // we are the parent
    105             true);
    106 
    107         // hour
    108         mHourPicker = (NumberPicker) findViewById(R.id.hour);
    109         mHourPicker.setOnChangeListener(new NumberPicker.OnChangedListener() {
    110             public void onChanged(NumberPicker spinner, int oldVal, int newVal) {
    111                 mCurrentHour = newVal;
    112                 if (!mIs24HourView) {
    113                     // adjust from [1-12] to [0-11] internally, with the times
    114                     // written "12:xx" being the start of the half-day
    115                     if (mCurrentHour == 12) {
    116                         mCurrentHour = 0;
    117                     }
    118                     if (!mIsAm) {
    119                         // PM means 12 hours later than nominal
    120                         mCurrentHour += 12;
    121                     }
    122                 }
    123                 onTimeChanged();
    124             }
    125         });
    126 
    127         // digits of minute
    128         mMinutePicker = (NumberPicker) findViewById(R.id.minute);
    129         mMinutePicker.setRange(0, 59);
    130         mMinutePicker.setSpeed(100);
    131         mMinutePicker.setFormatter(NumberPicker.TWO_DIGIT_FORMATTER);
    132         mMinutePicker.setOnChangeListener(new NumberPicker.OnChangedListener() {
    133             public void onChanged(NumberPicker spinner, int oldVal, int newVal) {
    134                 mCurrentMinute = newVal;
    135                 onTimeChanged();
    136             }
    137         });
    138 
    139         // am/pm
    140         mAmPmButton = (Button) findViewById(R.id.amPm);
    141 
    142         // now that the hour/minute picker objects have been initialized, set
    143         // the hour range properly based on the 12/24 hour display mode.
    144         configurePickerRanges();
    145 
    146         // initialize to current time
    147         Calendar cal = Calendar.getInstance();
    148         setOnTimeChangedListener(NO_OP_CHANGE_LISTENER);
    149 
    150         // by default we're not in 24 hour mode
    151         setCurrentHour(cal.get(Calendar.HOUR_OF_DAY));
    152         setCurrentMinute(cal.get(Calendar.MINUTE));
    153 
    154         mIsAm = (mCurrentHour < 12);
    155 
    156         /* Get the localized am/pm strings and use them in the spinner */
    157         DateFormatSymbols dfs = new DateFormatSymbols();
    158         String[] dfsAmPm = dfs.getAmPmStrings();
    159         mAmText = dfsAmPm[Calendar.AM];
    160         mPmText = dfsAmPm[Calendar.PM];
    161         mAmPmButton.setText(mIsAm ? mAmText : mPmText);
    162         mAmPmButton.setOnClickListener(new OnClickListener() {
    163             public void onClick(View v) {
    164                 requestFocus();
    165                 if (mIsAm) {
    166 
    167                     // Currently AM switching to PM
    168                     if (mCurrentHour < 12) {
    169                         mCurrentHour += 12;
    170                     }
    171                 } else {
    172 
    173                     // Currently PM switching to AM
    174                     if (mCurrentHour >= 12) {
    175                         mCurrentHour -= 12;
    176                     }
    177                 }
    178                 mIsAm = !mIsAm;
    179                 mAmPmButton.setText(mIsAm ? mAmText : mPmText);
    180                 onTimeChanged();
    181             }
    182         });
    183 
    184         if (!isEnabled()) {
    185             setEnabled(false);
    186         }
    187     }
    188 
    189     @Override
    190     public void setEnabled(boolean enabled) {
    191         super.setEnabled(enabled);
    192         mMinutePicker.setEnabled(enabled);
    193         mHourPicker.setEnabled(enabled);
    194         mAmPmButton.setEnabled(enabled);
    195     }
    196 
    197     /**
    198      * Used to save / restore state of time picker
    199      */
    200     private static class SavedState extends BaseSavedState {
    201 
    202         private final int mHour;
    203         private final int mMinute;
    204 
    205         private SavedState(Parcelable superState, int hour, int minute) {
    206             super(superState);
    207             mHour = hour;
    208             mMinute = minute;
    209         }
    210 
    211         private SavedState(Parcel in) {
    212             super(in);
    213             mHour = in.readInt();
    214             mMinute = in.readInt();
    215         }
    216 
    217         public int getHour() {
    218             return mHour;
    219         }
    220 
    221         public int getMinute() {
    222             return mMinute;
    223         }
    224 
    225         @Override
    226         public void writeToParcel(Parcel dest, int flags) {
    227             super.writeToParcel(dest, flags);
    228             dest.writeInt(mHour);
    229             dest.writeInt(mMinute);
    230         }
    231 
    232         public static final Parcelable.Creator<SavedState> CREATOR
    233                 = new Creator<SavedState>() {
    234             public SavedState createFromParcel(Parcel in) {
    235                 return new SavedState(in);
    236             }
    237 
    238             public SavedState[] newArray(int size) {
    239                 return new SavedState[size];
    240             }
    241         };
    242     }
    243 
    244     @Override
    245     protected Parcelable onSaveInstanceState() {
    246         Parcelable superState = super.onSaveInstanceState();
    247         return new SavedState(superState, mCurrentHour, mCurrentMinute);
    248     }
    249 
    250     @Override
    251     protected void onRestoreInstanceState(Parcelable state) {
    252         SavedState ss = (SavedState) state;
    253         super.onRestoreInstanceState(ss.getSuperState());
    254         setCurrentHour(ss.getHour());
    255         setCurrentMinute(ss.getMinute());
    256     }
    257 
    258     /**
    259      * Set the callback that indicates the time has been adjusted by the user.
    260      * @param onTimeChangedListener the callback, should not be null.
    261      */
    262     public void setOnTimeChangedListener(OnTimeChangedListener onTimeChangedListener) {
    263         mOnTimeChangedListener = onTimeChangedListener;
    264     }
    265 
    266     /**
    267      * @return The current hour (0-23).
    268      */
    269     public Integer getCurrentHour() {
    270         return mCurrentHour;
    271     }
    272 
    273     /**
    274      * Set the current hour.
    275      */
    276     public void setCurrentHour(Integer currentHour) {
    277         this.mCurrentHour = currentHour;
    278         updateHourDisplay();
    279     }
    280 
    281     /**
    282      * Set whether in 24 hour or AM/PM mode.
    283      * @param is24HourView True = 24 hour mode. False = AM/PM.
    284      */
    285     public void setIs24HourView(Boolean is24HourView) {
    286         if (mIs24HourView != is24HourView) {
    287             mIs24HourView = is24HourView;
    288             configurePickerRanges();
    289             updateHourDisplay();
    290         }
    291     }
    292 
    293     /**
    294      * @return true if this is in 24 hour view else false.
    295      */
    296     public boolean is24HourView() {
    297         return mIs24HourView;
    298     }
    299 
    300     /**
    301      * @return The current minute.
    302      */
    303     public Integer getCurrentMinute() {
    304         return mCurrentMinute;
    305     }
    306 
    307     /**
    308      * Set the current minute (0-59).
    309      */
    310     public void setCurrentMinute(Integer currentMinute) {
    311         this.mCurrentMinute = currentMinute;
    312         updateMinuteDisplay();
    313     }
    314 
    315     @Override
    316     public int getBaseline() {
    317         return mHourPicker.getBaseline();
    318     }
    319 
    320     /**
    321      * Set the state of the spinners appropriate to the current hour.
    322      */
    323     private void updateHourDisplay() {
    324         int currentHour = mCurrentHour;
    325         if (!mIs24HourView) {
    326             // convert [0,23] ordinal to wall clock display
    327             if (currentHour > 12) currentHour -= 12;
    328             else if (currentHour == 0) currentHour = 12;
    329         }
    330         mHourPicker.setCurrent(currentHour);
    331         mIsAm = mCurrentHour < 12;
    332         mAmPmButton.setText(mIsAm ? mAmText : mPmText);
    333         onTimeChanged();
    334     }
    335 
    336     private void configurePickerRanges() {
    337         if (mIs24HourView) {
    338             mHourPicker.setRange(0, 23);
    339             mHourPicker.setFormatter(NumberPicker.TWO_DIGIT_FORMATTER);
    340             mAmPmButton.setVisibility(View.GONE);
    341         } else {
    342             mHourPicker.setRange(1, 12);
    343             mHourPicker.setFormatter(null);
    344             mAmPmButton.setVisibility(View.VISIBLE);
    345         }
    346     }
    347 
    348     private void onTimeChanged() {
    349         mOnTimeChangedListener.onTimeChanged(this, getCurrentHour(), getCurrentMinute());
    350     }
    351 
    352     /**
    353      * Set the state of the spinners appropriate to the current minute.
    354      */
    355     private void updateMinuteDisplay() {
    356         mMinutePicker.setCurrent(mCurrentMinute);
    357         mOnTimeChangedListener.onTimeChanged(this, getCurrentHour(), getCurrentMinute());
    358     }
    359 }
    360