• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 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 
17 package com.android.deskclock;
18 
19 import android.Manifest;
20 import android.annotation.TargetApi;
21 import android.app.Fragment;
22 import android.app.FragmentManager;
23 import android.app.FragmentTransaction;
24 import android.app.TimePickerDialog;
25 import android.content.Context;
26 import android.content.pm.PackageManager;
27 import android.media.RingtoneManager;
28 import android.net.Uri;
29 import android.os.Build;
30 import android.provider.Settings;
31 import android.support.annotation.VisibleForTesting;
32 import android.text.format.DateFormat;
33 import android.text.format.DateUtils;
34 import android.widget.Toast;
35 
36 import com.android.deskclock.provider.Alarm;
37 import com.android.deskclock.provider.AlarmInstance;
38 
39 import java.util.Calendar;
40 import java.util.Locale;
41 
42 /**
43  * Static utility methods for Alarms.
44  */
45 public class AlarmUtils {
46     public static final String FRAG_TAG_TIME_PICKER = "time_dialog";
47 
getFormattedTime(Context context, Calendar time)48     public static String getFormattedTime(Context context, Calendar time) {
49         String pattern;
50         if (Utils.isJBMR2OrLater()) {
51             final String skeleton = DateFormat.is24HourFormat(context) ? "EHm" : "Ehma";
52             pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
53             return (String) DateFormat.format(pattern, time);
54         } else {
55             pattern = DateFormat.is24HourFormat(context)
56                     ? context.getString(R.string.weekday_time_format_24_mode)
57                     : context.getString(R.string.weekday_time_format_12_mode);
58         }
59         return (String) DateFormat.format(pattern, time);
60     }
61 
getAlarmText(Context context, AlarmInstance instance)62     public static String getAlarmText(Context context, AlarmInstance instance) {
63         String alarmTimeStr = getFormattedTime(context, instance.getAlarmTime());
64         return !instance.mLabel.isEmpty() ? alarmTimeStr + " - " + instance.mLabel
65                 : alarmTimeStr;
66     }
67 
68     // show time picker dialog for pre-L devices
showTimeEditDialog(FragmentManager manager, final Alarm alarm, com.android.datetimepicker.time.TimePickerDialog.OnTimeSetListener listener, boolean is24HourMode)69     public static void showTimeEditDialog(FragmentManager manager, final Alarm alarm,
70           com.android.datetimepicker.time.TimePickerDialog.OnTimeSetListener listener,
71           boolean is24HourMode) {
72 
73         final int hour, minutes;
74         if (alarm == null) {
75             hour = 0;
76             minutes = 0;
77         } else {
78             hour = alarm.hour;
79             minutes = alarm.minutes;
80         }
81         com.android.datetimepicker.time.TimePickerDialog dialog =
82                 com.android.datetimepicker.time.TimePickerDialog.newInstance(listener,
83                     hour, minutes, is24HourMode);
84         dialog.setThemeDark(true);
85 
86         // Make sure the dialog isn't already added.
87         manager.executePendingTransactions();
88         final FragmentTransaction ft = manager.beginTransaction();
89         final Fragment prev = manager.findFragmentByTag(FRAG_TAG_TIME_PICKER);
90         if (prev != null) {
91             ft.remove(prev);
92         }
93         ft.commit();
94 
95         if (!dialog.isAdded()) {
96             dialog.show(manager, FRAG_TAG_TIME_PICKER);
97         }
98     }
99 
100     /**
101      * Show the time picker dialog for post-L devices.
102      * This is called from AlarmClockFragment to set alarm.
103      * @param fragment The calling fragment (which is also a onTimeSetListener),
104      *                 we use it as the target fragment of the TimePickerFragment, so later the
105      *                 latter can retrieve it and set it as its onTimeSetListener when the fragment
106      *                 is recreated.
107      * @param alarm The clicked alarm, it can be null if user was clicking the fab instead.
108      */
109     @TargetApi(Build.VERSION_CODES.LOLLIPOP)
showTimeEditDialog(Fragment fragment, final Alarm alarm)110     public static void showTimeEditDialog(Fragment fragment, final Alarm alarm) {
111         final FragmentManager manager = fragment.getFragmentManager();
112         final FragmentTransaction ft = manager.beginTransaction();
113         final Fragment prev = manager.findFragmentByTag(FRAG_TAG_TIME_PICKER);
114         if (prev != null) {
115             ft.remove(prev);
116         }
117         ft.commit();
118         final TimePickerFragment timePickerFragment = new TimePickerFragment();
119         timePickerFragment.setTargetFragment(fragment, 0);
120         timePickerFragment.setOnTimeSetListener((TimePickerDialog.OnTimeSetListener) fragment);
121         timePickerFragment.setAlarm(alarm);
122         timePickerFragment.show(manager, FRAG_TAG_TIME_PICKER);
123     }
124 
125     /**
126      * @return {@code true} iff the user has granted permission to read the ringtone at the given
127      *      uri or no permission is required to read the ringtone
128      */
hasPermissionToDisplayRingtoneTitle(Context context, Uri ringtoneUri)129     public static boolean hasPermissionToDisplayRingtoneTitle(Context context, Uri ringtoneUri) {
130         final PackageManager pm = context.getPackageManager();
131         final String packageName = context.getPackageName();
132 
133         // If the default alarm alert ringtone URI is given, resolve it to the actual URI.
134         if (Settings.System.DEFAULT_ALARM_ALERT_URI.equals(ringtoneUri)) {
135             ringtoneUri = RingtoneManager.getActualDefaultRingtoneUri(context,
136                     RingtoneManager.TYPE_ALARM);
137         }
138 
139         // If no ringtone is specified, return true.
140         if (ringtoneUri == null || ringtoneUri == Alarm.NO_RINGTONE_URI) {
141             return true;
142         }
143 
144         // If the permission is already granted, return true.
145         if (pm.checkPermission(Manifest.permission.READ_EXTERNAL_STORAGE, packageName)
146                 == PackageManager.PERMISSION_GRANTED) {
147             return true;
148         }
149 
150         // If the ringtone is internal, return true;
151         // external ringtones require the permission to see their title
152         return ringtoneUri.toString().startsWith("content://media/internal/");
153     }
154 
155     /**
156      * format "Alarm set for 2 days, 7 hours, and 53 minutes from now."
157      */
158     @VisibleForTesting
formatElapsedTimeUntilAlarm(Context context, long delta)159     static String formatElapsedTimeUntilAlarm(Context context, long delta) {
160         // If the alarm will ring within 60 seconds, just report "less than a minute."
161         final String[] formats = context.getResources().getStringArray(R.array.alarm_set);
162         if (delta < DateUtils.MINUTE_IN_MILLIS) {
163             return formats[0];
164         }
165 
166         // Otherwise, format the remaining time until the alarm rings.
167 
168         // Round delta upwards to the nearest whole minute. (e.g. 7m 58s -> 8m)
169         final long remainder = delta % DateUtils.MINUTE_IN_MILLIS;
170         delta += remainder == 0 ? 0 : (DateUtils.MINUTE_IN_MILLIS - remainder);
171 
172         int hours = (int) delta / (1000 * 60 * 60);
173         final int minutes = (int) delta / (1000 * 60) % 60;
174         final int days = hours / 24;
175         hours = hours % 24;
176 
177         String daySeq = Utils.getNumberFormattedQuantityString(context, R.plurals.days, days);
178         String minSeq = Utils.getNumberFormattedQuantityString(context, R.plurals.minutes, minutes);
179         String hourSeq = Utils.getNumberFormattedQuantityString(context, R.plurals.hours, hours);
180 
181         final boolean showDays = days > 0;
182         final boolean showHours = hours > 0;
183         final boolean showMinutes = minutes > 0;
184 
185         // Compute the index of the most appropriate time format based on the time delta.
186         final int index = (showDays ? 1 : 0) | (showHours ? 2 : 0) | (showMinutes ? 4 : 0);
187 
188         return String.format(formats[index], daySeq, hourSeq, minSeq);
189     }
190 
popAlarmSetToast(Context context, long alarmTime)191     public static void popAlarmSetToast(Context context, long alarmTime) {
192         final long alarmTimeDelta = alarmTime - System.currentTimeMillis();
193         final String text = formatElapsedTimeUntilAlarm(context, alarmTimeDelta);
194         Toast toast = Toast.makeText(context, text, Toast.LENGTH_LONG);
195         ToastMaster.setToast(toast);
196         toast.show();
197     }
198 }
199