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