1/* 2 * Copyright (C) 2014 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 17package com.android.tv.settings.widget.picker; 18 19import android.os.Bundle; 20import android.view.View; 21 22import java.util.ArrayList; 23import java.util.Calendar; 24import java.util.Date; 25import java.util.GregorianCalendar; 26import android.widget.TextView; 27 28public class DatePicker extends Picker { 29 30 private static final String EXTRA_START_YEAR = "start_year"; 31 private static final String EXTRA_YEAR_RANGE = "year_range"; 32 private static final String EXTRA_DEFAULT_TO_CURRENT = "default_to_current"; 33 private static final String EXTRA_FORMAT = "date_format"; 34 private static final int DEFAULT_YEAR_RANGE = 24; 35 private static final int DEFAULT_START_YEAR = Calendar.getInstance().get(Calendar.YEAR); 36 private String[] mYears; 37 private int mStartYear; 38 private int mYearRange; 39 private String[] mDayString = null; 40 private int mColMonthIndex = 0; 41 private int mColDayIndex = 1; 42 private int mColYearIndex = 2; 43 44 private boolean mPendingDate = false; 45 private int mInitYear; 46 private int mInitMonth; 47 private int mInitDay; 48 49 50 private int mSelectedYear = DEFAULT_START_YEAR; 51 private String mSelectedMonth; 52 53 public static DatePicker newInstance() { 54 return newInstance(""); 55 } 56 57 /** 58 * Creates a new instance of DatePicker 59 * 60 * @param format String containing a permutation of Y, M and D, indicating the order 61 * of the fields Year, Month and Day to be displayed in the DatePicker. 62 */ 63 public static DatePicker newInstance(String format) { 64 return newInstance(format, DEFAULT_START_YEAR); 65 } 66 67 /** 68 * Creates a new instance of DatePicker 69 * 70 * @param format String containing a permutation of Y, M and D, indicating the order 71 * of the fields Year, Month and Day to be displayed in the DatePicker. 72 * @param startYear The lowest number to be displayed in the Year selector. 73 */ 74 public static DatePicker newInstance(String format, int startYear) { 75 return newInstance(format, startYear, DEFAULT_YEAR_RANGE, true); 76 } 77 78 /** 79 * Creates a new instance of DatePicker 80 * 81 * @param format String containing a permutation of Y, M and D, indicating the order 82 * of the fields Year, Month and Day to be displayed in the DatePicker. 83 * @param startYear The lowest number to be displayed in the Year selector. 84 * @param yearRange Number of entries to be displayed in the Year selector. 85 * @param startOnToday Indicates if the date should be set to the current date by default. 86 */ 87 public static DatePicker newInstance(String format, int startYear, int yearRange, 88 boolean startOnToday) { 89 DatePicker datePicker = new DatePicker(); 90 if (startYear <= 0) { 91 throw new IllegalArgumentException("The start year must be > 0. Got " + startYear); 92 } 93 if (yearRange <= 0) { 94 throw new IllegalArgumentException("The year range must be > 0. Got " + yearRange); 95 } 96 Bundle args = new Bundle(); 97 args.putString(EXTRA_FORMAT, format); 98 args.putInt(EXTRA_START_YEAR, startYear); 99 args.putInt(EXTRA_YEAR_RANGE, yearRange); 100 args.putBoolean(EXTRA_DEFAULT_TO_CURRENT, startOnToday); 101 datePicker.setArguments(args); 102 return datePicker; 103 } 104 105 private void initYearsArray(int startYear, int yearRange) { 106 mYears = new String[yearRange]; 107 for (int i = 0; i < yearRange; i++) { 108 mYears[i] = String.valueOf(startYear + i); 109 } 110 } 111 112 @Override 113 public void onCreate(Bundle savedInstanceState) { 114 super.onCreate(savedInstanceState); 115 mStartYear = getArguments().getInt(EXTRA_START_YEAR, DEFAULT_START_YEAR); 116 mYearRange = getArguments().getInt(EXTRA_YEAR_RANGE, DEFAULT_YEAR_RANGE); 117 boolean startOnToday = getArguments().getBoolean(EXTRA_DEFAULT_TO_CURRENT, false); 118 mSelectedMonth = mConstant.months[0]; 119 initYearsArray(mStartYear, mYearRange); 120 121 mDayString = mConstant.days30; 122 123 String format = getArguments().getString(EXTRA_FORMAT); 124 if (format != null && !format.isEmpty()) { 125 format = format.toUpperCase(); 126 127 int yIndex = format.indexOf('Y'); 128 int mIndex = format.indexOf('M'); 129 int dIndex = format.indexOf('D'); 130 if (yIndex < 0 || mIndex < 0 || dIndex < 0 || yIndex > 2 || mIndex > 2 || dIndex > 2) { 131 // Badly formatted input. Use default order. 132 mColMonthIndex = 0; 133 mColDayIndex = 1; 134 mColYearIndex = 2; 135 } else { 136 mColMonthIndex = mIndex; 137 mColDayIndex = dIndex; 138 mColYearIndex = yIndex; 139 } 140 } 141 142 if (startOnToday) { 143 mPendingDate = true; 144 Calendar cal = Calendar.getInstance(); 145 mInitYear = cal.get(Calendar.YEAR); 146 mInitMonth = cal.get(Calendar.MONTH); 147 mInitDay = cal.get(Calendar.DATE); 148 } 149 } 150 151 @Override 152 public void onResume() { 153 if (mPendingDate) { 154 mPendingDate = false; 155 setDate(mInitYear, mInitMonth, mInitDay); 156 } 157 super.onResume(); 158 } 159 160 @Override 161 protected ArrayList<PickerColumn> getColumns() { 162 ArrayList<PickerColumn> ret = new ArrayList<PickerColumn>(); 163 // TODO orders of these columns might need to be localized 164 PickerColumn months = new PickerColumn(mConstant.months); 165 PickerColumn days = new PickerColumn(mDayString); 166 PickerColumn years = new PickerColumn(mYears); 167 168 for (int i = 0; i < 3; i++) { 169 if (i == mColYearIndex) { 170 ret.add(years); 171 } else if (i == mColMonthIndex) { 172 ret.add(months); 173 } else if (i == mColDayIndex) { 174 ret.add(days); 175 } 176 } 177 178 return ret; 179 } 180 181 @Override 182 protected String getSeparator() { 183 return mConstant.dateSeparator; 184 } 185 186 protected boolean setDate(int year, int month, int day) { 187 boolean isLeapYear = false; 188 189 if (year < mStartYear || year > (mStartYear + mYearRange)) { 190 return false; 191 } 192 193 // Test to see if this is a valid date 194 try { 195 GregorianCalendar cal = new GregorianCalendar(year, month, day); 196 cal.setLenient(false); 197 Date test = cal.getTime(); 198 } catch (IllegalArgumentException e) { 199 return false; 200 } 201 202 mSelectedYear = year; 203 mSelectedMonth = mConstant.months[month]; 204 205 updateSelection(mColYearIndex, year - mStartYear); 206 updateSelection(mColMonthIndex, month); 207 208 String[] dayString = null; 209 // This is according to http://en.wikipedia.org/wiki/Leap_year#Algorithm 210 if (year % 400 == 0) { 211 isLeapYear = true; 212 } else if (year % 100 == 0) { 213 isLeapYear = false; 214 } else if (year % 4 == 0) { 215 isLeapYear = true; 216 } 217 218 if (month == 1) { 219 if (isLeapYear) { 220 dayString = mConstant.days29; 221 } else { 222 dayString = mConstant.days28; 223 } 224 } else if ((month == 3) || (month == 5) || (month == 8) || (month == 10)) { 225 dayString = mConstant.days30; 226 } else { 227 dayString = mConstant.days31; 228 } 229 230 if (mDayString != dayString) { 231 mDayString = dayString; 232 updateAdapter(mColDayIndex, new PickerColumn(mDayString)); 233 } 234 235 updateSelection(mColDayIndex, day - 1); 236 return true; 237 } 238 239 @Override 240 public void onScroll(View v) { 241 int column = (Integer) v.getTag(); 242 String text = ((TextView) v).getText().toString(); 243 if (column == mColMonthIndex) { 244 mSelectedMonth = text; 245 } else if (column == mColYearIndex) { 246 mSelectedYear = Integer.parseInt(text); 247 } else { 248 return; 249 } 250 251 String[] dayString = null; 252 253 boolean isLeapYear = false; 254 // This is according to http://en.wikipedia.org/wiki/Leap_year#Algorithm 255 if (mSelectedYear % 400 == 0) { 256 isLeapYear = true; 257 } else if (mSelectedYear % 100 == 0) { 258 isLeapYear = false; 259 } else if (mSelectedYear % 4 == 0) { 260 isLeapYear = true; 261 } 262 if (mSelectedMonth.equals(mConstant.months[1])) { 263 if (isLeapYear) { 264 dayString = mConstant.days29; 265 } else { 266 dayString = mConstant.days28; 267 } 268 } else if (mSelectedMonth.equals(mConstant.months[3]) 269 || mSelectedMonth.equals(mConstant.months[5]) 270 || mSelectedMonth.equals(mConstant.months[8]) 271 || mSelectedMonth.equals(mConstant.months[10])) { 272 dayString = mConstant.days30; 273 } else { 274 dayString = mConstant.days31; 275 } 276 if (!mDayString.equals(dayString)) { 277 mDayString = dayString; 278 updateAdapter(mColDayIndex, new PickerColumn(mDayString)); 279 } 280 } 281} 282