Utils.java revision 3ea333d41c04fd5f3a5d45f540c17894874429e8
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.content.SharedPreferences.Editor; 28import android.database.Cursor; 29import android.database.MatrixCursor; 30import android.graphics.drawable.Drawable; 31import android.graphics.drawable.GradientDrawable; 32import android.net.Uri; 33import android.os.AsyncTask; 34import android.os.Bundle; 35import android.text.TextUtils; 36import android.text.format.Time; 37import android.util.Log; 38 39import java.util.Calendar; 40import java.util.List; 41import java.util.Map; 42import java.util.TimeZone; 43 44public class Utils { 45 // Set to 0 until we have UI to perform undo 46 public static final long UNDO_DELAY = 0; 47 48 // For recurring events which instances of the series are being modified 49 public static final int MODIFY_UNINITIALIZED = 0; 50 public static final int MODIFY_SELECTED = 1; 51 public static final int MODIFY_ALL_FOLLOWING = 2; 52 public static final int MODIFY_ALL = 3; 53 54 // When the edit event view finishes it passes back the appropriate exit code. 55 public static final int DONE_REVERT = 0; 56 public static final int DONE_SAVE = 1; 57 public static final int DONE_DELETE = 2; 58 59 private static final int CLEAR_ALPHA_MASK = 0x00FFFFFF; 60 private static final int HIGH_ALPHA = 255 << 24; 61 private static final int MED_ALPHA = 180 << 24; 62 private static final int LOW_ALPHA = 150 << 24; 63 64 protected static final String OPEN_EMAIL_MARKER = " <"; 65 protected static final String CLOSE_EMAIL_MARKER = ">"; 66 67 /* The corner should be rounded on the top right and bottom right */ 68 private static final float[] CORNERS = new float[] {0, 0, 5, 5, 5, 5, 0, 0}; 69 70 public static final String INTENT_KEY_DETAIL_VIEW = "DETAIL_VIEW"; 71 public static final String INTENT_KEY_VIEW_TYPE = "VIEW"; 72 public static final String INTENT_VALUE_VIEW_TYPE_DAY = "DAY"; 73 74 public static int getViewTypeFromIntentAndSharedPref(Activity activity) { 75 Bundle extras = activity.getIntent().getExtras(); 76 SharedPreferences prefs = CalendarPreferenceActivity.getSharedPreferences(activity); 77 78 if (extras != null) { 79 if (extras.getBoolean(INTENT_KEY_DETAIL_VIEW, false)) { 80 // This is the "detail" view which is either agenda or day view 81 return prefs.getInt(CalendarPreferenceActivity.KEY_DETAILED_VIEW, 82 CalendarPreferenceActivity.DEFAULT_DETAILED_VIEW); 83 } else if (INTENT_VALUE_VIEW_TYPE_DAY.equals(extras.getString(INTENT_KEY_VIEW_TYPE))) { 84 // Not sure who uses this. This logic came from LaunchActivity 85 return ViewType.DAY; 86 } 87 } 88 89 // Default to the last view 90 return prefs.getInt(CalendarPreferenceActivity.KEY_START_VIEW, 91 CalendarPreferenceActivity.DEFAULT_START_VIEW); 92 } 93 94 public static String getSharedPreference(Context context, String key, String defaultValue) { 95 SharedPreferences prefs = CalendarPreferenceActivity.getSharedPreferences(context); 96 return prefs.getString(key, defaultValue); 97 } 98 99 public static int getSharedPreference(Context context, String key, int defaultValue) { 100 SharedPreferences prefs = CalendarPreferenceActivity.getSharedPreferences(context); 101 return prefs.getInt(key, defaultValue); 102 } 103 104 105 private static class PreferenceCommitTask extends 106 AsyncTask<SharedPreferences.Editor, Integer, Boolean> { 107 @Override 108 protected Boolean doInBackground(Editor... params) { 109 Editor editor = params[0]; 110 return editor.commit(); 111 } 112 } 113 114 /** 115 * Commits the given shared preferences editor asynchronously in the 116 * background. 117 * 118 * @param editor the editor to commit 119 */ 120 public static void commitSharedPreferencesEditor(Editor editor) { 121 (new PreferenceCommitTask()).execute(editor); 122 } 123 124 /** 125 * Asynchronously sets the preference with the given key to the given value 126 * 127 * @param context the context to use to get preferences from 128 * @param key the key of the preference to set 129 * @param value the value to set 130 */ 131 public static void setSharedPreference(Context context, String key, String value) { 132 SharedPreferences prefs = CalendarPreferenceActivity.getSharedPreferences(context); 133 Editor editor = prefs.edit().putString(key, value); 134 commitSharedPreferencesEditor(editor); 135 } 136 137 /** 138 * Save default agenda/day/week/month view for next time 139 * 140 * @param context 141 * @param viewId {@link CalendarController.ViewType} 142 */ 143 static void setDefaultView(Context context, int viewId) { 144 SharedPreferences prefs = CalendarPreferenceActivity.getSharedPreferences(context); 145 SharedPreferences.Editor editor = prefs.edit(); 146 147 if (viewId == CalendarController.ViewType.AGENDA 148 || viewId == CalendarController.ViewType.DAY) { 149 // Record the (new) detail start view only for Agenda and Day 150 editor.putInt(CalendarPreferenceActivity.KEY_DETAILED_VIEW, viewId); 151 } 152 153 // Record the (new) start view 154 editor.putInt(CalendarPreferenceActivity.KEY_START_VIEW, viewId); 155 156 commitSharedPreferencesEditor(editor); 157 } 158 159 public static MatrixCursor matrixCursorFromCursor(Cursor cursor) { 160 MatrixCursor newCursor = new MatrixCursor(cursor.getColumnNames()); 161 int numColumns = cursor.getColumnCount(); 162 String data[] = new String[numColumns]; 163 cursor.moveToPosition(-1); 164 while (cursor.moveToNext()) { 165 for (int i = 0; i < numColumns; i++) { 166 data[i] = cursor.getString(i); 167 } 168 newCursor.addRow(data); 169 } 170 return newCursor; 171 } 172 173 /** 174 * Compares two cursors to see if they contain the same data. 175 * 176 * @return Returns true of the cursors contain the same data and are not null, false 177 * otherwise 178 */ 179 public static boolean compareCursors(Cursor c1, Cursor c2) { 180 if(c1 == null || c2 == null) { 181 return false; 182 } 183 184 int numColumns = c1.getColumnCount(); 185 if (numColumns != c2.getColumnCount()) { 186 return false; 187 } 188 189 if (c1.getCount() != c2.getCount()) { 190 return false; 191 } 192 193 c1.moveToPosition(-1); 194 c2.moveToPosition(-1); 195 while(c1.moveToNext() && c2.moveToNext()) { 196 for(int i = 0; i < numColumns; i++) { 197 if(!TextUtils.equals(c1.getString(i), c2.getString(i))) { 198 return false; 199 } 200 } 201 } 202 203 return true; 204 } 205 206 /** 207 * If the given intent specifies a time (in milliseconds since the epoch), 208 * then that time is returned. Otherwise, the current time is returned. 209 */ 210 public static final long timeFromIntentInMillis(Intent intent) { 211 // If the time was specified, then use that. Otherwise, use the current time. 212 Uri data = intent.getData(); 213 long millis = intent.getLongExtra(EVENT_BEGIN_TIME, -1); 214 if (millis == -1 && data != null && data.isHierarchical()) { 215 List<String> path = data.getPathSegments(); 216 if(path.size() == 2 && path.get(0).equals("time")) { 217 try { 218 millis = Long.valueOf(data.getLastPathSegment()); 219 } catch (NumberFormatException e) { 220 Log.i("Calendar", "timeFromIntentInMillis: Data existed but no valid time " + 221 "found. Using current time."); 222 } 223 } 224 } 225 if (millis <= 0) { 226 millis = System.currentTimeMillis(); 227 } 228 return millis; 229 } 230 231 public static Drawable getColorChip(int color) { 232 /* 233 * We want the color chip to have a nice gradient using 234 * the color of the calendar. To do this we use a GradientDrawable. 235 * The color supplied has an alpha of FF so we first do: 236 * color & 0x00FFFFFF 237 * to clear the alpha. Then we add our alpha to it. 238 * We use 3 colors to get a step effect where it starts off very 239 * light and quickly becomes dark and then a slow transition to 240 * be even darker. 241 */ 242 color &= CLEAR_ALPHA_MASK; 243 int startColor = color | HIGH_ALPHA; 244 int middleColor = color | MED_ALPHA; 245 int endColor = color | LOW_ALPHA; 246 int[] colors = new int[] {startColor, middleColor, endColor}; 247 GradientDrawable d = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, colors); 248 d.setCornerRadii(CORNERS); 249 return d; 250 } 251 252 /** 253 * Formats the given Time object so that it gives the month and year 254 * (for example, "September 2007"). 255 * 256 * @param time the time to format 257 * @return the string containing the weekday and the date 258 */ 259 public static String formatMonthYear(Context context, Time time) { 260 return time.format(context.getResources().getString(R.string.month_year)); 261 } 262 263 /** 264 * Returns a list joined together by the provided delimiter, for example, 265 * ["a", "b", "c"] could be joined into "a,b,c" 266 * 267 * @param things the things to join together 268 * @param delim the delimiter to use 269 * @return a string contained the things joined together 270 */ 271 public static String join(List<?> things, String delim) { 272 StringBuilder builder = new StringBuilder(); 273 boolean first = true; 274 for (Object thing : things) { 275 if (first) { 276 first = false; 277 } else { 278 builder.append(delim); 279 } 280 builder.append(thing.toString()); 281 } 282 return builder.toString(); 283 } 284 285 /** 286 * Get first day of week as android.text.format.Time constant. 287 * @return the first day of week in android.text.format.Time 288 */ 289 public static int getFirstDayOfWeek(Context context) { 290 SharedPreferences prefs = CalendarPreferenceActivity.getSharedPreferences(context); 291 String pref = prefs.getString(CalendarPreferenceActivity.KEY_WEEK_START_DAY, 292 CalendarPreferenceActivity.WEEK_START_DEFAULT); 293 294 int startDay; 295 if (CalendarPreferenceActivity.WEEK_START_DEFAULT.equals(pref)) { 296 startDay = Calendar.getInstance().getFirstDayOfWeek(); 297 } else { 298 startDay = Integer.parseInt(pref); 299 } 300 301 if (startDay == Calendar.SATURDAY) { 302 return Time.SATURDAY; 303 } else if (startDay == Calendar.MONDAY) { 304 return Time.MONDAY; 305 } else { 306 return Time.SUNDAY; 307 } 308 } 309 310 /** 311 * Determine whether the column position is Saturday or not. 312 * @param column the column position 313 * @param firstDayOfWeek the first day of week in android.text.format.Time 314 * @return true if the column is Saturday position 315 */ 316 public static boolean isSaturday(int column, int firstDayOfWeek) { 317 return (firstDayOfWeek == Time.SUNDAY && column == 6) 318 || (firstDayOfWeek == Time.MONDAY && column == 5) 319 || (firstDayOfWeek == Time.SATURDAY && column == 0); 320 } 321 322 /** 323 * Determine whether the column position is Sunday or not. 324 * @param column the column position 325 * @param firstDayOfWeek the first day of week in android.text.format.Time 326 * @return true if the column is Sunday position 327 */ 328 public static boolean isSunday(int column, int firstDayOfWeek) { 329 return (firstDayOfWeek == Time.SUNDAY && column == 0) 330 || (firstDayOfWeek == Time.MONDAY && column == 6) 331 || (firstDayOfWeek == Time.SATURDAY && column == 1); 332 } 333 334 /** 335 * Convert given UTC time into current local time. 336 * 337 * @param recycle Time object to recycle, otherwise null. 338 * @param utcTime Time to convert, in UTC. 339 */ 340 public static long convertUtcToLocal(Time recycle, long utcTime) { 341 if (recycle == null) { 342 recycle = new Time(); 343 } 344 recycle.timezone = Time.TIMEZONE_UTC; 345 recycle.set(utcTime); 346 recycle.timezone = TimeZone.getDefault().getID(); 347 return recycle.normalize(true); 348 } 349 350 /** 351 * Scan through a cursor of calendars and check if names are duplicated. 352 * 353 * This travels a cursor containing calendar display names and fills in the provided map with 354 * whether or not each name is repeated. 355 * @param isDuplicateName The map to put the duplicate check results in. 356 * @param cursor The query of calendars to check 357 * @param nameIndex The column of the query that contains the display name 358 */ 359 public static void checkForDuplicateNames(Map<String, Boolean> isDuplicateName, Cursor cursor, 360 int nameIndex) { 361 isDuplicateName.clear(); 362 cursor.moveToPosition(-1); 363 while (cursor.moveToNext()) { 364 String displayName = cursor.getString(nameIndex); 365 // Set it to true if we've seen this name before, false otherwise 366 if (displayName != null) { 367 isDuplicateName.put(displayName, isDuplicateName.containsKey(displayName)); 368 } 369 } 370 } 371 372 /** 373 * Null-safe object comparison 374 * @param s1 375 * @param s2 376 * @return 377 */ 378 public static boolean equals(Object o1, Object o2) { 379 return o1 == null ? o2 == null : o1.equals(o2); 380 } 381} 382