Utils.java revision ec047f19da78f4feaa5b9866846b375b454cd547
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 return time.format(context.getResources().getString(R.string.month_year)); 320 } 321 322 /** 323 * Returns a list joined together by the provided delimiter, for example, 324 * ["a", "b", "c"] could be joined into "a,b,c" 325 * 326 * @param things the things to join together 327 * @param delim the delimiter to use 328 * @return a string contained the things joined together 329 */ 330 public static String join(List<?> things, String delim) { 331 StringBuilder builder = new StringBuilder(); 332 boolean first = true; 333 for (Object thing : things) { 334 if (first) { 335 first = false; 336 } else { 337 builder.append(delim); 338 } 339 builder.append(thing.toString()); 340 } 341 return builder.toString(); 342 } 343 344 /** 345 * Returns the week since {@link Time#EPOCH_JULIAN_DAY} (Jan 1, 1970) 346 * adjusted for first day of week. 347 * 348 * This takes a julian day and the week start day and calculates which 349 * week since {@link Time#EPOCH_JULIAN_DAY} that day occurs in, starting 350 * at 0. *Do not* use this to compute the ISO week number for the year. 351 * 352 * @param julianDay The julian day to calculate the week number for 353 * @param firstDayOfWeek Which week day is the first day of the week, 354 * see {@link Time#SUNDAY} 355 * @return Weeks since the epoch 356 */ 357 public static int getWeeksSinceEpochFromJulianDay(int julianDay, int firstDayOfWeek) { 358 int diff = Time.THURSDAY - firstDayOfWeek; 359 if (diff < 0) { 360 diff += 7; 361 } 362 int refDay = Time.EPOCH_JULIAN_DAY - diff; 363 return (julianDay - refDay) / 7; 364 } 365 366 /** 367 * Takes a number of weeks since the epoch and calculates the Julian day of 368 * the Monday for that week. 369 * 370 * This assumes that the week containing the {@link Time#EPOCH_JULIAN_DAY} 371 * is considered week 0. It returns the Julian day for the Monday 372 * {@code week} weeks after the Monday of the week containing the epoch. 373 * 374 * @param week Number of weeks since the epoch 375 * @return The julian day for the Monday of the given week since the epoch 376 */ 377 public static int getJulianMondayFromWeeksSinceEpoch(int week) { 378 return MONDAY_BEFORE_JULIAN_EPOCH + week * 7; 379 } 380 381 /** 382 * Get first day of week as android.text.format.Time constant. 383 * 384 * @return the first day of week in android.text.format.Time 385 */ 386 public static int getFirstDayOfWeek(Context context) { 387 SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); 388 String pref = prefs.getString( 389 GeneralPreferences.KEY_WEEK_START_DAY, GeneralPreferences.WEEK_START_DEFAULT); 390 391 int startDay; 392 if (GeneralPreferences.WEEK_START_DEFAULT.equals(pref)) { 393 startDay = Calendar.getInstance().getFirstDayOfWeek(); 394 } else { 395 startDay = Integer.parseInt(pref); 396 } 397 398 if (startDay == Calendar.SATURDAY) { 399 return Time.SATURDAY; 400 } else if (startDay == Calendar.MONDAY) { 401 return Time.MONDAY; 402 } else { 403 return Time.SUNDAY; 404 } 405 } 406 407 /** 408 * @return true when week number should be shown. 409 */ 410 public static boolean getShowWeekNumber(Context context) { 411 final SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); 412 return prefs.getBoolean( 413 GeneralPreferences.KEY_SHOW_WEEK_NUM, GeneralPreferences.DEFAULT_SHOW_WEEK_NUM); 414 } 415 416 /** 417 * @return true when declined events should be hidden. 418 */ 419 public static boolean getHideDeclinedEvents(Context context) { 420 final SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); 421 return prefs.getBoolean(GeneralPreferences.KEY_HIDE_DECLINED, false); 422 } 423 424 public static int getDaysPerWeek(Context context) { 425 final SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); 426 return prefs.getInt(GeneralPreferences.KEY_DAYS_PER_WEEK, 7); 427 } 428 429 /** 430 * Determine whether the column position is Saturday or not. 431 * 432 * @param column the column position 433 * @param firstDayOfWeek the first day of week in android.text.format.Time 434 * @return true if the column is Saturday position 435 */ 436 public static boolean isSaturday(int column, int firstDayOfWeek) { 437 return (firstDayOfWeek == Time.SUNDAY && column == 6) 438 || (firstDayOfWeek == Time.MONDAY && column == 5) 439 || (firstDayOfWeek == Time.SATURDAY && column == 0); 440 } 441 442 /** 443 * Determine whether the column position is Sunday or not. 444 * 445 * @param column the column position 446 * @param firstDayOfWeek the first day of week in android.text.format.Time 447 * @return true if the column is Sunday position 448 */ 449 public static boolean isSunday(int column, int firstDayOfWeek) { 450 return (firstDayOfWeek == Time.SUNDAY && column == 0) 451 || (firstDayOfWeek == Time.MONDAY && column == 6) 452 || (firstDayOfWeek == Time.SATURDAY && column == 1); 453 } 454 455 /** 456 * Convert given UTC time into current local time. 457 * 458 * @param recycle Time object to recycle, otherwise null. 459 * @param utcTime Time to convert, in UTC. 460 */ 461 public static long convertUtcToLocal(Time recycle, long utcTime) { 462 if (recycle == null) { 463 recycle = new Time(); 464 } 465 recycle.timezone = Time.TIMEZONE_UTC; 466 recycle.set(utcTime); 467 recycle.timezone = TimeZone.getDefault().getID(); 468 return recycle.normalize(true); 469 } 470 471 /** 472 * Scan through a cursor of calendars and check if names are duplicated. 473 * This travels a cursor containing calendar display names and fills in the 474 * provided map with whether or not each name is repeated. 475 * 476 * @param isDuplicateName The map to put the duplicate check results in. 477 * @param cursor The query of calendars to check 478 * @param nameIndex The column of the query that contains the display name 479 */ 480 public static void checkForDuplicateNames( 481 Map<String, Boolean> isDuplicateName, Cursor cursor, int nameIndex) { 482 isDuplicateName.clear(); 483 cursor.moveToPosition(-1); 484 while (cursor.moveToNext()) { 485 String displayName = cursor.getString(nameIndex); 486 // Set it to true if we've seen this name before, false otherwise 487 if (displayName != null) { 488 isDuplicateName.put(displayName, isDuplicateName.containsKey(displayName)); 489 } 490 } 491 } 492 493 /** 494 * Null-safe object comparison 495 * 496 * @param s1 497 * @param s2 498 * @return 499 */ 500 public static boolean equals(Object o1, Object o2) { 501 return o1 == null ? o2 == null : o1.equals(o2); 502 } 503 504 public static void allowWeekForDetailView(boolean allowWeekView) { 505 mAllowWeekForDetailView = allowWeekView; 506 } 507} 508