Utils.java revision cfa204ba5c59eb6ebd4b54788ceb31010e4a37a0
1/* 2 * Copyright (C) 2006 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.calendar; 18 19import static android.provider.Calendar.EVENT_BEGIN_TIME; 20 21import com.android.calendar.CalendarController.ViewType; 22 23import android.app.Activity; 24import android.content.Context; 25import android.content.Intent; 26import android.content.SharedPreferences; 27import android.database.Cursor; 28import android.database.MatrixCursor; 29import android.net.Uri; 30import android.os.Bundle; 31import android.text.TextUtils; 32import android.text.format.DateUtils; 33import android.text.format.Time; 34import android.util.CalendarUtils.TimeZoneUtils; 35import android.util.Log; 36 37import java.util.Calendar; 38import java.util.Formatter; 39import java.util.List; 40import java.util.Map; 41import java.util.TimeZone; 42 43public class Utils { 44 private static final boolean DEBUG = true; 45 private static final String TAG = "CalUtils"; 46 // Set to 0 until we have UI to perform undo 47 public static final long UNDO_DELAY = 0; 48 49 // For recurring events which instances of the series are being modified 50 public static final int MODIFY_UNINITIALIZED = 0; 51 public static final int MODIFY_SELECTED = 1; 52 public static final int MODIFY_ALL_FOLLOWING = 2; 53 public static final int MODIFY_ALL = 3; 54 55 // When the edit event view finishes it passes back the appropriate exit 56 // code. 57 public static final int DONE_REVERT = 1 << 0; 58 public static final int DONE_SAVE = 1 << 1; 59 public static final int DONE_DELETE = 1 << 2; 60 // And should re run with DONE_EXIT if it should also leave the view, just 61 // exiting is identical to reverting 62 public static final int DONE_EXIT = 1 << 0; 63 64 private static final int CLEAR_ALPHA_MASK = 0x00FFFFFF; 65 private static final int HIGH_ALPHA = 255 << 24; 66 private static final int MED_ALPHA = 180 << 24; 67 private static final int LOW_ALPHA = 150 << 24; 68 69 protected static final String OPEN_EMAIL_MARKER = " <"; 70 protected static final String CLOSE_EMAIL_MARKER = ">"; 71 /* The corner should be rounded on the top right and bottom right */ 72 private static final float[] CORNERS = new float[] { 0, 0, 5, 5, 5, 5, 0, 0 }; 73 74 public static final String INTENT_KEY_DETAIL_VIEW = "DETAIL_VIEW"; 75 public static final String INTENT_KEY_VIEW_TYPE = "VIEW"; 76 public static final String INTENT_VALUE_VIEW_TYPE_DAY = "DAY"; 77 78 public static final int MONDAY_BEFORE_JULIAN_EPOCH = Time.EPOCH_JULIAN_DAY - 3; 79 80 // The name of the shared preferences file. This name must be maintained for 81 // historical 82 // reasons, as it's what PreferenceManager assigned the first time the file 83 // was created. 84 private static final String SHARED_PREFS_NAME = "com.android.calendar_preferences"; 85 86 private static final TimeZoneUtils mTZUtils = new TimeZoneUtils(SHARED_PREFS_NAME); 87 private static boolean mAllowWeekForDetailView = false; 88 private static long mTardis = 0; 89 90 public static int getViewTypeFromIntentAndSharedPref(Activity activity) { 91 Intent intent = activity.getIntent(); 92 Bundle extras = intent.getExtras(); 93 SharedPreferences prefs = GeneralPreferences.getSharedPreferences(activity); 94 95 if (TextUtils.equals(intent.getAction(), Intent.ACTION_EDIT)) { 96 return ViewType.EDIT; 97 } 98 if (extras != null) { 99 if (extras.getBoolean(INTENT_KEY_DETAIL_VIEW, false)) { 100 // This is the "detail" view which is either agenda or day view 101 return prefs.getInt(GeneralPreferences.KEY_DETAILED_VIEW, 102 GeneralPreferences.DEFAULT_DETAILED_VIEW); 103 } else if (INTENT_VALUE_VIEW_TYPE_DAY.equals(extras.getString(INTENT_KEY_VIEW_TYPE))) { 104 // Not sure who uses this. This logic came from LaunchActivity 105 return ViewType.DAY; 106 } 107 } 108 109 // Default to the last view 110 return prefs.getInt( 111 GeneralPreferences.KEY_START_VIEW, GeneralPreferences.DEFAULT_START_VIEW); 112 } 113 114 /** 115 * Writes a new home time zone to the db. Updates the home time zone in the 116 * db asynchronously and updates the local cache. Sending a time zone of 117 * **tbd** will cause it to be set to the device's time zone. null or empty 118 * tz will be ignored. 119 * 120 * @param context The calling activity 121 * @param timeZone The time zone to set Calendar to, or **tbd** 122 */ 123 public static void setTimeZone(Context context, String timeZone) { 124 mTZUtils.setTimeZone(context, timeZone); 125 } 126 127 /** 128 * Gets the time zone that Calendar should be displayed in This is a helper 129 * method to get the appropriate time zone for Calendar. If this is the 130 * first time this method has been called it will initiate an asynchronous 131 * query to verify that the data in preferences is correct. The callback 132 * supplied will only be called if this query returns a value other than 133 * what is stored in preferences and should cause the calling activity to 134 * refresh anything that depends on calling this method. 135 * 136 * @param context The calling activity 137 * @param callback The runnable that should execute if a query returns new 138 * values 139 * @return The string value representing the time zone Calendar should 140 * display 141 */ 142 public static String getTimeZone(Context context, Runnable callback) { 143 return mTZUtils.getTimeZone(context, callback); 144 } 145 146 /** 147 * Formats a date or a time range according to the local conventions. 148 * 149 * @param context the context is required only if the time is shown 150 * @param startMillis the start time in UTC milliseconds 151 * @param endMillis the end time in UTC milliseconds 152 * @param flags a bit mask of options See {@link DateUtils#formatDateRange(Context, Formatter, 153 * long, long, int, String) formatDateRange} 154 * @return a string containing the formatted date/time range. 155 */ 156 public static String formatDateRange( 157 Context context, long startMillis, long endMillis, int flags) { 158 return mTZUtils.formatDateRange(context, startMillis, endMillis, flags); 159 } 160 161 public static String getSharedPreference(Context context, String key, String defaultValue) { 162 SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); 163 return prefs.getString(key, defaultValue); 164 } 165 166 public static int getSharedPreference(Context context, String key, int defaultValue) { 167 SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); 168 return prefs.getInt(key, defaultValue); 169 } 170 171 public static boolean getSharedPreference(Context context, String key, boolean defaultValue) { 172 SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); 173 return prefs.getBoolean(key, defaultValue); 174 } 175 176 /** 177 * Asynchronously sets the preference with the given key to the given value 178 * 179 * @param context the context to use to get preferences from 180 * @param key the key of the preference to set 181 * @param value the value to set 182 */ 183 public static void setSharedPreference(Context context, String key, String value) { 184 SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); 185 prefs.edit().putString(key, value).apply(); 186 } 187 188 protected static void tardis() { 189 mTardis = System.currentTimeMillis(); 190 } 191 192 protected static long getTardis() { 193 return mTardis; 194 } 195 196 static void setSharedPreference(Context context, String key, boolean value) { 197 SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); 198 SharedPreferences.Editor editor = prefs.edit(); 199 editor.putBoolean(key, value); 200 editor.apply(); 201 } 202 203 static void setSharedPreference(Context context, String key, int value) { 204 SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); 205 SharedPreferences.Editor editor = prefs.edit(); 206 editor.putInt(key, value); 207 editor.apply(); 208 } 209 210 /** 211 * Save default agenda/day/week/month view for next time 212 * 213 * @param context 214 * @param viewId {@link CalendarController.ViewType} 215 */ 216 static void setDefaultView(Context context, int viewId) { 217 SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); 218 SharedPreferences.Editor editor = prefs.edit(); 219 220 boolean validDetailView = false; 221 if (mAllowWeekForDetailView && viewId == CalendarController.ViewType.WEEK) { 222 validDetailView = true; 223 } else { 224 validDetailView = viewId == CalendarController.ViewType.AGENDA 225 || viewId == CalendarController.ViewType.DAY; 226 } 227 228 if (validDetailView) { 229 // Record the detail start view 230 editor.putInt(GeneralPreferences.KEY_DETAILED_VIEW, viewId); 231 } 232 233 // Record the (new) start view 234 editor.putInt(GeneralPreferences.KEY_START_VIEW, viewId); 235 editor.apply(); 236 } 237 238 public static MatrixCursor matrixCursorFromCursor(Cursor cursor) { 239 MatrixCursor newCursor = new MatrixCursor(cursor.getColumnNames()); 240 int numColumns = cursor.getColumnCount(); 241 String data[] = new String[numColumns]; 242 cursor.moveToPosition(-1); 243 while (cursor.moveToNext()) { 244 for (int i = 0; i < numColumns; i++) { 245 data[i] = cursor.getString(i); 246 } 247 newCursor.addRow(data); 248 } 249 return newCursor; 250 } 251 252 /** 253 * Compares two cursors to see if they contain the same data. 254 * 255 * @return Returns true of the cursors contain the same data and are not 256 * null, false otherwise 257 */ 258 public static boolean compareCursors(Cursor c1, Cursor c2) { 259 if (c1 == null || c2 == null) { 260 return false; 261 } 262 263 int numColumns = c1.getColumnCount(); 264 if (numColumns != c2.getColumnCount()) { 265 return false; 266 } 267 268 if (c1.getCount() != c2.getCount()) { 269 return false; 270 } 271 272 c1.moveToPosition(-1); 273 c2.moveToPosition(-1); 274 while (c1.moveToNext() && c2.moveToNext()) { 275 for (int i = 0; i < numColumns; i++) { 276 if (!TextUtils.equals(c1.getString(i), c2.getString(i))) { 277 return false; 278 } 279 } 280 } 281 282 return true; 283 } 284 285 /** 286 * If the given intent specifies a time (in milliseconds since the epoch), 287 * then that time is returned. Otherwise, the current time is returned. 288 */ 289 public static final long timeFromIntentInMillis(Intent intent) { 290 // If the time was specified, then use that. Otherwise, use the current 291 // time. 292 Uri data = intent.getData(); 293 long millis = intent.getLongExtra(EVENT_BEGIN_TIME, -1); 294 if (millis == -1 && data != null && data.isHierarchical()) { 295 List<String> path = data.getPathSegments(); 296 if (path.size() == 2 && path.get(0).equals("time")) { 297 try { 298 millis = Long.valueOf(data.getLastPathSegment()); 299 } catch (NumberFormatException e) { 300 Log.i("Calendar", "timeFromIntentInMillis: Data existed but no valid time " 301 + "found. Using current time."); 302 } 303 } 304 } 305 if (millis <= 0) { 306 millis = System.currentTimeMillis(); 307 } 308 return millis; 309 } 310 311 /** 312 * Formats the given Time object so that it gives the month and year (for 313 * example, "September 2007"). 314 * 315 * @param time the time to format 316 * @return the string containing the weekday and the date 317 */ 318 public static String formatMonthYear(Context context, Time time) { 319 int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_MONTH_DAY 320 | DateUtils.FORMAT_SHOW_YEAR; 321 long millis = time.toMillis(true); 322 return formatDateRange(context, millis, millis, flags); 323 } 324 325 /** 326 * Returns a list joined together by the provided delimiter, for example, 327 * ["a", "b", "c"] could be joined into "a,b,c" 328 * 329 * @param things the things to join together 330 * @param delim the delimiter to use 331 * @return a string contained the things joined together 332 */ 333 public static String join(List<?> things, String delim) { 334 StringBuilder builder = new StringBuilder(); 335 boolean first = true; 336 for (Object thing : things) { 337 if (first) { 338 first = false; 339 } else { 340 builder.append(delim); 341 } 342 builder.append(thing.toString()); 343 } 344 return builder.toString(); 345 } 346 347 /** 348 * Returns the week since {@link Time#EPOCH_JULIAN_DAY} (Jan 1, 1970) 349 * adjusted for first day of week. 350 * 351 * This takes a julian day and the week start day and calculates which 352 * week since {@link Time#EPOCH_JULIAN_DAY} that day occurs in, starting 353 * at 0. *Do not* use this to compute the ISO week number for the year. 354 * 355 * @param julianDay The julian day to calculate the week number for 356 * @param firstDayOfWeek Which week day is the first day of the week, 357 * see {@link Time#SUNDAY} 358 * @return Weeks since the epoch 359 */ 360 public static int getWeeksSinceEpochFromJulianDay(int julianDay, int firstDayOfWeek) { 361 int diff = Time.THURSDAY - firstDayOfWeek; 362 if (diff < 0) { 363 diff += 7; 364 } 365 int refDay = Time.EPOCH_JULIAN_DAY - diff; 366 return (julianDay - refDay) / 7; 367 } 368 369 /** 370 * Takes a number of weeks since the epoch and calculates the Julian day of 371 * the Monday for that week. 372 * 373 * This assumes that the week containing the {@link Time#EPOCH_JULIAN_DAY} 374 * is considered week 0. It returns the Julian day for the Monday 375 * {@code week} weeks after the Monday of the week containing the epoch. 376 * 377 * @param week Number of weeks since the epoch 378 * @return The julian day for the Monday of the given week since the epoch 379 */ 380 public static int getJulianMondayFromWeeksSinceEpoch(int week) { 381 return MONDAY_BEFORE_JULIAN_EPOCH + week * 7; 382 } 383 384 /** 385 * Get first day of week as android.text.format.Time constant. 386 * 387 * @return the first day of week in android.text.format.Time 388 */ 389 public static int getFirstDayOfWeek(Context context) { 390 SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); 391 String pref = prefs.getString( 392 GeneralPreferences.KEY_WEEK_START_DAY, GeneralPreferences.WEEK_START_DEFAULT); 393 394 int startDay; 395 if (GeneralPreferences.WEEK_START_DEFAULT.equals(pref)) { 396 startDay = Calendar.getInstance().getFirstDayOfWeek(); 397 } else { 398 startDay = Integer.parseInt(pref); 399 } 400 401 if (startDay == Calendar.SATURDAY) { 402 return Time.SATURDAY; 403 } else if (startDay == Calendar.MONDAY) { 404 return Time.MONDAY; 405 } else { 406 return Time.SUNDAY; 407 } 408 } 409 410 /** 411 * @return true when week number should be shown. 412 */ 413 public static boolean getShowWeekNumber(Context context) { 414 final SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); 415 return prefs.getBoolean( 416 GeneralPreferences.KEY_SHOW_WEEK_NUM, GeneralPreferences.DEFAULT_SHOW_WEEK_NUM); 417 } 418 419 /** 420 * @return true when declined events should be hidden. 421 */ 422 public static boolean getHideDeclinedEvents(Context context) { 423 final SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); 424 return prefs.getBoolean(GeneralPreferences.KEY_HIDE_DECLINED, false); 425 } 426 427 public static int getDaysPerWeek(Context context) { 428 final SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); 429 return prefs.getInt(GeneralPreferences.KEY_DAYS_PER_WEEK, 7); 430 } 431 432 /** 433 * Determine whether the column position is Saturday or not. 434 * 435 * @param column the column position 436 * @param firstDayOfWeek the first day of week in android.text.format.Time 437 * @return true if the column is Saturday position 438 */ 439 public static boolean isSaturday(int column, int firstDayOfWeek) { 440 return (firstDayOfWeek == Time.SUNDAY && column == 6) 441 || (firstDayOfWeek == Time.MONDAY && column == 5) 442 || (firstDayOfWeek == Time.SATURDAY && column == 0); 443 } 444 445 /** 446 * Determine whether the column position is Sunday or not. 447 * 448 * @param column the column position 449 * @param firstDayOfWeek the first day of week in android.text.format.Time 450 * @return true if the column is Sunday position 451 */ 452 public static boolean isSunday(int column, int firstDayOfWeek) { 453 return (firstDayOfWeek == Time.SUNDAY && column == 0) 454 || (firstDayOfWeek == Time.MONDAY && column == 6) 455 || (firstDayOfWeek == Time.SATURDAY && column == 1); 456 } 457 458 /** 459 * Convert given UTC time into current local time. 460 * 461 * @param recycle Time object to recycle, otherwise null. 462 * @param utcTime Time to convert, in UTC. 463 */ 464 public static long convertUtcToLocal(Time recycle, long utcTime) { 465 if (recycle == null) { 466 recycle = new Time(); 467 } 468 recycle.timezone = Time.TIMEZONE_UTC; 469 recycle.set(utcTime); 470 recycle.timezone = TimeZone.getDefault().getID(); 471 return recycle.normalize(true); 472 } 473 474 /** 475 * Scan through a cursor of calendars and check if names are duplicated. 476 * This travels a cursor containing calendar display names and fills in the 477 * provided map with whether or not each name is repeated. 478 * 479 * @param isDuplicateName The map to put the duplicate check results in. 480 * @param cursor The query of calendars to check 481 * @param nameIndex The column of the query that contains the display name 482 */ 483 public static void checkForDuplicateNames( 484 Map<String, Boolean> isDuplicateName, Cursor cursor, int nameIndex) { 485 isDuplicateName.clear(); 486 cursor.moveToPosition(-1); 487 while (cursor.moveToNext()) { 488 String displayName = cursor.getString(nameIndex); 489 // Set it to true if we've seen this name before, false otherwise 490 if (displayName != null) { 491 isDuplicateName.put(displayName, isDuplicateName.containsKey(displayName)); 492 } 493 } 494 } 495 496 /** 497 * Null-safe object comparison 498 * 499 * @param s1 500 * @param s2 501 * @return 502 */ 503 public static boolean equals(Object o1, Object o2) { 504 return o1 == null ? o2 == null : o1.equals(o2); 505 } 506 507 public static void setAllowWeekForDetailView(boolean allowWeekView) { 508 mAllowWeekForDetailView = allowWeekView; 509 } 510 511 public static boolean getAllowWeekForDetailView() { 512 return mAllowWeekForDetailView; 513 } 514} 515