Home | History | Annotate | Download | only in deskclock
      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 
     17 package com.android.deskclock;
     18 
     19 import android.content.Context;
     20 import android.content.res.Resources;
     21 import android.os.Parcel;
     22 import android.os.Parcelable;
     23 import android.util.AttributeSet;
     24 import android.view.View;
     25 import android.widget.Button;
     26 import android.widget.TextView;
     27 
     28 import java.text.DateFormatSymbols;
     29 
     30 
     31 public class TimePicker extends TimerSetupView implements Button.OnClickListener{
     32 
     33     private TextView mAmPmLabel;
     34     private String[] mAmpm;
     35     private final String mNoAmPmLabel;
     36     private int mAmPmState;
     37     private Button mSetButton;
     38     private final boolean mIs24HoursMode = Alarms.get24HourMode(mContext);
     39 
     40     private static final int AMPM_NOT_SELECTED = 0;
     41     private static final int PM_SELECTED = 1;
     42     private static final int AM_SELECTED = 2;
     43     private static final int HOURS24_MODE = 3;
     44 
     45     private static final String TIME_PICKER_SAVED_BUFFER_POINTER =
     46             "timer_picker_saved_buffer_pointer";
     47     private static final String TIME_PICKER_SAVED_INPUT = "timer_picker_saved_input";
     48     private static final String TIME_PICKER_SAVED_AMPM = "timer_picker_saved_ampm";
     49 
     50     public TimePicker(Context context) {
     51         this(context, null);
     52     }
     53 
     54     public TimePicker(Context context, AttributeSet attrs) {
     55         super(context, attrs);
     56         mInputSize = 4;
     57         mNoAmPmLabel = context.getResources().getString(R.string.time_picker_ampm_label);
     58     }
     59 
     60     @Override
     61     protected int getLayoutId() {
     62         return R.layout.time_picker_view;
     63     }
     64 
     65     @Override
     66     protected void onFinishInflate() {
     67         super.onFinishInflate();
     68         Resources res = mContext.getResources();
     69         mAmpm = new DateFormatSymbols().getAmPmStrings();
     70 
     71         if (mIs24HoursMode) {
     72             mLeft.setText(res.getString(R.string.time_picker_00_label));
     73             mRight.setText(res.getString(R.string.time_picker_30_label));
     74         } else {
     75             mLeft.setText(mAmpm[0]);
     76             mRight.setText(mAmpm[1]);
     77         }
     78         mLeft.setOnClickListener(this);
     79         mRight.setOnClickListener(this);
     80         mAmPmLabel = (TextView)findViewById(R.id.ampm_label);
     81         mAmPmState = AMPM_NOT_SELECTED;
     82         updateKeypad();
     83     }
     84 
     85 
     86     @Override
     87     public void onClick(View v) {
     88         doOnClick(v);
     89     }
     90 
     91     @Override
     92     protected void doOnClick(View v) {
     93         Integer val = (Integer) v.getTag(R.id.numbers_key);
     94         // A number was pressed
     95         if (val != null) {
     96             addClickedNumber(val);
     97         } else if (v == mDelete) {
     98             // Pressing delete when AM or PM is selected, clears the AM/PM
     99             // selection
    100             if (!mIs24HoursMode && mAmPmState != AMPM_NOT_SELECTED) {
    101                 mAmPmState = AMPM_NOT_SELECTED;
    102             } else if (mInputPointer >= 0) {
    103                 for (int i = 0; i < mInputPointer; i++) {
    104                     mInput[i] = mInput[i + 1];
    105                 }
    106                 mInput[mInputPointer] = 0;
    107                 mInputPointer--;
    108             }
    109         } else if (v == mLeft) {
    110             onLeftClicked();
    111         } else if (v == mRight) {
    112             onRightClicked();
    113         }
    114         updateKeypad();
    115     }
    116 
    117     @Override
    118     public boolean onLongClick(View v) {
    119         if (v == mDelete) {
    120             mAmPmState = AMPM_NOT_SELECTED;
    121             reset();
    122             updateKeypad();
    123             return true;
    124         }
    125         return false;
    126     }
    127 
    128     private void updateKeypad() {
    129         // Update state of keypad
    130         // Set the correct AM/PM state
    131         showAmPm();
    132         // Update the time
    133         updateLeftRightButtons();
    134         updateTime();
    135         // enable/disable numeric keys according to the numbers entered already
    136         updateNumericKeys();
    137         // enable/disable the "set" key
    138         enableSetButton();
    139         // Update the backspace button
    140         updateDeleteButton();
    141 
    142     }
    143 
    144     // Update the time displayed in the picker:
    145     // Special cases:
    146     // 1. show "-" for digits not entered yet.
    147     // 2. hide the hours digits when it is not relevant
    148     @Override
    149     protected void updateTime() {
    150         // Put "-" in digits that was not entered by passing -1
    151         // Hide digit by passing -2 (for highest hours digit only);
    152 
    153         int  hours1 = -1;
    154         int time = getEnteredTime();
    155         // If the user entered 2 to 9 or 13 to 15 , there is no need for a 4th digit (AM/PM mode)
    156         // If the user entered 3 to 9 or 24 to 25 , there is no need for a 4th digit (24 hours mode)
    157         if (mInputPointer > -1) {
    158             // Test to see if the highest digit is 2 to 9 for AM/PM or 3 to 9 for 24 hours mode
    159             if (mInputPointer >= 0) {
    160                 int digit = mInput[mInputPointer];
    161                 if ((mIs24HoursMode && digit >= 3 && digit <= 9) ||
    162                         (!mIs24HoursMode && digit >= 2 && digit <= 9)) {
    163                     hours1 = -2;
    164                 }
    165             }
    166             // Test to see if the 2 highest digits are 13 to 15 for AM/PM or 24 to 25 for 24 hours
    167             // mode
    168             if (mInputPointer > 0 && mInputPointer < 3 && hours1 != -2) {
    169                 int digits = mInput[mInputPointer] * 10 + mInput[mInputPointer - 1];
    170                 if ((mIs24HoursMode && digits >= 24 && digits <= 25) ||
    171                         (!mIs24HoursMode && digits >= 13 && digits <= 15)) {
    172                     hours1 = -2;
    173                 }
    174             }
    175             // If we have a digit show it
    176             if (mInputPointer == 3) {
    177                 hours1 = mInput[3];
    178             }
    179         } else {
    180             hours1 = -1;
    181         }
    182         int hours2 = (mInputPointer < 2) ? -1 :  mInput[2];
    183         int minutes1 = (mInputPointer < 1) ? -1 :  mInput[1];
    184         int minutes2 = (mInputPointer < 0) ? -1 :  mInput[0];
    185         mEnteredTime.setTime(hours1, hours2, minutes1, minutes2, -1);
    186     }
    187 
    188     private void showAmPm() {
    189         if (!mIs24HoursMode) {
    190             switch(mAmPmState) {
    191                 case AMPM_NOT_SELECTED:
    192                     mAmPmLabel.setText(mNoAmPmLabel);
    193                     break;
    194                 case AM_SELECTED:
    195                     mAmPmLabel.setText(mAmpm[0]);
    196                     break;
    197                 case PM_SELECTED:
    198                     mAmPmLabel.setText(mAmpm[1]);
    199                     break;
    200                 default:
    201                     break;
    202             }
    203         } else {
    204             mAmPmLabel.setVisibility(View.INVISIBLE);
    205             mAmPmState = HOURS24_MODE;
    206         }
    207     }
    208 
    209     private void addClickedNumber(int val) {
    210         if (mInputPointer < mInputSize - 1) {
    211             for (int i = mInputPointer; i >= 0; i--) {
    212                 mInput[i + 1] = mInput[i];
    213             }
    214             mInputPointer++;
    215             mInput[0] = val;
    216         }
    217     }
    218 
    219     // Clicking on the bottom left button will add "00" to the time
    220     // In AM/PM mode is will also set the time to AM.
    221     private void onLeftClicked() {
    222         int time = getEnteredTime();
    223         if (!mIs24HoursMode) {
    224             if (canAddDigits()) {
    225                 addClickedNumber(0);
    226                 addClickedNumber(0);
    227             }
    228             mAmPmState = AM_SELECTED;
    229         } else if (canAddDigits()) {
    230                 addClickedNumber(0);
    231                 addClickedNumber(0);
    232         }
    233     }
    234 
    235     // Clicking on the bottom right button will add "00" to the time in AM/PM
    236     // mode and "30" is 24 hours mode.
    237     // In AM/PM mode is will also set the time to PM.
    238     private void onRightClicked() {
    239         int time = getEnteredTime();
    240         if (!mIs24HoursMode) {
    241             if (canAddDigits()) {
    242                 addClickedNumber(0);
    243                 addClickedNumber(0);
    244             }
    245             mAmPmState = PM_SELECTED;
    246         } else {
    247             if (canAddDigits()) {
    248                 addClickedNumber(3);
    249                 addClickedNumber(0);
    250             }
    251         }
    252     }
    253 
    254     // Checks if the user allowed to click on the left or right button that enters "00" or "30"
    255     private boolean canAddDigits() {
    256         int time = getEnteredTime();
    257         // For AM/PM mode , can add "00" if an hour between 1 and 12 was entered
    258         if (!mIs24HoursMode) {
    259             return (time >= 1 && time <= 12);
    260         }
    261         // For 24 hours mode , can add "00"/"30" if an hour between 0 and 23 was entered
    262         return (time >= 0 && time <= 23 && mInputPointer > -1 && mInputPointer < 2);
    263     }
    264 
    265 
    266     // Enable/disable keys in the numeric key pad according to the data entered
    267     private void updateNumericKeys() {
    268         int time = getEnteredTime();
    269         if (mIs24HoursMode) {
    270             if (mInputPointer >= 3) {
    271                 setKeyRange(-1);
    272             } else if (time == 0) {
    273                 if (mInputPointer == -1 || mInputPointer == 0 || mInputPointer == 2) {
    274                     setKeyRange(9);
    275                 } else if (mInputPointer == 1) {
    276                     setKeyRange(5);
    277                 } else {
    278                     setKeyRange(-1);
    279                 }
    280             } else if (time == 1) {
    281                 if (mInputPointer == 0 || mInputPointer == 2) {
    282                     setKeyRange(9);
    283                 } else  if (mInputPointer == 1) {
    284                     setKeyRange(5);
    285                 } else {
    286                     setKeyRange(-1);
    287                 }
    288             } else if (time == 2) {
    289                 if (mInputPointer == 2|| mInputPointer == 1) {
    290                     setKeyRange(9);
    291                 } else  if (mInputPointer == 0) {
    292                     setKeyRange(3);
    293                 } else {
    294                     setKeyRange(-1);
    295                 }
    296             } else if (time <= 5) {
    297                 setKeyRange(9);
    298             } else if (time <= 9) {
    299                 setKeyRange(5);
    300             } else if (time >= 10 && time <= 15) {
    301                 setKeyRange(9);
    302             } else if (time >= 16 && time <= 19) {
    303                 setKeyRange(5);
    304             } else if (time >= 20 && time <= 25) {
    305                 setKeyRange(9);
    306             } else if (time >= 26 && time <= 29) {
    307                 setKeyRange(-1);
    308             } else if (time >= 30 && time <= 35) {
    309                 setKeyRange(9);
    310             } else if (time >= 36 && time <= 39) {
    311                 setKeyRange(-1);
    312             } else if (time >= 40 && time <= 45) {
    313                 setKeyRange(9);
    314             } else if (time >= 46 && time <= 49) {
    315                 setKeyRange(-1);
    316             } else if (time >= 50 && time <= 55) {
    317                 setKeyRange(9);
    318             } else if (time >= 56 && time <= 59) {
    319                 setKeyRange(-1);
    320             } else if (time >= 60 && time <= 65) {
    321                 setKeyRange(9);
    322             } else if (time >= 70 && time <= 75) {
    323                 setKeyRange(9);
    324             } else if (time >= 80 && time <= 85) {
    325                 setKeyRange(9);
    326             } else if (time >= 90 && time <= 95) {
    327                 setKeyRange(9);
    328             } else if (time >= 100 && time <= 105) {
    329                 setKeyRange(9);
    330             } else if (time >= 106 && time <= 109) {
    331                 setKeyRange(-1);
    332             } else if (time >= 110 && time <= 115) {
    333                 setKeyRange(9);
    334             } else if (time >= 116 && time <= 119) {
    335                 setKeyRange(-1);
    336             } else if (time >= 120 && time <= 125) {
    337                 setKeyRange(9);
    338             } else if (time >= 126 && time <= 129) {
    339                 setKeyRange(-1);
    340             } else if (time >= 130 && time <= 135) {
    341                 setKeyRange(9);
    342             } else if (time >= 136 && time <= 139) {
    343                 setKeyRange(-1);
    344             } else if (time >= 140 && time <= 145) {
    345                 setKeyRange(9);
    346             } else if (time >= 146 && time <= 149) {
    347                 setKeyRange(-1);
    348             } else if (time >= 150 && time <= 155) {
    349                 setKeyRange(9);
    350             } else if (time >= 156 && time <= 159) {
    351                 setKeyRange(-1);
    352             } else if (time >= 160 && time <= 165) {
    353                 setKeyRange(9);
    354             } else if (time >= 166 && time <= 169) {
    355                 setKeyRange(-1);
    356             } else if (time >= 170 && time <= 175) {
    357                 setKeyRange(9);
    358             } else if (time >= 176 && time <= 179) {
    359                 setKeyRange(-1);
    360             } else if (time >= 180 && time <= 185) {
    361                 setKeyRange(9);
    362             } else if (time >= 186 && time <= 189) {
    363                 setKeyRange(-1);
    364             } else if (time >= 190 && time <= 195) {
    365                 setKeyRange(9);
    366             } else if (time >= 196 && time <= 199) {
    367                 setKeyRange(-1);
    368             } else if (time >= 200 && time <= 205) {
    369                 setKeyRange(9);
    370             } else if (time >= 206 && time <= 209) {
    371                 setKeyRange(-1);
    372             } else if (time >= 210 && time <= 215) {
    373                 setKeyRange(9);
    374             } else if (time >= 216 && time <= 219) {
    375                 setKeyRange(-1);
    376             } else if (time >= 220 && time <= 225) {
    377                 setKeyRange(9);
    378             } else if (time >= 226 && time <= 229) {
    379                 setKeyRange(-1);
    380             } else if (time >= 230 && time <= 235) {
    381                 setKeyRange(9);
    382             } else if (time >= 236) {
    383                 setKeyRange(-1);
    384             }
    385         } else {
    386             // Selecting AM/PM disabled the keypad
    387             if (mAmPmState != AMPM_NOT_SELECTED) {
    388                 setKeyRange(-1);
    389             } else if (time == 0) {
    390                 setKeyRange(9);
    391                 // If 0 was entered as the first digit in AM/PM mode, do not allow a second 0
    392         //        if (mInputPointer == 0) {
    393                     mNumbers[0].setEnabled(false);
    394           //      }
    395             } else if (time <= 9) {
    396                 setKeyRange(5);
    397             } else if (time <= 95) {
    398                 setKeyRange(9);
    399             } else if (time >= 100 && time <= 105) {
    400                 setKeyRange(9);
    401             } else if (time >= 106 && time <= 109) {
    402                 setKeyRange(-1);
    403             } else if (time >= 110 && time <= 115) {
    404                 setKeyRange(9);
    405             } else if (time >= 116 && time <= 119) {
    406                 setKeyRange(-1);
    407             } else if (time >= 120 && time <= 125) {
    408                 setKeyRange(9);
    409             } else if (time >= 126) {
    410                 setKeyRange(-1);
    411             }
    412         }
    413     }
    414 
    415     // Returns the time already entered in decimal representation. if time is H1 H2 : M1 M2
    416     // the value retured is H1*1000+H2*100+M1*10+M2
    417     private int getEnteredTime() {
    418         return  mInput[3] * 1000 + mInput[2] * 100 + mInput[1] * 10 + mInput[0];
    419     }
    420 
    421     // enables a range of numeric keys from zero to maxKey. The rest of the keys will be disabled
    422     private void setKeyRange(int maxKey) {
    423         for (int i = 0; i < mNumbers.length; i++) {
    424             mNumbers[i].setEnabled(i <= maxKey);
    425         }
    426     }
    427 
    428     private void updateLeftRightButtons() {
    429         int time = getEnteredTime();
    430         if (mIs24HoursMode) {
    431             boolean enable = canAddDigits();
    432             mLeft.setEnabled(enable);
    433             mRight.setEnabled(enable);
    434         } else {
    435             // You can use the AM/PM if time entered is 0 to 12 or it is 3 digits or more
    436             if ((time > 12 && time < 100) || time == 0 || mAmPmState != AMPM_NOT_SELECTED) {
    437                 mLeft.setEnabled(false);
    438                 mRight.setEnabled(false);
    439             } else {
    440                 mLeft.setEnabled(true);
    441                 mRight.setEnabled(true);
    442             }
    443         }
    444     }
    445 
    446     // Enable/disable the set key
    447     private void enableSetButton() {
    448         if (mSetButton == null) {
    449             return;
    450         }
    451 
    452         // Nothing entered - disable
    453         if (mInputPointer == -1) {
    454             mSetButton.setEnabled(false);
    455             return;
    456         }
    457         // If the user entered 3 digits or more but not 060 to 095
    458         // it is a legal time and the set key should be enabled.
    459         if (mIs24HoursMode) {
    460             int time = getEnteredTime();
    461             mSetButton.setEnabled(mInputPointer >= 2 && (time < 60 || time > 95));
    462         } else {
    463             // If AM/PM mode , enable the set button if AM/PM was selected
    464             mSetButton.setEnabled(mAmPmState != AMPM_NOT_SELECTED);
    465         }
    466     }
    467 
    468     public void setSetButton(Button b) {
    469         mSetButton = b;
    470         enableSetButton();
    471     }
    472 
    473     public int getHours() {
    474         int hours = mInput[3] * 10 + mInput[2];
    475         if (hours == 12) {
    476             switch (mAmPmState) {
    477                 case PM_SELECTED:
    478                     return 12;
    479                 case AM_SELECTED:
    480                     return 0;
    481                 case HOURS24_MODE:
    482                     return hours;
    483                 default:
    484                     break;
    485             }
    486         }
    487         return hours + (mAmPmState == PM_SELECTED ? 12 : 0);
    488     }
    489     public int getMinutes() {
    490         return mInput[1] * 10 + mInput[0];
    491     }
    492 
    493     @Override
    494     public Parcelable onSaveInstanceState () {
    495         final Parcelable parcel = super.onSaveInstanceState();
    496         final SavedState state = new SavedState(parcel);
    497         state.mInput = mInput;
    498         state.mAmPmState = mAmPmState;
    499         state.mInputPointer = mInputPointer;
    500         return state;
    501     }
    502 
    503     @Override
    504     protected void onRestoreInstanceState (Parcelable state) {
    505         if (!(state instanceof SavedState)) {
    506             super.onRestoreInstanceState(state);
    507             return;
    508         }
    509 
    510         final SavedState savedState = (SavedState) state;
    511         super.onRestoreInstanceState(savedState.getSuperState());
    512 
    513         mInputPointer = savedState.mInputPointer;
    514         mInput = savedState.mInput;
    515         if (mInput == null) {
    516             mInput = new int[mInputSize];
    517             mInputPointer = -1;
    518         }
    519         mAmPmState = savedState.mAmPmState;
    520         updateKeypad();
    521     }
    522 
    523     private static class SavedState extends BaseSavedState {
    524         int mInputPointer;
    525         int[] mInput;
    526         int mAmPmState;
    527 
    528         public SavedState(Parcelable superState) {
    529             super(superState);
    530         }
    531 
    532         private SavedState(Parcel in) {
    533             super(in);
    534             mInputPointer = in.readInt();
    535             in.readIntArray(mInput);
    536             mAmPmState = in.readInt();
    537         }
    538 
    539         @Override
    540         public void writeToParcel(Parcel dest, int flags) {
    541             super.writeToParcel(dest, flags);
    542             dest.writeInt(mInputPointer);
    543             dest.writeIntArray(mInput);
    544             dest.writeInt(mAmPmState);
    545         }
    546 
    547         public static final Parcelable.Creator<SavedState> CREATOR
    548                 = new Parcelable.Creator<SavedState>() {
    549             public SavedState createFromParcel(Parcel in) {
    550                 return new SavedState(in);
    551             }
    552 
    553             public SavedState[] newArray(int size) {
    554                 return new SavedState[size];
    555             }
    556         };
    557     }
    558 }
    559