Utils.java revision 3a4ba0db218b830af3dd17fde2952125a2e50fdc
1d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson/*
2d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson * Copyright (C) 2012 The Android Open Source Project
3d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson *
4d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson * Licensed under the Apache License, Version 2.0 (the "License");
5d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson * you may not use this file except in compliance with the License.
6d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson * You may obtain a copy of the License at
7d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson *
8d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson *      http://www.apache.org/licenses/LICENSE-2.0
9d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson *
10d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson * Unless required by applicable law or agreed to in writing, software
11d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson * distributed under the License is distributed on an "AS IS" BASIS,
12d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson * See the License for the specific language governing permissions and
14d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson * limitations under the License.
15d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson */
16d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson
17d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelsonpackage com.android.deskclock;
18d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson
1978b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzsteinimport android.animation.Animator;
2078b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzsteinimport android.animation.AnimatorSet;
2178b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzsteinimport android.animation.ObjectAnimator;
2278b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzsteinimport android.animation.TimeInterpolator;
230b7c93a6b691b25ccbcd8d683774d3d0d351a8e9Robyn Coultasimport android.app.AlarmManager;
240b7c93a6b691b25ccbcd8d683774d3d0d351a8e9Robyn Coultasimport android.app.PendingIntent;
25d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelsonimport android.content.Context;
26d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelsonimport android.content.Intent;
27de5f183f54b5d10bc8cbebb977c7c8ab391ad3f5Isaac Katzenelsonimport android.content.SharedPreferences;
28d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelsonimport android.content.pm.PackageInfo;
29d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelsonimport android.content.pm.PackageManager.NameNotFoundException;
30d4bbc122b90c63c5906ad32c4652b72c9409fd44Isaac Katzenelsonimport android.content.res.Resources;
3178b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzsteinimport android.graphics.Color;
3278b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzsteinimport android.graphics.Paint;
3378b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzsteinimport android.graphics.PorterDuff;
3478b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzsteinimport android.graphics.PorterDuffColorFilter;
35d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelsonimport android.net.Uri;
364fd80b58da8ad66e0e4a23cbf7c900174727fb4eRobyn Coultasimport android.os.Build;
3778b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzsteinimport android.os.Handler;
38287f2d82aaf91d881f5de50a133d21684a8d4821Sam Blitzsteinimport android.os.SystemClock;
3978b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzsteinimport android.preference.PreferenceManager;
4078b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzsteinimport android.provider.Settings;
41690f451a6aea35d944cb2eaddf488d3acf377184Isaac Katzenelsonimport android.text.Spannable;
42690f451a6aea35d944cb2eaddf488d3acf377184Isaac Katzenelsonimport android.text.SpannableString;
43d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelsonimport android.text.TextUtils;
4478b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzsteinimport android.text.format.DateFormat;
4562b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kungimport android.text.format.DateUtils;
46eb3a1f0714e209a8335d84142778465aa6b44c5cRobyn Coultasimport android.text.format.Time;
47690f451a6aea35d944cb2eaddf488d3acf377184Isaac Katzenelsonimport android.text.style.AbsoluteSizeSpan;
48690f451a6aea35d944cb2eaddf488d3acf377184Isaac Katzenelsonimport android.text.style.StyleSpan;
49690f451a6aea35d944cb2eaddf488d3acf377184Isaac Katzenelsonimport android.text.style.TypefaceSpan;
50d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelsonimport android.view.MenuItem;
5178b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzsteinimport android.view.View;
5278b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzsteinimport android.view.animation.AccelerateInterpolator;
5378b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzsteinimport android.view.animation.DecelerateInterpolator;
54690f451a6aea35d944cb2eaddf488d3acf377184Isaac Katzenelsonimport android.widget.TextClock;
5578b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzsteinimport android.widget.TextView;
56d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson
57de5f183f54b5d10bc8cbebb977c7c8ab391ad3f5Isaac Katzenelsonimport com.android.deskclock.stopwatch.Stopwatches;
5898b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultasimport com.android.deskclock.timer.Timers;
59d4bbc122b90c63c5906ad32c4652b72c9409fd44Isaac Katzenelsonimport com.android.deskclock.worldclock.CityObj;
60de5f183f54b5d10bc8cbebb977c7c8ab391ad3f5Isaac Katzenelson
6135300f84cef68e7f5666ec88591496aaaad242d1Isaac Katzenelsonimport java.text.SimpleDateFormat;
6278b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzsteinimport java.util.Calendar;
6335300f84cef68e7f5666ec88591496aaaad242d1Isaac Katzenelsonimport java.util.Date;
64d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelsonimport java.util.Locale;
6562b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kungimport java.util.TimeZone;
66d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson
67d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson
68d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelsonpublic class Utils {
69d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson    private final static String PARAM_LANGUAGE_CODE = "hl";
70d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson
71d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson    /**
72d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson     * Help URL query parameter key for the app version.
73d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson     */
74d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson    private final static String PARAM_VERSION = "version";
75d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson
76d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson    /**
77d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson     * Cached version code to prevent repeated calls to the package manager.
78d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson     */
79d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson    private static String sCachedVersionCode = null;
80d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson
8178b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein    /** Types that may be used for clock displays. **/
8278b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein    public static final String CLOCK_TYPE_DIGITAL = "digital";
8378b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein    public static final String CLOCK_TYPE_ANALOG = "analog";
84d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson
857048625adb7c349deab4c00b2d85bcf65c164ae5Isaac Katzenelson    /**
86f1ce98a6e2972854968488d2a28adbc29364b0f9Isaac Katzenelson     * Returns whether the SDK is KitKat or later
874fd80b58da8ad66e0e4a23cbf7c900174727fb4eRobyn Coultas     */
88f1ce98a6e2972854968488d2a28adbc29364b0f9Isaac Katzenelson    public static boolean isKitKatOrLater() {
89f1ce98a6e2972854968488d2a28adbc29364b0f9Isaac Katzenelson        return Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2;
904fd80b58da8ad66e0e4a23cbf7c900174727fb4eRobyn Coultas    }
914fd80b58da8ad66e0e4a23cbf7c900174727fb4eRobyn Coultas
927048625adb7c349deab4c00b2d85bcf65c164ae5Isaac Katzenelson
93d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson    public static void prepareHelpMenuItem(Context context, MenuItem helpMenuItem) {
94d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson        String helpUrlString = context.getResources().getString(R.string.desk_clock_help_url);
95d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson        if (TextUtils.isEmpty(helpUrlString)) {
96d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson            // The help url string is empty or null, so set the help menu item to be invisible.
97d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson            helpMenuItem.setVisible(false);
98d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson            return;
99d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson        }
100d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson        // The help url string exists, so first add in some extra query parameters.  87
101d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson        final Uri fullUri = uriWithAddedParameters(context, Uri.parse(helpUrlString));
102d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson
103d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson        // Then, create an intent that will be fired when the user
104d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson        // selects this help menu item.
105d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson        Intent intent = new Intent(Intent.ACTION_VIEW, fullUri);
106d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
107d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson                | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
108d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson
109d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson        // Set the intent to the help menu item, show the help menu item in the overflow
110d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson        // menu, and make it visible.
111d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson        helpMenuItem.setIntent(intent);
112d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson        helpMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
113d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson        helpMenuItem.setVisible(true);
114d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson    }
115d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson
116d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson    /**
117d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson     * Adds two query parameters into the Uri, namely the language code and the version code
1188322b2fa9d474fee9e7950fec68bd35e03fc9861Robyn Coultas     * of the application's package as gotten via the context.
119d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson     * @return the uri with added query parameters
120d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson     */
121d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson    private static Uri uriWithAddedParameters(Context context, Uri baseUri) {
122d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson        Uri.Builder builder = baseUri.buildUpon();
123d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson
124d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson        // Add in the preferred language
125d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson        builder.appendQueryParameter(PARAM_LANGUAGE_CODE, Locale.getDefault().toString());
126d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson
127d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson        // Add in the package version code
128d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson        if (sCachedVersionCode == null) {
129d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson            // There is no cached version code, so try to get it from the package manager.
130d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson            try {
131d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson                // cache the version code
132d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson                PackageInfo info = context.getPackageManager().getPackageInfo(
133d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson                        context.getPackageName(), 0);
134d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson                sCachedVersionCode = Integer.toString(info.versionCode);
135d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson
136d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson                // append the version code to the uri
137d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson                builder.appendQueryParameter(PARAM_VERSION, sCachedVersionCode);
138d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson            } catch (NameNotFoundException e) {
139d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson                // Cannot find the package name, so don't add in the version parameter
140d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson                // This shouldn't happen.
141d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson                Log.wtf("Invalid package name for context " + e);
142d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson            }
143d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson        } else {
144d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson            builder.appendQueryParameter(PARAM_VERSION, sCachedVersionCode);
145d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson        }
146d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson
147d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson        // Build the full uri and return it
148d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson        return builder.build();
149d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson    }
150d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson
151287f2d82aaf91d881f5de50a133d21684a8d4821Sam Blitzstein    public static long getTimeNow() {
152287f2d82aaf91d881f5de50a133d21684a8d4821Sam Blitzstein        return SystemClock.elapsedRealtime();
153287f2d82aaf91d881f5de50a133d21684a8d4821Sam Blitzstein    }
154287f2d82aaf91d881f5de50a133d21684a8d4821Sam Blitzstein
15500cb5981c984bc13aa2f6af510a9dd9a784f2af0Sam Blitzstein    /**
15600cb5981c984bc13aa2f6af510a9dd9a784f2af0Sam Blitzstein     * Calculate the amount by which the radius of a CircleTimerView should be offset by the any
15700cb5981c984bc13aa2f6af510a9dd9a784f2af0Sam Blitzstein     * of the extra painted objects.
15800cb5981c984bc13aa2f6af510a9dd9a784f2af0Sam Blitzstein     */
15900cb5981c984bc13aa2f6af510a9dd9a784f2af0Sam Blitzstein    public static float calculateRadiusOffset(
1608322b2fa9d474fee9e7950fec68bd35e03fc9861Robyn Coultas            float strokeSize, float dotStrokeSize, float markerStrokeSize) {
1618322b2fa9d474fee9e7950fec68bd35e03fc9861Robyn Coultas        return Math.max(strokeSize, Math.max(dotStrokeSize, markerStrokeSize));
16200cb5981c984bc13aa2f6af510a9dd9a784f2af0Sam Blitzstein    }
16300cb5981c984bc13aa2f6af510a9dd9a784f2af0Sam Blitzstein
164efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas    /**
165efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas     * Uses {@link Utils#calculateRadiusOffset(float, float, float)} after fetching the values
166efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas     * from the resources just as {@link CircleTimerView#init(android.content.Context)} does.
167efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas     */
168efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas    public static float calculateRadiusOffset(Resources resources) {
169efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas        if (resources != null) {
170efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas            float strokeSize = resources.getDimension(R.dimen.circletimer_circle_size);
1718322b2fa9d474fee9e7950fec68bd35e03fc9861Robyn Coultas            float dotStrokeSize = resources.getDimension(R.dimen.circletimer_dot_size);
172efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas            float markerStrokeSize = resources.getDimension(R.dimen.circletimer_marker_size);
1738322b2fa9d474fee9e7950fec68bd35e03fc9861Robyn Coultas            return calculateRadiusOffset(strokeSize, dotStrokeSize, markerStrokeSize);
174efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas        } else {
175efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas            return 0f;
176efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas        }
177efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas    }
178efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas
17972332c2290d17c344ece074726f72b35ea469f31Sam Blitzstein    /**  The pressed color used throughout the app. If this method is changed, it will not have
18072332c2290d17c344ece074726f72b35ea469f31Sam Blitzstein     *   any effect on the button press states, and those must be changed separately.
18172332c2290d17c344ece074726f72b35ea469f31Sam Blitzstein    **/
18272332c2290d17c344ece074726f72b35ea469f31Sam Blitzstein    public static int getPressedColorId() {
18372332c2290d17c344ece074726f72b35ea469f31Sam Blitzstein        return R.color.clock_red;
18472332c2290d17c344ece074726f72b35ea469f31Sam Blitzstein    }
18572332c2290d17c344ece074726f72b35ea469f31Sam Blitzstein
18672332c2290d17c344ece074726f72b35ea469f31Sam Blitzstein    /**  The un-pressed color used throughout the app. If this method is changed, it will not have
18772332c2290d17c344ece074726f72b35ea469f31Sam Blitzstein     *   any effect on the button press states, and those must be changed separately.
18872332c2290d17c344ece074726f72b35ea469f31Sam Blitzstein    **/
18972332c2290d17c344ece074726f72b35ea469f31Sam Blitzstein    public static int getGrayColorId() {
19072332c2290d17c344ece074726f72b35ea469f31Sam Blitzstein        return R.color.clock_gray;
19172332c2290d17c344ece074726f72b35ea469f31Sam Blitzstein    }
192de5f183f54b5d10bc8cbebb977c7c8ab391ad3f5Isaac Katzenelson
193de5f183f54b5d10bc8cbebb977c7c8ab391ad3f5Isaac Katzenelson    /**
194de5f183f54b5d10bc8cbebb977c7c8ab391ad3f5Isaac Katzenelson     * Clears the persistent data of stopwatch (start time, state, laps, etc...).
195de5f183f54b5d10bc8cbebb977c7c8ab391ad3f5Isaac Katzenelson     */
196de5f183f54b5d10bc8cbebb977c7c8ab391ad3f5Isaac Katzenelson    public static void clearSwSharedPref(SharedPreferences prefs) {
197de5f183f54b5d10bc8cbebb977c7c8ab391ad3f5Isaac Katzenelson        SharedPreferences.Editor editor = prefs.edit();
198de5f183f54b5d10bc8cbebb977c7c8ab391ad3f5Isaac Katzenelson        editor.remove (Stopwatches.PREF_START_TIME);
199de5f183f54b5d10bc8cbebb977c7c8ab391ad3f5Isaac Katzenelson        editor.remove (Stopwatches.PREF_ACCUM_TIME);
200de5f183f54b5d10bc8cbebb977c7c8ab391ad3f5Isaac Katzenelson        editor.remove (Stopwatches.PREF_STATE);
201de5f183f54b5d10bc8cbebb977c7c8ab391ad3f5Isaac Katzenelson        int lapNum = prefs.getInt(Stopwatches.PREF_LAP_NUM, Stopwatches.STOPWATCH_RESET);
202de5f183f54b5d10bc8cbebb977c7c8ab391ad3f5Isaac Katzenelson        for (int i = 0; i < lapNum; i++) {
203de5f183f54b5d10bc8cbebb977c7c8ab391ad3f5Isaac Katzenelson            String key = Stopwatches.PREF_LAP_TIME + Integer.toString(i);
204de5f183f54b5d10bc8cbebb977c7c8ab391ad3f5Isaac Katzenelson            editor.remove(key);
205de5f183f54b5d10bc8cbebb977c7c8ab391ad3f5Isaac Katzenelson        }
206de5f183f54b5d10bc8cbebb977c7c8ab391ad3f5Isaac Katzenelson        editor.remove(Stopwatches.PREF_LAP_NUM);
207de5f183f54b5d10bc8cbebb977c7c8ab391ad3f5Isaac Katzenelson        editor.apply();
208de5f183f54b5d10bc8cbebb977c7c8ab391ad3f5Isaac Katzenelson    }
20998b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas
21098b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas    /**
21198b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas     * Broadcast a message to show the in-use timers in the notifications
21298b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas     */
21398b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas    public static void showInUseNotifications(Context context) {
21498b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas        Intent timerIntent = new Intent();
21598b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas        timerIntent.setAction(Timers.NOTIF_IN_USE_SHOW);
21698b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas        context.sendBroadcast(timerIntent);
21798b2b05e7a0cebcd6e065bdd0b25cb6eb86b497cRobyn Coultas    }
21878b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein
219e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas    /**
220e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas     * Broadcast a message to show the in-use timers in the notifications
221e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas     */
222e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas    public static void showTimesUpNotifications(Context context) {
223e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        Intent timerIntent = new Intent();
224e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        timerIntent.setAction(Timers.NOTIF_TIMES_UP_SHOW);
225e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        context.sendBroadcast(timerIntent);
226e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas    }
227e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
228e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas    /**
229e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas     * Broadcast a message to cancel the in-use timers in the notifications
230e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas     */
231e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas    public static void cancelTimesUpNotifications(Context context) {
232e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        Intent timerIntent = new Intent();
233e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        timerIntent.setAction(Timers.NOTIF_TIMES_UP_CANCEL);
234e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas        context.sendBroadcast(timerIntent);
235e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas    }
236e332c5744e5741a6149afd226e1f193111dcb7deRobyn Coultas
23778b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein    /** Runnable for use with screensaver and dream, to move the clock every minute.
23878b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein     *  registerViews() must be called prior to posting.
23978b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein     */
24078b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein    public static class ScreensaverMoveSaverRunnable implements Runnable {
24178b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        static final long MOVE_DELAY = 60000; // DeskClock.SCREEN_SAVER_MOVE_DELAY;
24278b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        static final long SLIDE_TIME = 10000;
24378b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        static final long FADE_TIME = 3000;
24478b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein
24578b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        static final boolean SLIDE = false;
24678b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein
24778b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        private View mContentView, mSaverView;
24878b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        private final Handler mHandler;
24978b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein
25078b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        private static TimeInterpolator mSlowStartWithBrakes;
25178b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein
25278b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein
25378b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        public ScreensaverMoveSaverRunnable(Handler handler) {
25478b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein            mHandler = handler;
25578b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein            mSlowStartWithBrakes = new TimeInterpolator() {
25678b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                @Override
25778b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                public float getInterpolation(float x) {
25878b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                    return (float)(Math.cos((Math.pow(x,3) + 1) * Math.PI) / 2.0f) + 0.5f;
25978b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                }
26078b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein            };
26178b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        }
26278b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein
26378b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        public void registerViews(View contentView, View saverView) {
26478b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein            mContentView = contentView;
26578b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein            mSaverView = saverView;
26678b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        }
26778b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein
26878b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        @Override
26978b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        public void run() {
27078b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein            long delay = MOVE_DELAY;
27178b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein            if (mContentView == null || mSaverView == null) {
27278b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                mHandler.removeCallbacks(this);
27378b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                mHandler.postDelayed(this, delay);
27478b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                return;
27578b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein            }
27678b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein
27778b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein            final float xrange = mContentView.getWidth() - mSaverView.getWidth();
27878b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein            final float yrange = mContentView.getHeight() - mSaverView.getHeight();
27978b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein
28078b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein            if (xrange == 0 && yrange == 0) {
28178b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                delay = 500; // back in a split second
28278b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein            } else {
28378b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                final int nextx = (int) (Math.random() * xrange);
28478b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                final int nexty = (int) (Math.random() * yrange);
28578b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein
28678b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                if (mSaverView.getAlpha() == 0f) {
28778b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                    // jump right there
28878b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                    mSaverView.setX(nextx);
28978b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                    mSaverView.setY(nexty);
29078b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                    ObjectAnimator.ofFloat(mSaverView, "alpha", 0f, 1f)
29178b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                        .setDuration(FADE_TIME)
29278b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                        .start();
29378b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                } else {
29478b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                    AnimatorSet s = new AnimatorSet();
29578b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                    Animator xMove   = ObjectAnimator.ofFloat(mSaverView,
29678b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                                         "x", mSaverView.getX(), nextx);
29778b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                    Animator yMove   = ObjectAnimator.ofFloat(mSaverView,
29878b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                                         "y", mSaverView.getY(), nexty);
29978b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein
30078b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                    Animator xShrink = ObjectAnimator.ofFloat(mSaverView, "scaleX", 1f, 0.85f);
30178b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                    Animator xGrow   = ObjectAnimator.ofFloat(mSaverView, "scaleX", 0.85f, 1f);
30278b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein
30378b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                    Animator yShrink = ObjectAnimator.ofFloat(mSaverView, "scaleY", 1f, 0.85f);
30478b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                    Animator yGrow   = ObjectAnimator.ofFloat(mSaverView, "scaleY", 0.85f, 1f);
30578b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                    AnimatorSet shrink = new AnimatorSet(); shrink.play(xShrink).with(yShrink);
30678b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                    AnimatorSet grow = new AnimatorSet(); grow.play(xGrow).with(yGrow);
30778b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein
30878b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                    Animator fadeout = ObjectAnimator.ofFloat(mSaverView, "alpha", 1f, 0f);
30978b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                    Animator fadein = ObjectAnimator.ofFloat(mSaverView, "alpha", 0f, 1f);
31078b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein
31178b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein
31278b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                    if (SLIDE) {
31378b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                        s.play(xMove).with(yMove);
31478b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                        s.setDuration(SLIDE_TIME);
31578b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein
31678b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                        s.play(shrink.setDuration(SLIDE_TIME/2));
31778b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                        s.play(grow.setDuration(SLIDE_TIME/2)).after(shrink);
31878b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                        s.setInterpolator(mSlowStartWithBrakes);
31978b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                    } else {
32078b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                        AccelerateInterpolator accel = new AccelerateInterpolator();
32178b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                        DecelerateInterpolator decel = new DecelerateInterpolator();
32278b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein
32378b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                        shrink.setDuration(FADE_TIME).setInterpolator(accel);
32478b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                        fadeout.setDuration(FADE_TIME).setInterpolator(accel);
32578b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                        grow.setDuration(FADE_TIME).setInterpolator(decel);
32678b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                        fadein.setDuration(FADE_TIME).setInterpolator(decel);
32778b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                        s.play(shrink);
32878b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                        s.play(fadeout);
32978b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                        s.play(xMove.setDuration(0)).after(FADE_TIME);
33078b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                        s.play(yMove.setDuration(0)).after(FADE_TIME);
33178b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                        s.play(fadein).after(FADE_TIME);
33278b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                        s.play(grow).after(FADE_TIME);
33378b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                    }
33478b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                    s.start();
33578b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                }
33678b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein
33778b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                long now = System.currentTimeMillis();
33878b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                long adjust = (now % 60000);
33978b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                delay = delay
34078b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                        + (MOVE_DELAY - adjust) // minute aligned
34178b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                        - (SLIDE ? 0 : FADE_TIME) // start moving before the fade
34278b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                        ;
34378b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein            }
34478b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein
34578b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein            mHandler.removeCallbacks(this);
34678b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein            mHandler.postDelayed(this, delay);
34778b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        }
34878b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein    }
34978b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein
35078b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein    /** Setup to find out when the quarter-hour changes (e.g. Kathmandu is GMT+5:45) **/
3513bb9d29c12584480f3651ccbef86f2777c0b7818Robyn Coultas    public static long getAlarmOnQuarterHour() {
35278b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        Calendar nextQuarter = Calendar.getInstance();
35378b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        //  Set 1 second to ensure quarter-hour threshold passed.
35478b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        nextQuarter.set(Calendar.SECOND, 1);
35578b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        int minute = nextQuarter.get(Calendar.MINUTE);
35678b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        nextQuarter.add(Calendar.MINUTE, 15 - (minute % 15));
35778b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        long alarmOnQuarterHour = nextQuarter.getTimeInMillis();
35878b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        if (0 >= (alarmOnQuarterHour - System.currentTimeMillis())
35978b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                || (alarmOnQuarterHour - System.currentTimeMillis()) > 901000) {
36078b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein            Log.wtf("quarterly alarm calculation error");
36178b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        }
3623bb9d29c12584480f3651ccbef86f2777c0b7818Robyn Coultas        Log.v("getAlarmOnQuarterHour returns " // STOPSHIP Don't ship with this log
3637651c373a1301761bea4090738df9d03884684ddRobyn Coultas                + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(nextQuarter.getTime())
3647651c373a1301761bea4090738df9d03884684ddRobyn Coultas                + " to fire in " + (alarmOnQuarterHour - System.currentTimeMillis()));
36578b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        return alarmOnQuarterHour;
36678b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein    }
36778b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein
368eb3a1f0714e209a8335d84142778465aa6b44c5cRobyn Coultas    // Setup a thread that starts at midnight plus one second. The extra second is added to ensure
369eb3a1f0714e209a8335d84142778465aa6b44c5cRobyn Coultas    // the date has changed.
370eb3a1f0714e209a8335d84142778465aa6b44c5cRobyn Coultas    public static void setMidnightUpdater(Handler handler, Runnable runnable) {
371eb3a1f0714e209a8335d84142778465aa6b44c5cRobyn Coultas        String timezone = TimeZone.getDefault().getID();
372eb3a1f0714e209a8335d84142778465aa6b44c5cRobyn Coultas        if (handler == null || runnable == null || timezone == null) {
373eb3a1f0714e209a8335d84142778465aa6b44c5cRobyn Coultas            return;
374eb3a1f0714e209a8335d84142778465aa6b44c5cRobyn Coultas        }
375eb3a1f0714e209a8335d84142778465aa6b44c5cRobyn Coultas        long now = System.currentTimeMillis();
376eb3a1f0714e209a8335d84142778465aa6b44c5cRobyn Coultas        Time time = new Time(timezone);
377eb3a1f0714e209a8335d84142778465aa6b44c5cRobyn Coultas        time.set(now);
378eb3a1f0714e209a8335d84142778465aa6b44c5cRobyn Coultas        long runInMillis = ((24 - time.hour) * 3600 - time.minute * 60 - time.second + 1) * 1000;
379eb3a1f0714e209a8335d84142778465aa6b44c5cRobyn Coultas        handler.removeCallbacks(runnable);
380eb3a1f0714e209a8335d84142778465aa6b44c5cRobyn Coultas        handler.postDelayed(runnable, runInMillis);
381eb3a1f0714e209a8335d84142778465aa6b44c5cRobyn Coultas    }
382eb3a1f0714e209a8335d84142778465aa6b44c5cRobyn Coultas
383eb3a1f0714e209a8335d84142778465aa6b44c5cRobyn Coultas    // Stop the midnight update thread
3843bb9d29c12584480f3651ccbef86f2777c0b7818Robyn Coultas    public static void cancelMidnightUpdater(Handler handler, Runnable runnable) {
385eb3a1f0714e209a8335d84142778465aa6b44c5cRobyn Coultas        if (handler == null || runnable == null) {
386eb3a1f0714e209a8335d84142778465aa6b44c5cRobyn Coultas            return;
387eb3a1f0714e209a8335d84142778465aa6b44c5cRobyn Coultas        }
388eb3a1f0714e209a8335d84142778465aa6b44c5cRobyn Coultas        handler.removeCallbacks(runnable);
3899096dcec19f88883933e74b2d446eb94e6196089Robyn Coultas    }
3909096dcec19f88883933e74b2d446eb94e6196089Robyn Coultas
3919096dcec19f88883933e74b2d446eb94e6196089Robyn Coultas    // Setup a thread that starts at the quarter-hour plus one second. The extra second is added to
3929096dcec19f88883933e74b2d446eb94e6196089Robyn Coultas    // ensure dates have changed.
3939096dcec19f88883933e74b2d446eb94e6196089Robyn Coultas    public static void setQuarterHourUpdater(Handler handler, Runnable runnable) {
3949096dcec19f88883933e74b2d446eb94e6196089Robyn Coultas        String timezone = TimeZone.getDefault().getID();
3959096dcec19f88883933e74b2d446eb94e6196089Robyn Coultas        if (handler == null || runnable == null || timezone == null) {
3969096dcec19f88883933e74b2d446eb94e6196089Robyn Coultas            return;
3979096dcec19f88883933e74b2d446eb94e6196089Robyn Coultas        }
3989096dcec19f88883933e74b2d446eb94e6196089Robyn Coultas        long runInMillis = getAlarmOnQuarterHour() - System.currentTimeMillis();
3999096dcec19f88883933e74b2d446eb94e6196089Robyn Coultas        // Ensure the delay is at least one second.
4009096dcec19f88883933e74b2d446eb94e6196089Robyn Coultas        if (runInMillis < 1000) {
4019096dcec19f88883933e74b2d446eb94e6196089Robyn Coultas            runInMillis = 1000;
4029096dcec19f88883933e74b2d446eb94e6196089Robyn Coultas        }
4039096dcec19f88883933e74b2d446eb94e6196089Robyn Coultas        handler.removeCallbacks(runnable);
4049096dcec19f88883933e74b2d446eb94e6196089Robyn Coultas        handler.postDelayed(runnable, runInMillis);
4059096dcec19f88883933e74b2d446eb94e6196089Robyn Coultas    }
4069096dcec19f88883933e74b2d446eb94e6196089Robyn Coultas
4079096dcec19f88883933e74b2d446eb94e6196089Robyn Coultas    // Stop the quarter-hour update thread
4089096dcec19f88883933e74b2d446eb94e6196089Robyn Coultas    public static void cancelQuarterHourUpdater(Handler handler, Runnable runnable) {
4099096dcec19f88883933e74b2d446eb94e6196089Robyn Coultas        if (handler == null || runnable == null) {
4109096dcec19f88883933e74b2d446eb94e6196089Robyn Coultas            return;
4119096dcec19f88883933e74b2d446eb94e6196089Robyn Coultas        }
4129096dcec19f88883933e74b2d446eb94e6196089Robyn Coultas        handler.removeCallbacks(runnable);
413eb3a1f0714e209a8335d84142778465aa6b44c5cRobyn Coultas    }
414eb3a1f0714e209a8335d84142778465aa6b44c5cRobyn Coultas
41578b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein    /**
41678b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein     * For screensavers to set whether the digital or analog clock should be displayed.
41778b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein     * Returns the view to be displayed.
41878b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein     */
41978b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein    public static View setClockStyle(Context context, View digitalClock, View analogClock,
42078b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein            String clockStyleKey) {
42178b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context);
42278b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        String defaultClockStyle = context.getResources().getString(R.string.default_clock_style);
42378b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        String style = sharedPref.getString(clockStyleKey, defaultClockStyle);
42478b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        View returnView;
42578b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        if (style.equals(CLOCK_TYPE_ANALOG)) {
42678b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein            digitalClock.setVisibility(View.GONE);
42778b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein            analogClock.setVisibility(View.VISIBLE);
42878b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein            returnView = analogClock;
42978b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        } else {
43078b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein            digitalClock.setVisibility(View.VISIBLE);
43178b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein            analogClock.setVisibility(View.GONE);
43278b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein            returnView = digitalClock;
43378b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        }
43478b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein
43578b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        return returnView;
43678b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein    }
43778b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein
43878b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein    /**
43978b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein     * For screensavers to dim the lights if necessary.
44078b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein     */
44178b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein    public static void dimClockView(boolean dim, View clockView) {
44278b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        Paint paint = new Paint();
44378b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        paint.setColor(Color.WHITE);
44478b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        paint.setColorFilter(new PorterDuffColorFilter(
445e8709b46a14defe610515a1a4c4fefa32ec9b044Isaac Katzenelson                        (dim ? 0x40FFFFFF : 0xC0FFFFFF),
44678b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                PorterDuff.Mode.MULTIPLY));
44778b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        clockView.setLayerType(View.LAYER_TYPE_HARDWARE, paint);
44878b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein    }
44978b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein
45078b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein    /** Clock views can call this to refresh their alarm to the next upcoming value. **/
451b41f177aef12a5aa5ec43057535f5deacf98a881Isaac Katzenelson    public static void refreshAlarm(Context context, View clock) {
45278b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        String nextAlarm = Settings.System.getString(context.getContentResolver(),
45378b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                Settings.System.NEXT_ALARM_FORMATTED);
45478b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        TextView nextAlarmView;
455b41f177aef12a5aa5ec43057535f5deacf98a881Isaac Katzenelson        nextAlarmView = (TextView) clock.findViewById(R.id.nextAlarm);
45678b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        if (!TextUtils.isEmpty(nextAlarm) && nextAlarmView != null) {
45778b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein            nextAlarmView.setText(
45878b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                    context.getString(R.string.control_set_alarm_with_existing, nextAlarm));
45978b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein            nextAlarmView.setContentDescription(context.getResources().getString(
46078b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein                    R.string.next_alarm_description, nextAlarm));
46178b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein            nextAlarmView.setVisibility(View.VISIBLE);
46278b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        } else  {
46378b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein            nextAlarmView.setVisibility(View.GONE);
46478b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        }
46578b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein    }
46678b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein
46778b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein    /** Clock views can call this to refresh their date. **/
468b41f177aef12a5aa5ec43057535f5deacf98a881Isaac Katzenelson    public static void updateDate(
469b41f177aef12a5aa5ec43057535f5deacf98a881Isaac Katzenelson            String dateFormat, String dateFormatForAccessibility, View clock) {
47078b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein
47135300f84cef68e7f5666ec88591496aaaad242d1Isaac Katzenelson        Date now = new Date();
47278b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        TextView dateDisplay;
473b41f177aef12a5aa5ec43057535f5deacf98a881Isaac Katzenelson        dateDisplay = (TextView) clock.findViewById(R.id.date);
47478b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        if (dateDisplay != null) {
47535300f84cef68e7f5666ec88591496aaaad242d1Isaac Katzenelson            final Locale l = Locale.getDefault();
47635300f84cef68e7f5666ec88591496aaaad242d1Isaac Katzenelson            String fmt = DateFormat.getBestDateTimePattern(l, dateFormat);
47735300f84cef68e7f5666ec88591496aaaad242d1Isaac Katzenelson            SimpleDateFormat sdf = new SimpleDateFormat(fmt, l);
47835300f84cef68e7f5666ec88591496aaaad242d1Isaac Katzenelson            dateDisplay.setText(sdf.format(now));
47978b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein            dateDisplay.setVisibility(View.VISIBLE);
48035300f84cef68e7f5666ec88591496aaaad242d1Isaac Katzenelson            fmt = DateFormat.getBestDateTimePattern(l, dateFormatForAccessibility);
48135300f84cef68e7f5666ec88591496aaaad242d1Isaac Katzenelson            sdf = new SimpleDateFormat(fmt, l);
48235300f84cef68e7f5666ec88591496aaaad242d1Isaac Katzenelson            dateDisplay.setContentDescription(sdf.format(now));
48378b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein        }
48478b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein    }
48578b8e1513e24c58ffea6ee4edbebdce85c248f6fSam Blitzstein
486690f451a6aea35d944cb2eaddf488d3acf377184Isaac Katzenelson    /***
4873a4ba0db218b830af3dd17fde2952125a2e50fdcIsaac Katzenelson     * Formats the time in the TextClock according to the Locale with a special
488690f451a6aea35d944cb2eaddf488d3acf377184Isaac Katzenelson     * formatting treatment for the am/pm label.
489690f451a6aea35d944cb2eaddf488d3acf377184Isaac Katzenelson     * @param clock - TextClock to format
4903a4ba0db218b830af3dd17fde2952125a2e50fdcIsaac Katzenelson     * @param amPmFontSize - size of the am/pm label since it is usually smaller
4913a4ba0db218b830af3dd17fde2952125a2e50fdcIsaac Katzenelson     *        than the clock time size.
492690f451a6aea35d944cb2eaddf488d3acf377184Isaac Katzenelson     */
493690f451a6aea35d944cb2eaddf488d3acf377184Isaac Katzenelson    public static void setTimeFormat(TextClock clock, int amPmFontSize) {
494690f451a6aea35d944cb2eaddf488d3acf377184Isaac Katzenelson        if (clock != null) {
4953a4ba0db218b830af3dd17fde2952125a2e50fdcIsaac Katzenelson            // Get the best format for 12 hours mode according to the locale
4963a4ba0db218b830af3dd17fde2952125a2e50fdcIsaac Katzenelson            clock.setFormat12Hour(get12ModeFormet(amPmFontSize));
4973a4ba0db218b830af3dd17fde2952125a2e50fdcIsaac Katzenelson            // Get the best format for 24 hours mode according to the locale
4983a4ba0db218b830af3dd17fde2952125a2e50fdcIsaac Katzenelson            clock.setFormat24Hour(get24ModeFormet());
499690f451a6aea35d944cb2eaddf488d3acf377184Isaac Katzenelson        }
500690f451a6aea35d944cb2eaddf488d3acf377184Isaac Katzenelson    }
501690f451a6aea35d944cb2eaddf488d3acf377184Isaac Katzenelson
5023a4ba0db218b830af3dd17fde2952125a2e50fdcIsaac Katzenelson    public static CharSequence get12ModeFormet(int amPmFontSize) {
5033a4ba0db218b830af3dd17fde2952125a2e50fdcIsaac Katzenelson        String skeleton = "hma";
5043a4ba0db218b830af3dd17fde2952125a2e50fdcIsaac Katzenelson        String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
5053a4ba0db218b830af3dd17fde2952125a2e50fdcIsaac Katzenelson        // Replace spaces with "Hair Space"
5063a4ba0db218b830af3dd17fde2952125a2e50fdcIsaac Katzenelson        pattern = pattern.replaceAll(" ", "\u200A");
5073a4ba0db218b830af3dd17fde2952125a2e50fdcIsaac Katzenelson        // Build a spannable so that the am/pm will be formatted
5083a4ba0db218b830af3dd17fde2952125a2e50fdcIsaac Katzenelson        int amPmPos = pattern.indexOf('a');
5093a4ba0db218b830af3dd17fde2952125a2e50fdcIsaac Katzenelson        if (amPmPos == -1) {
5103a4ba0db218b830af3dd17fde2952125a2e50fdcIsaac Katzenelson            return pattern;
5113a4ba0db218b830af3dd17fde2952125a2e50fdcIsaac Katzenelson        }
5123a4ba0db218b830af3dd17fde2952125a2e50fdcIsaac Katzenelson        Spannable sp = new SpannableString(pattern);
5133a4ba0db218b830af3dd17fde2952125a2e50fdcIsaac Katzenelson        sp.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), amPmPos, amPmPos + 1,
5143a4ba0db218b830af3dd17fde2952125a2e50fdcIsaac Katzenelson                Spannable.SPAN_POINT_MARK);
5153a4ba0db218b830af3dd17fde2952125a2e50fdcIsaac Katzenelson        sp.setSpan(new AbsoluteSizeSpan(amPmFontSize), amPmPos, amPmPos + 1,
5163a4ba0db218b830af3dd17fde2952125a2e50fdcIsaac Katzenelson                Spannable.SPAN_POINT_MARK);
5173a4ba0db218b830af3dd17fde2952125a2e50fdcIsaac Katzenelson        sp.setSpan(new TypefaceSpan("sans-serif-condensed"), amPmPos, amPmPos + 1,
5183a4ba0db218b830af3dd17fde2952125a2e50fdcIsaac Katzenelson                Spannable.SPAN_POINT_MARK);
5193a4ba0db218b830af3dd17fde2952125a2e50fdcIsaac Katzenelson        return sp;
5203a4ba0db218b830af3dd17fde2952125a2e50fdcIsaac Katzenelson    }
5213a4ba0db218b830af3dd17fde2952125a2e50fdcIsaac Katzenelson
5223a4ba0db218b830af3dd17fde2952125a2e50fdcIsaac Katzenelson    public static CharSequence get24ModeFormet() {
5233a4ba0db218b830af3dd17fde2952125a2e50fdcIsaac Katzenelson        String skeleton = "Hm";
5243a4ba0db218b830af3dd17fde2952125a2e50fdcIsaac Katzenelson        return DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
5253a4ba0db218b830af3dd17fde2952125a2e50fdcIsaac Katzenelson    }
5263a4ba0db218b830af3dd17fde2952125a2e50fdcIsaac Katzenelson
52762b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung    public static CityObj[] loadCitiesFromXml(Context c) {
528d4bbc122b90c63c5906ad32c4652b72c9409fd44Isaac Katzenelson        Resources r = c.getResources();
529d4bbc122b90c63c5906ad32c4652b72c9409fd44Isaac Katzenelson        // Read strings array of name,timezone, id
530d4bbc122b90c63c5906ad32c4652b72c9409fd44Isaac Katzenelson        // make sure the list are the same length
531d851e8a4b134028bd12f509312d31664e4c826ddIsaac Katzenelson        String[] cities = r.getStringArray(R.array.cities_names);
532d851e8a4b134028bd12f509312d31664e4c826ddIsaac Katzenelson        String[] timezones = r.getStringArray(R.array.cities_tz);
533d851e8a4b134028bd12f509312d31664e4c826ddIsaac Katzenelson        String[] ids = r.getStringArray(R.array.cities_id);
53425ca0e1b5e70386b30171d8aba8810593baffecbIsaac Katzenelson        int minLength = cities.length;
535d4bbc122b90c63c5906ad32c4652b72c9409fd44Isaac Katzenelson        if (cities.length != timezones.length || ids.length != cities.length) {
53625ca0e1b5e70386b30171d8aba8810593baffecbIsaac Katzenelson            // StopShip: Make sure to remove this after we get transations for K
53725ca0e1b5e70386b30171d8aba8810593baffecbIsaac Katzenelson            minLength = Math.min(cities.length, Math.min(timezones.length, ids.length));
53825ca0e1b5e70386b30171d8aba8810593baffecbIsaac Katzenelson            Log.e("City lists sizes are not the same, trancating");
53925ca0e1b5e70386b30171d8aba8810593baffecbIsaac Katzenelson            // return null;
540d851e8a4b134028bd12f509312d31664e4c826ddIsaac Katzenelson        }
54125ca0e1b5e70386b30171d8aba8810593baffecbIsaac Katzenelson        CityObj[] tempList = new CityObj[minLength];
542d851e8a4b134028bd12f509312d31664e4c826ddIsaac Katzenelson        for (int i = 0; i < cities.length; i++) {
543d4bbc122b90c63c5906ad32c4652b72c9409fd44Isaac Katzenelson            tempList[i] = new CityObj(cities[i], timezones[i], ids[i]);
544d851e8a4b134028bd12f509312d31664e4c826ddIsaac Katzenelson        }
545d4bbc122b90c63c5906ad32c4652b72c9409fd44Isaac Katzenelson        return tempList;
546d4bbc122b90c63c5906ad32c4652b72c9409fd44Isaac Katzenelson    }
547d851e8a4b134028bd12f509312d31664e4c826ddIsaac Katzenelson
54862b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung    /**
54962b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung     * Returns string denoting the timezone hour offset (e.g. GMT-8:00)
55062b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung     */
55162b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung    public static String getGMTHourOffset(TimeZone timezone, boolean showMinutes) {
55262b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung        StringBuilder sb = new StringBuilder();
55362b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung        sb.append("GMT");
55462b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung        int gmtOffset = timezone.getRawOffset();
55562b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung        if (gmtOffset < 0) {
55662b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung            sb.append('-');
55762b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung        } else {
55862b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung            sb.append('+');
55962b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung        }
56062b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung        sb.append(Math.abs(gmtOffset) / DateUtils.HOUR_IN_MILLIS); // Hour
56162b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung
56262b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung        if (showMinutes) {
56362b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung            final int min = (Math.abs(gmtOffset) / (int) DateUtils.MINUTE_IN_MILLIS) % 60;
56462b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung            sb.append(':');
56562b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung            if (min < 10) {
56662b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung                sb.append('0');
56762b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung            }
56862b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung            sb.append(min);
56962b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung        }
57062b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung
57162b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung        return sb.toString();
57262b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung    }
57362b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung
574d851e8a4b134028bd12f509312d31664e4c826ddIsaac Katzenelson    public static String getCityName(CityObj city, CityObj dbCity) {
575d851e8a4b134028bd12f509312d31664e4c826ddIsaac Katzenelson        return (city.mCityId == null || dbCity == null) ? city.mCityName : dbCity.mCityName;
576d851e8a4b134028bd12f509312d31664e4c826ddIsaac Katzenelson    }
577d2f8c51aba02f5e8cbbf82234ff453f305fba759Isaac Katzenelson}
578