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 17 package com.android.tv.settings.system; 18 19 import android.app.AlarmManager; 20 import android.content.BroadcastReceiver; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.IntentFilter; 24 import android.content.res.Resources; 25 import android.os.Bundle; 26 import android.provider.Settings; 27 import android.provider.Settings.SettingNotFoundException; 28 import android.text.format.DateFormat; 29 30 import com.android.settingslib.datetime.ZoneGetter; 31 import com.android.tv.settings.R; 32 import com.android.tv.settings.dialog.Layout; 33 import com.android.tv.settings.dialog.SettingsLayoutActivity; 34 import com.android.tv.settings.util.SettingsHelper; 35 36 import java.util.Calendar; 37 import java.util.Collections; 38 import java.util.Comparator; 39 import java.util.Date; 40 import java.util.List; 41 import java.util.Map; 42 import java.util.TimeZone; 43 44 public class DateTimeActivity extends SettingsLayoutActivity { 45 46 private static final String TAG = "DateTimeActivity"; 47 48 private static final String HOURS_12 = "12"; 49 private static final String HOURS_24 = "24"; 50 51 private static final int ACTION_AUTO_TIME_ON = 0; 52 private static final int ACTION_AUTO_TIME_OFF = 1; 53 private static final int ACTION_24HOUR_FORMAT_ON = 3; 54 private static final int ACTION_24HOUR_FORMAT_OFF = 4; 55 private static final int ACTION_SET_TIMEZONE_BASE = 1<<10; 56 57 58 private Calendar mDummyDate; 59 private boolean mIsResumed; 60 61 private String mNowDate; 62 private String mNowTime; 63 64 private SettingsHelper mHelper; 65 66 private final BroadcastReceiver mTimeUpdateIntentReceiver = new BroadcastReceiver() { 67 @Override 68 public void onReceive(Context context, Intent intent) { 69 if (mIsResumed) { 70 updateTimeAndDateStrings(); 71 } 72 } 73 }; 74 75 private final Layout.StringGetter mTimeStringGetter = new Layout.StringGetter() { 76 @Override 77 public String get() { 78 return mNowTime; 79 } 80 }; 81 82 private final Layout.StringGetter mDateStringGetter = new Layout.StringGetter() { 83 @Override 84 public String get() { 85 return mNowDate; 86 } 87 }; 88 89 private final Layout.StringGetter mTimezoneStringGetter = new Layout.StringGetter() { 90 @Override 91 public String get() { 92 final Calendar now = Calendar.getInstance(); 93 TimeZone tz = now.getTimeZone(); 94 95 Date date = new Date(); 96 return getString(R.string.desc_set_time_zone, 97 formatOffset(tz.getOffset(date.getTime())), 98 tz.getDisplayName(tz.inDaylightTime(date), TimeZone.LONG)); 99 } 100 }; 101 102 private final Layout.StringGetter mTimeFormatStringGetter = new Layout.StringGetter() { 103 @Override 104 public String get() { 105 String status = mHelper.getStatusStringFromBoolean(isTimeFormat24h()); 106 return getString(R.string.desc_set_time_format, status, 107 DateFormat.getTimeFormat(DateTimeActivity.this).format(mDummyDate.getTime())); 108 } 109 }; 110 111 private final Layout.LayoutGetter mSetDateLayoutGetter = new Layout.LayoutGetter() { 112 @Override 113 public Layout get() { 114 final Resources res = getResources(); 115 return new Layout() 116 .add(new Layout.Header.Builder(res) 117 .title(R.string.system_date) 118 .description(mDateStringGetter) 119 .enabled(!getAutoState(Settings.Global.AUTO_TIME)) 120 .build() 121 .add(new Layout.Action.Builder(res, 122 SetDateTimeActivity.getSetDateIntent(DateTimeActivity.this)) 123 .title(R.string.system_set_date) 124 .description(mDateStringGetter) 125 .build())); 126 } 127 }; 128 129 private final Layout.LayoutGetter mSetTimeLayoutGetter = new Layout.LayoutGetter() { 130 @Override 131 public Layout get() { 132 final Resources res = getResources(); 133 return new Layout() 134 .add(new Layout.Action.Builder(res, 135 SetDateTimeActivity.getSetTimeIntent(DateTimeActivity.this)) 136 .title(R.string.system_set_time) 137 .description(mTimeStringGetter) 138 .enabled(!getAutoState(Settings.Global.AUTO_TIME)) 139 .build()); 140 141 } 142 }; 143 144 private Layout.SelectionGroup mAutoDateTimeSelector; 145 private Layout.SelectionGroup mTimezoneSelector; 146 private Layout.SelectionGroup mTimeFormatSelector; 147 148 private List<Map<String, Object>> mTimeZones; 149 150 @Override 151 public void onCreate(Bundle savedInstanceState) { 152 mDummyDate = Calendar.getInstance(); 153 154 mHelper = new SettingsHelper(getApplicationContext()); 155 156 // Auto date time 157 mAutoDateTimeSelector = new Layout.SelectionGroup.Builder(2) 158 .add(getString(R.string.action_on_description), null, ACTION_AUTO_TIME_ON) 159 .add(getString(R.string.action_off_description), null, ACTION_AUTO_TIME_OFF) 160 .build(); 161 mAutoDateTimeSelector.setSelected(getAutoState(Settings.Global.AUTO_TIME) ? 162 ACTION_AUTO_TIME_ON : ACTION_AUTO_TIME_OFF); 163 164 // Time zone 165 mTimeZones = ZoneGetter.getZonesList(this); 166 // Sort the Time Zones list in ascending offset order 167 Collections.sort(mTimeZones, new MyComparator(ZoneGetter.KEY_OFFSET)); 168 final TimeZone currentTz = TimeZone.getDefault(); 169 170 final Layout.SelectionGroup.Builder tzBuilder = 171 new Layout.SelectionGroup.Builder(mTimeZones.size()); 172 173 int i = ACTION_SET_TIMEZONE_BASE; 174 int currentTzActionId = -1; 175 for (final Map<String, Object> tz : mTimeZones) { 176 if (currentTz.getID().equals(tz.get(ZoneGetter.KEY_ID))) { 177 currentTzActionId = i; 178 } 179 tzBuilder.add((String) tz.get(ZoneGetter.KEY_DISPLAYNAME), 180 formatOffset((Integer) tz.get(ZoneGetter.KEY_OFFSET)), i); 181 i++; 182 } 183 184 mTimezoneSelector = tzBuilder.build(); 185 mTimezoneSelector.setSelected(currentTzActionId); 186 187 // Time Format 188 mTimeFormatSelector = new Layout.SelectionGroup.Builder(2) 189 .add(getString(R.string.settings_on), null, ACTION_24HOUR_FORMAT_ON) 190 .add(getString(R.string.settings_off), null, ACTION_24HOUR_FORMAT_OFF) 191 .build(); 192 193 setSampleDate(); 194 195 updateTimeAndDateStrings(); 196 197 super.onCreate(savedInstanceState); 198 } 199 200 @Override 201 protected void onResume() { 202 super.onResume(); 203 mIsResumed = true; 204 205 final IntentFilter filter = new IntentFilter(); 206 filter.addAction(Intent.ACTION_TIME_TICK); 207 filter.addAction(Intent.ACTION_TIME_CHANGED); 208 filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); 209 filter.addAction(Intent.ACTION_DATE_CHANGED); 210 211 registerReceiver(mTimeUpdateIntentReceiver, filter); 212 213 updateTimeAndDateStrings(); 214 } 215 216 @Override 217 protected void onPause() { 218 super.onPause(); 219 mIsResumed = false; 220 unregisterReceiver(mTimeUpdateIntentReceiver); 221 } 222 223 @Override 224 public Layout createLayout() { 225 final Resources res = getResources(); 226 return new Layout().breadcrumb(getString(R.string.header_category_preferences)) 227 .add(new Layout.Header.Builder(res) 228 .icon(R.drawable.ic_settings_datetime) 229 .title(R.string.system_date_time) 230 .build() 231 .add(new Layout.Header.Builder(res) 232 .title(R.string.system_auto_date_time) 233 .description(mAutoDateTimeSelector) 234 .build() 235 .add(mAutoDateTimeSelector)) 236 .add(mSetDateLayoutGetter) 237 .add(new Layout.Header.Builder(res) 238 .title(R.string.system_time) 239 .description(mTimeStringGetter) 240 .build() 241 .add(mSetTimeLayoutGetter) 242 .add(new Layout.Header.Builder(res) 243 .title(R.string.system_set_time_zone) 244 .description(mTimezoneStringGetter) 245 .build() 246 .add(mTimezoneSelector)) 247 .add(new Layout.Header.Builder(res) 248 .title(R.string.system_set_time_format) 249 .description(mTimeFormatStringGetter) 250 .build() 251 .add(mTimeFormatSelector)))); 252 } 253 254 private void setSampleDate() { 255 Calendar now = Calendar.getInstance(); 256 mDummyDate.setTimeZone(now.getTimeZone()); 257 // We use December 31st because it's unambiguous when demonstrating the date format. 258 // We use 15:14 so we can demonstrate the 12/24 hour options. 259 mDummyDate.set(now.get(Calendar.YEAR), 11, 31, 15, 14, 0); 260 } 261 262 private boolean getAutoState(String name) { 263 try { 264 return Settings.Global.getInt(getContentResolver(), name) > 0; 265 } catch (SettingNotFoundException snfe) { 266 return false; 267 } 268 } 269 270 271 private boolean isTimeFormat24h() { 272 return DateFormat.is24HourFormat(this); 273 } 274 275 private void setTime24Hour(boolean is24Hour) { 276 Settings.System.putString(getContentResolver(), 277 Settings.System.TIME_12_24, 278 is24Hour ? HOURS_24 : HOURS_12); 279 updateTimeAndDateStrings(); 280 } 281 282 private void setAutoDateTime(boolean on) { 283 Settings.Global.putInt(getContentResolver(), Settings.Global.AUTO_TIME, on ? 1 : 0); 284 } 285 286 // Updates the member strings to reflect the current date and time. 287 private void updateTimeAndDateStrings() { 288 final Calendar now = Calendar.getInstance(); 289 java.text.DateFormat dateFormat = DateFormat.getDateFormat(this); 290 mNowDate = dateFormat.format(now.getTime()); 291 java.text.DateFormat timeFormat = DateFormat.getTimeFormat(this); 292 mNowTime = timeFormat.format(now.getTime()); 293 294 mDateStringGetter.refreshView(); 295 mTimeStringGetter.refreshView(); 296 } 297 298 @Override 299 public void onActionClicked(Layout.Action action) { 300 final int actionId = action.getId(); 301 switch (actionId) { 302 case Layout.Action.ACTION_INTENT: 303 startActivity(action.getIntent()); 304 break; 305 case ACTION_AUTO_TIME_ON: 306 setAutoDateTime(true); 307 mSetDateLayoutGetter.refreshView(); 308 mSetTimeLayoutGetter.refreshView(); 309 break; 310 case ACTION_AUTO_TIME_OFF: 311 setAutoDateTime(false); 312 mSetDateLayoutGetter.refreshView(); 313 mSetTimeLayoutGetter.refreshView(); 314 break; 315 case ACTION_24HOUR_FORMAT_ON: 316 setTime24Hour(true); 317 break; 318 case ACTION_24HOUR_FORMAT_OFF: 319 setTime24Hour(false); 320 break; 321 default: 322 if ((actionId & ACTION_SET_TIMEZONE_BASE) != 0) { 323 setTimeZone((String) mTimeZones.get(actionId - ACTION_SET_TIMEZONE_BASE) 324 .get(ZoneGetter.KEY_ID)); 325 } 326 break; 327 } 328 } 329 330 /** 331 * Formats the provided timezone offset into a string of the form GMT+XX:XX 332 */ 333 private static String formatOffset(long offset) { 334 long off = offset / 1000 / 60; 335 final StringBuilder sb = new StringBuilder(); 336 337 sb.append("GMT"); 338 if (off < 0) { 339 sb.append('-'); 340 off = -off; 341 } else { 342 sb.append('+'); 343 } 344 345 int hours = (int) (off / 60); 346 int minutes = (int) (off % 60); 347 348 sb.append((char) ('0' + hours / 10)); 349 sb.append((char) ('0' + hours % 10)); 350 351 sb.append(':'); 352 353 sb.append((char) ('0' + minutes / 10)); 354 sb.append((char) ('0' + minutes % 10)); 355 356 return sb.toString(); 357 } 358 359 private void setTimeZone(String tzId) { 360 // Update the system timezone value 361 final AlarmManager alarm = (AlarmManager) getSystemService(Context.ALARM_SERVICE); 362 alarm.setTimeZone(tzId); 363 364 setSampleDate(); 365 } 366 367 private static class MyComparator implements Comparator<Map<?, ?>> { 368 private String mSortingKey; 369 370 public MyComparator(String sortingKey) { 371 mSortingKey = sortingKey; 372 } 373 374 public void setSortingKey(String sortingKey) { 375 mSortingKey = sortingKey; 376 } 377 378 public int compare(Map<?, ?> map1, Map<?, ?> map2) { 379 Object value1 = map1.get(mSortingKey); 380 Object value2 = map2.get(mSortingKey); 381 382 /* 383 * This should never happen, but just in-case, put non-comparable 384 * items at the end. 385 */ 386 if (!isComparable(value1)) { 387 return isComparable(value2) ? 1 : 0; 388 } else if (!isComparable(value2)) { 389 return -1; 390 } 391 392 return ((Comparable) value1).compareTo(value2); 393 } 394 395 private boolean isComparable(Object value) { 396 return (value != null) && (value instanceof Comparable); 397 } 398 } 399 400 } 401