Utils.java revision a865ce5e62e06b5fcd427defb0cce06705ed08f7
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.CalendarUtils.TimeZoneUtils;
32import android.util.Log;
33import android.view.animation.AlphaAnimation;
34import android.widget.ViewFlipper;
35
36import java.util.Calendar;
37import java.util.Formatter;
38import java.util.List;
39import java.util.Map;
40
41public class Utils {
42    private static final boolean DEBUG = true;
43    private static final String TAG = "CalUtils";
44    private static final int CLEAR_ALPHA_MASK = 0x00FFFFFF;
45    private static final int HIGH_ALPHA = 255 << 24;
46    private static final int MED_ALPHA = 180 << 24;
47    private static final int LOW_ALPHA = 150 << 24;
48
49    protected static final String OPEN_EMAIL_MARKER = " <";
50    protected static final String CLOSE_EMAIL_MARKER = ">";
51    /* The corner should be rounded on the top right and bottom right */
52    private static final float[] CORNERS = new float[] {0, 0, 5, 5, 5, 5, 0, 0};
53
54    // The name of the shared preferences file. This name must be maintained for historical
55    // reasons, as it's what PreferenceManager assigned the first time the file was created.
56    private static final String SHARED_PREFS_NAME = "com.android.calendar_preferences";
57
58    private static final TimeZoneUtils mTZUtils = new TimeZoneUtils(SHARED_PREFS_NAME);
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     * Writes a new home time zone to the db.
77     *
78     * Updates the home time zone in the db asynchronously and updates
79     * the local cache. Sending a time zone of **tbd** will cause it to
80     * be set to the device's time zone. null or empty tz will be ignored.
81     *
82     * @param context The calling activity
83     * @param timeZone The time zone to set Calendar to, or **tbd**
84     */
85    public static void setTimeZone(Context context, String timeZone) {
86        mTZUtils.setTimeZone(context, timeZone);
87    }
88
89    /**
90     * Gets the time zone that Calendar should be displayed in
91     *
92     * This is a helper method to get the appropriate time zone for Calendar. If this
93     * is the first time this method has been called it will initiate an asynchronous
94     * query to verify that the data in preferences is correct. The callback supplied
95     * will only be called if this query returns a value other than what is stored in
96     * preferences and should cause the calling activity to refresh anything that
97     * depends on calling this method.
98     *
99     * @param context The calling activity
100     * @param callback The runnable that should execute if a query returns new values
101     * @return The string value representing the time zone Calendar should display
102     */
103    public static String getTimeZone(Context context, Runnable callback) {
104        return mTZUtils.getTimeZone(context, callback);
105    }
106
107    /**
108     * Formats a date or a time range according to the local conventions.
109     *
110     * @param context the context is required only if the time is shown
111     * @param startMillis the start time in UTC milliseconds
112     * @param endMillis the end time in UTC milliseconds
113     * @param flags a bit mask of options See
114     * {@link #formatDateRange(Context, Formatter, long, long, int, String) formatDateRange}
115     * @return a string containing the formatted date/time range.
116     */
117    public static String formatDateRange(Context context, long startMillis,
118            long endMillis, int flags) {
119        return mTZUtils.formatDateRange(context, startMillis, endMillis, flags);
120    }
121
122    static void setSharedPreference(Context context, String key, String value) {
123        SharedPreferences prefs = CalendarPreferenceActivity.getSharedPreferences(context);
124        SharedPreferences.Editor editor = prefs.edit();
125        editor.putString(key, value);
126        editor.apply();
127    }
128
129    static void setDefaultView(Context context, int viewId) {
130        String activityString = CalendarApplication.ACTIVITY_NAMES[viewId];
131
132        SharedPreferences prefs = CalendarPreferenceActivity.getSharedPreferences(context);
133        SharedPreferences.Editor editor = prefs.edit();
134        if (viewId == CalendarApplication.AGENDA_VIEW_ID ||
135                viewId == CalendarApplication.DAY_VIEW_ID) {
136            // Record the (new) detail start view only for Agenda and Day
137            editor.putString(CalendarPreferenceActivity.KEY_DETAILED_VIEW, activityString);
138        }
139
140        // Record the (new) start view
141        editor.putString(CalendarPreferenceActivity.KEY_START_VIEW, activityString);
142        editor.apply();
143    }
144
145    public static final Time timeFromIntent(Intent intent) {
146        Time time = new Time();
147        time.set(timeFromIntentInMillis(intent));
148        return time;
149    }
150
151    public static MatrixCursor matrixCursorFromCursor(Cursor cursor) {
152        MatrixCursor newCursor = new MatrixCursor(cursor.getColumnNames());
153        int numColumns = cursor.getColumnCount();
154        String data[] = new String[numColumns];
155        cursor.moveToPosition(-1);
156        while (cursor.moveToNext()) {
157            for (int i = 0; i < numColumns; i++) {
158                data[i] = cursor.getString(i);
159            }
160            newCursor.addRow(data);
161        }
162        return newCursor;
163    }
164
165    /**
166     * Compares two cursors to see if they contain the same data.
167     *
168     * @return Returns true of the cursors contain the same data and are not null, false
169     * otherwise
170     */
171    public static boolean compareCursors(Cursor c1, Cursor c2) {
172        if(c1 == null || c2 == null) {
173            return false;
174        }
175
176        int numColumns = c1.getColumnCount();
177        if (numColumns != c2.getColumnCount()) {
178            return false;
179        }
180
181        if (c1.getCount() != c2.getCount()) {
182            return false;
183        }
184
185        c1.moveToPosition(-1);
186        c2.moveToPosition(-1);
187        while(c1.moveToNext() && c2.moveToNext()) {
188            for(int i = 0; i < numColumns; i++) {
189                if(!TextUtils.equals(c1.getString(i), c2.getString(i))) {
190                    return false;
191                }
192            }
193        }
194
195        return true;
196    }
197
198    /**
199     * If the given intent specifies a time (in milliseconds since the epoch),
200     * then that time is returned. Otherwise, the current time is returned.
201     */
202    public static final long timeFromIntentInMillis(Intent intent) {
203        // If the time was specified, then use that.  Otherwise, use the current time.
204        Uri data = intent.getData();
205        long millis = intent.getLongExtra(EVENT_BEGIN_TIME, -1);
206        if (millis == -1 && data != null && data.isHierarchical()) {
207            List<String> path = data.getPathSegments();
208            if(path.size() == 2 && path.get(0).equals("time")) {
209                try {
210                    millis = Long.valueOf(data.getLastPathSegment());
211                } catch (NumberFormatException e) {
212                    Log.i("Calendar", "timeFromIntentInMillis: Data existed but no valid time " +
213                            "found. Using current time.");
214                }
215            }
216        }
217        if (millis <= 0) {
218            millis = System.currentTimeMillis();
219        }
220        return millis;
221    }
222
223    public static final void applyAlphaAnimation(ViewFlipper v) {
224        AlphaAnimation in = new AlphaAnimation(0.0f, 1.0f);
225
226        in.setStartOffset(0);
227        in.setDuration(500);
228
229        AlphaAnimation out = new AlphaAnimation(1.0f, 0.0f);
230
231        out.setStartOffset(0);
232        out.setDuration(500);
233
234        v.setInAnimation(in);
235        v.setOutAnimation(out);
236    }
237
238    public static Drawable getColorChip(int color) {
239        /*
240         * We want the color chip to have a nice gradient using
241         * the color of the calendar. To do this we use a GradientDrawable.
242         * The color supplied has an alpha of FF so we first do:
243         * color & 0x00FFFFFF
244         * to clear the alpha. Then we add our alpha to it.
245         * We use 3 colors to get a step effect where it starts off very
246         * light and quickly becomes dark and then a slow transition to
247         * be even darker.
248         */
249        color &= CLEAR_ALPHA_MASK;
250        int startColor = color | HIGH_ALPHA;
251        int middleColor = color | MED_ALPHA;
252        int endColor = color | LOW_ALPHA;
253        int[] colors = new int[] {startColor, middleColor, endColor};
254        GradientDrawable d = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, colors);
255        d.setCornerRadii(CORNERS);
256        return d;
257    }
258
259    /**
260     * Formats the given Time object so that it gives the month and year
261     * (for example, "September 2007").
262     *
263     * @param time the time to format
264     * @return the string containing the weekday and the date
265     */
266    public static String formatMonthYear(Context context, Time time) {
267        return time.format(context.getResources().getString(R.string.month_year));
268    }
269
270    // TODO: replace this with the correct i18n way to do this
271    public static final String englishNthDay[] = {
272        "", "1st", "2nd", "3rd", "4th", "5th", "6th", "7th", "8th", "9th",
273        "10th", "11th", "12th", "13th", "14th", "15th", "16th", "17th", "18th", "19th",
274        "20th", "21st", "22nd", "23rd", "24th", "25th", "26th", "27th", "28th", "29th",
275        "30th", "31st"
276    };
277
278    public static String formatNth(int nth) {
279        return "the " + englishNthDay[nth];
280    }
281
282    /**
283     * Returns a list joined together by the provided delimiter, for example,
284     * ["a", "b", "c"] could be joined into "a,b,c"
285     *
286     * @param things the things to join together
287     * @param delim the delimiter to use
288     * @return a string contained the things joined together
289     */
290    public static String join(List<?> things, String delim) {
291        StringBuilder builder = new StringBuilder();
292        boolean first = true;
293        for (Object thing : things) {
294            if (first) {
295                first = false;
296            } else {
297                builder.append(delim);
298            }
299            builder.append(thing.toString());
300        }
301        return builder.toString();
302    }
303
304    /**
305     * Sets the time to the beginning of the day (midnight) by clearing the
306     * hour, minute, and second fields.
307     */
308    static void setTimeToStartOfDay(Time time) {
309        time.second = 0;
310        time.minute = 0;
311        time.hour = 0;
312    }
313
314    /**
315     * Get first day of week as android.text.format.Time constant.
316     * @return the first day of week in android.text.format.Time
317     */
318    public static int getFirstDayOfWeek() {
319        int startDay = Calendar.getInstance().getFirstDayOfWeek();
320        if (startDay == Calendar.SATURDAY) {
321            return Time.SATURDAY;
322        } else if (startDay == Calendar.MONDAY) {
323            return Time.MONDAY;
324        } else {
325            return Time.SUNDAY;
326        }
327    }
328
329    /**
330     * Determine whether the column position is Saturday or not.
331     * @param column the column position
332     * @param firstDayOfWeek the first day of week in android.text.format.Time
333     * @return true if the column is Saturday position
334     */
335    public static boolean isSaturday(int column, int firstDayOfWeek) {
336        return (firstDayOfWeek == Time.SUNDAY && column == 6)
337            || (firstDayOfWeek == Time.MONDAY && column == 5)
338            || (firstDayOfWeek == Time.SATURDAY && column == 0);
339    }
340
341    /**
342     * Determine whether the column position is Sunday or not.
343     * @param column the column position
344     * @param firstDayOfWeek the first day of week in android.text.format.Time
345     * @return true if the column is Sunday position
346     */
347    public static boolean isSunday(int column, int firstDayOfWeek) {
348        return (firstDayOfWeek == Time.SUNDAY && column == 0)
349            || (firstDayOfWeek == Time.MONDAY && column == 6)
350            || (firstDayOfWeek == Time.SATURDAY && column == 1);
351    }
352
353    /**
354     * Scan through a cursor of calendars and check if names are duplicated.
355     *
356     * This travels a cursor containing calendar display names and fills in the provided map with
357     * whether or not each name is repeated.
358     * @param isDuplicateName The map to put the duplicate check results in.
359     * @param cursor The query of calendars to check
360     * @param nameIndex The column of the query that contains the display name
361     */
362    public static void checkForDuplicateNames(Map<String, Boolean> isDuplicateName, Cursor cursor,
363            int nameIndex) {
364        isDuplicateName.clear();
365        cursor.moveToPosition(-1);
366        while (cursor.moveToNext()) {
367            String displayName = cursor.getString(nameIndex);
368            // Set it to true if we've seen this name before, false otherwise
369            if (displayName != null) {
370                isDuplicateName.put(displayName, isDuplicateName.containsKey(displayName));
371            }
372        }
373    }
374}
375