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