Utils.java revision 235d59cf61769ec8ab777d81cd1ceb2e7530f439
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 android.content.Context; 22import android.content.Intent; 23import android.content.SharedPreferences; 24import android.database.Cursor; 25import android.database.MatrixCursor; 26import android.graphics.drawable.Drawable; 27import android.graphics.drawable.GradientDrawable; 28import android.net.Uri; 29import android.text.TextUtils; 30import android.text.format.Time; 31import android.util.Log; 32import android.view.animation.AlphaAnimation; 33import android.widget.ViewFlipper; 34 35import java.util.Calendar; 36import java.util.HashSet; 37import java.util.List; 38import java.util.Map; 39 40public class Utils { 41 private static final int CLEAR_ALPHA_MASK = 0x00FFFFFF; 42 private static final int HIGH_ALPHA = 255 << 24; 43 private static final int MED_ALPHA = 180 << 24; 44 private static final int LOW_ALPHA = 150 << 24; 45 46 protected static final String OPEN_EMAIL_MARKER = " <"; 47 protected static final String CLOSE_EMAIL_MARKER = ">"; 48 49 /* The corner should be rounded on the top right and bottom right */ 50 private static final float[] CORNERS = new float[] {0, 0, 5, 5, 5, 5, 0, 0}; 51 52 private volatile static boolean mFirstTZRequest = true; 53 private volatile static boolean mTZQueryInProgress = false; 54 55 private volatile static boolean mUseHomeTZ = false; 56 private volatile static String mHomeTZ = Time.getCurrentTimezone(); 57 58 private static HashSet<Runnable> mTZCallbacks = new HashSet<Runnable>(); 59 60 public static void startActivity(Context context, String className, long time) { 61 Intent intent = new Intent(Intent.ACTION_VIEW); 62 63 intent.setClassName(context, className); 64 intent.putExtra(EVENT_BEGIN_TIME, time); 65 intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_SINGLE_TOP); 66 67 context.startActivity(intent); 68 } 69 70 static String getSharedPreference(Context context, String key, String defaultValue) { 71 SharedPreferences prefs = CalendarPreferenceActivity.getSharedPreferences(context); 72 return prefs.getString(key, defaultValue); 73 } 74 75 /** 76 * Gets the time zone that Calendar should be displayed in 77 * 78 * This is a helper method to get the appropriate time zone for Calendar. If this 79 * is the first time this method has been called it will initiate an asynchronous 80 * query to verify that the data in preferences is correct. The callback supplied 81 * will only be called if this query returns a value other than what is stored in 82 * preferences and should cause the calling activity to refresh anything that 83 * depends on calling this method. 84 * 85 * @param context The calling activity 86 * @param callback The runnable that should execute if a query returns new values 87 * @return The string value representing the time zone Calendar should display 88 */ 89 public static String getTimeZone(Context context, Runnable callback) { 90 synchronized (mTZCallbacks){ 91 if (mFirstTZRequest) { 92 mTZQueryInProgress = true; 93 mFirstTZRequest = false; 94 95 SharedPreferences prefs = CalendarPreferenceActivity.getSharedPreferences(context); 96 mUseHomeTZ = prefs.getBoolean( 97 CalendarPreferenceActivity.KEY_HOME_TZ_ENABLED, false); 98 mHomeTZ = prefs.getString( 99 CalendarPreferenceActivity.KEY_HOME_TZ, Time.getCurrentTimezone()); 100 // TODO kick off async query 101 // When the async query returns it should synchronize on 102 // mTZCallbacks, update mUseHomeTZ, mHomeTZ, and the 103 // preferences, set mTZQueryInProgress to false, and call all 104 // the runnables in mTZCallbacks. 105 // TODO remove this line when we have a query 106 mTZQueryInProgress = false; 107 } 108 if (mTZQueryInProgress) { 109 mTZCallbacks.add(callback); 110 } 111 } 112 return mUseHomeTZ ? mHomeTZ : Time.getCurrentTimezone(); 113 } 114 115 static void setSharedPreference(Context context, String key, String value) { 116 SharedPreferences prefs = CalendarPreferenceActivity.getSharedPreferences(context); 117 SharedPreferences.Editor editor = prefs.edit(); 118 editor.putString(key, value); 119 editor.commit(); 120 } 121 122 static void setDefaultView(Context context, int viewId) { 123 String activityString = CalendarApplication.ACTIVITY_NAMES[viewId]; 124 125 SharedPreferences prefs = CalendarPreferenceActivity.getSharedPreferences(context); 126 SharedPreferences.Editor editor = prefs.edit(); 127 if (viewId == CalendarApplication.AGENDA_VIEW_ID || 128 viewId == CalendarApplication.DAY_VIEW_ID) { 129 // Record the (new) detail start view only for Agenda and Day 130 editor.putString(CalendarPreferenceActivity.KEY_DETAILED_VIEW, activityString); 131 } 132 133 // Record the (new) start view 134 editor.putString(CalendarPreferenceActivity.KEY_START_VIEW, activityString); 135 editor.commit(); 136 } 137 138 public static final Time timeFromIntent(Intent intent) { 139 Time time = new Time(); 140 time.set(timeFromIntentInMillis(intent)); 141 return time; 142 } 143 144 public static MatrixCursor matrixCursorFromCursor(Cursor cursor) { 145 MatrixCursor newCursor = new MatrixCursor(cursor.getColumnNames()); 146 int numColumns = cursor.getColumnCount(); 147 String data[] = new String[numColumns]; 148 cursor.moveToPosition(-1); 149 while (cursor.moveToNext()) { 150 for (int i = 0; i < numColumns; i++) { 151 data[i] = cursor.getString(i); 152 } 153 newCursor.addRow(data); 154 } 155 return newCursor; 156 } 157 158 /** 159 * Compares two cursors to see if they contain the same data. 160 * 161 * @return Returns true of the cursors contain the same data and are not null, false 162 * otherwise 163 */ 164 public static boolean compareCursors(Cursor c1, Cursor c2) { 165 if(c1 == null || c2 == null) { 166 return false; 167 } 168 169 int numColumns = c1.getColumnCount(); 170 if (numColumns != c2.getColumnCount()) { 171 return false; 172 } 173 174 if (c1.getCount() != c2.getCount()) { 175 return false; 176 } 177 178 c1.moveToPosition(-1); 179 c2.moveToPosition(-1); 180 while(c1.moveToNext() && c2.moveToNext()) { 181 for(int i = 0; i < numColumns; i++) { 182 if(!TextUtils.equals(c1.getString(i), c2.getString(i))) { 183 return false; 184 } 185 } 186 } 187 188 return true; 189 } 190 191 /** 192 * If the given intent specifies a time (in milliseconds since the epoch), 193 * then that time is returned. Otherwise, the current time is returned. 194 */ 195 public static final long timeFromIntentInMillis(Intent intent) { 196 // If the time was specified, then use that. Otherwise, use the current time. 197 Uri data = intent.getData(); 198 long millis = intent.getLongExtra(EVENT_BEGIN_TIME, -1); 199 if (millis == -1 && data != null && data.isHierarchical()) { 200 List<String> path = data.getPathSegments(); 201 if(path.size() == 2 && path.get(0).equals("time")) { 202 try { 203 millis = Long.valueOf(data.getLastPathSegment()); 204 } catch (NumberFormatException e) { 205 Log.i("Calendar", "timeFromIntentInMillis: Data existed but no valid time " + 206 "found. Using current time."); 207 } 208 } 209 } 210 if (millis <= 0) { 211 millis = System.currentTimeMillis(); 212 } 213 return millis; 214 } 215 216 public static final void applyAlphaAnimation(ViewFlipper v) { 217 AlphaAnimation in = new AlphaAnimation(0.0f, 1.0f); 218 219 in.setStartOffset(0); 220 in.setDuration(500); 221 222 AlphaAnimation out = new AlphaAnimation(1.0f, 0.0f); 223 224 out.setStartOffset(0); 225 out.setDuration(500); 226 227 v.setInAnimation(in); 228 v.setOutAnimation(out); 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 // TODO: replace this with the correct i18n way to do this 264 public static final String englishNthDay[] = { 265 "", "1st", "2nd", "3rd", "4th", "5th", "6th", "7th", "8th", "9th", 266 "10th", "11th", "12th", "13th", "14th", "15th", "16th", "17th", "18th", "19th", 267 "20th", "21st", "22nd", "23rd", "24th", "25th", "26th", "27th", "28th", "29th", 268 "30th", "31st" 269 }; 270 271 public static String formatNth(int nth) { 272 return "the " + englishNthDay[nth]; 273 } 274 275 /** 276 * Returns a list joined together by the provided delimiter, for example, 277 * ["a", "b", "c"] could be joined into "a,b,c" 278 * 279 * @param things the things to join together 280 * @param delim the delimiter to use 281 * @return a string contained the things joined together 282 */ 283 public static String join(List<?> things, String delim) { 284 StringBuilder builder = new StringBuilder(); 285 boolean first = true; 286 for (Object thing : things) { 287 if (first) { 288 first = false; 289 } else { 290 builder.append(delim); 291 } 292 builder.append(thing.toString()); 293 } 294 return builder.toString(); 295 } 296 297 /** 298 * Sets the time to the beginning of the day (midnight) by clearing the 299 * hour, minute, and second fields. 300 */ 301 static void setTimeToStartOfDay(Time time) { 302 time.second = 0; 303 time.minute = 0; 304 time.hour = 0; 305 } 306 307 /** 308 * Get first day of week as android.text.format.Time constant. 309 * @return the first day of week in android.text.format.Time 310 */ 311 public static int getFirstDayOfWeek() { 312 int startDay = Calendar.getInstance().getFirstDayOfWeek(); 313 if (startDay == Calendar.SATURDAY) { 314 return Time.SATURDAY; 315 } else if (startDay == Calendar.MONDAY) { 316 return Time.MONDAY; 317 } else { 318 return Time.SUNDAY; 319 } 320 } 321 322 /** 323 * Determine whether the column position is Saturday 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 Saturday position 327 */ 328 public static boolean isSaturday(int column, int firstDayOfWeek) { 329 return (firstDayOfWeek == Time.SUNDAY && column == 6) 330 || (firstDayOfWeek == Time.MONDAY && column == 5) 331 || (firstDayOfWeek == Time.SATURDAY && column == 0); 332 } 333 334 /** 335 * Determine whether the column position is Sunday or not. 336 * @param column the column position 337 * @param firstDayOfWeek the first day of week in android.text.format.Time 338 * @return true if the column is Sunday position 339 */ 340 public static boolean isSunday(int column, int firstDayOfWeek) { 341 return (firstDayOfWeek == Time.SUNDAY && column == 0) 342 || (firstDayOfWeek == Time.MONDAY && column == 6) 343 || (firstDayOfWeek == Time.SATURDAY && column == 1); 344 } 345 346 /** 347 * Scan through a cursor of calendars and check if names are duplicated. 348 * 349 * This travels a cursor containing calendar display names and fills in the provided map with 350 * whether or not each name is repeated. 351 * @param isDuplicateName The map to put the duplicate check results in. 352 * @param cursor The query of calendars to check 353 * @param nameIndex The column of the query that contains the display name 354 */ 355 public static void checkForDuplicateNames(Map<String, Boolean> isDuplicateName, Cursor cursor, 356 int nameIndex) { 357 isDuplicateName.clear(); 358 cursor.moveToPosition(-1); 359 while (cursor.moveToNext()) { 360 String displayName = cursor.getString(nameIndex); 361 // Set it to true if we've seen this name before, false otherwise 362 if (displayName != null) { 363 isDuplicateName.put(displayName, isDuplicateName.containsKey(displayName)); 364 } 365 } 366 } 367} 368