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