1/*
2 * Copyright (C) 2010 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.deskclock;
18
19import android.app.Activity;
20import android.content.ContentResolver;
21import android.content.Intent;
22import android.content.SharedPreferences;
23import android.media.RingtoneManager;
24import android.net.Uri;
25import android.os.Bundle;
26import android.preference.PreferenceManager;
27import android.text.TextUtils;
28
29import com.android.deskclock.alarms.AlarmStateManager;
30import com.android.deskclock.provider.Alarm;
31import com.android.deskclock.provider.AlarmInstance;
32import com.android.deskclock.provider.DaysOfWeek;
33import com.android.deskclock.timer.TimerFragment;
34import com.android.deskclock.timer.TimerObj;
35import com.android.deskclock.timer.Timers;
36
37import java.util.ArrayList;
38import java.util.Calendar;
39import java.util.List;
40
41import static android.provider.AlarmClock.ACTION_SET_ALARM;
42import static android.provider.AlarmClock.ACTION_SET_TIMER;
43import static android.provider.AlarmClock.ACTION_SHOW_ALARMS;
44import static android.provider.AlarmClock.EXTRA_DAYS;
45import static android.provider.AlarmClock.EXTRA_HOUR;
46import static android.provider.AlarmClock.EXTRA_LENGTH;
47import static android.provider.AlarmClock.EXTRA_MESSAGE;
48import static android.provider.AlarmClock.EXTRA_MINUTES;
49import static android.provider.AlarmClock.EXTRA_RINGTONE;
50import static android.provider.AlarmClock.EXTRA_SKIP_UI;
51import static android.provider.AlarmClock.EXTRA_VIBRATE;
52import static android.provider.AlarmClock.VALUE_RINGTONE_SILENT;
53
54public class HandleApiCalls extends Activity {
55
56    public static final long TIMER_MIN_LENGTH = 1000;
57    public static final long TIMER_MAX_LENGTH = 24 * 60 * 60 * 1000;
58
59    @Override
60    protected void onCreate(Bundle icicle) {
61        try {
62            super.onCreate(icicle);
63            Intent intent = getIntent();
64            if (intent != null) {
65                if (ACTION_SET_ALARM.equals(intent.getAction())) {
66                    handleSetAlarm(intent);
67                } else if (ACTION_SHOW_ALARMS.equals(intent.getAction())) {
68                    handleShowAlarms();
69                } else if (ACTION_SET_TIMER.equals(intent.getAction())) {
70                    handleSetTimer(intent);
71                }
72            }
73        } finally {
74            finish();
75        }
76    }
77
78    /***
79     * Processes the SET_ALARM intent
80     * @param intent
81     */
82    private void handleSetAlarm(Intent intent) {
83        // If not provided or invalid, show UI
84        final int hour = intent.getIntExtra(EXTRA_HOUR, -1);
85
86        // If not provided, use zero. If it is provided, make sure it's valid, otherwise, show UI
87        final int minutes;
88        if (intent.hasExtra(EXTRA_MINUTES)) {
89            minutes = intent.getIntExtra(EXTRA_MINUTES, -1);
90        } else {
91            minutes = 0;
92        }
93        if (hour < 0 || hour > 23 || minutes < 0 || minutes > 59) {
94            // Intent has no time or an invalid time, open the alarm creation UI
95            Intent createAlarm = Alarm.createIntent(this, DeskClock.class, Alarm.INVALID_ID);
96            createAlarm.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
97            createAlarm.putExtra(AlarmClockFragment.ALARM_CREATE_NEW_INTENT_EXTRA, true);
98            createAlarm.putExtra(DeskClock.SELECT_TAB_INTENT_EXTRA, DeskClock.ALARM_TAB_INDEX);
99            startActivity(createAlarm);
100            finish();
101            return;
102        }
103
104        final boolean skipUi = intent.getBooleanExtra(EXTRA_SKIP_UI, false);
105
106        final StringBuilder selection = new StringBuilder();
107        final List<String> args = new ArrayList<String>();
108        setSelectionFromIntent(intent, hour, minutes, selection, args);
109
110        // Check if the alarm already exists and handle it
111        final ContentResolver cr = getContentResolver();
112        final List<Alarm> alarms = Alarm.getAlarms(cr,
113                selection.toString(),
114                args.toArray(new String[args.size()]));
115        if (!alarms.isEmpty()) {
116            Alarm alarm = alarms.get(0);
117            alarm.enabled = true;
118            Alarm.updateAlarm(cr, alarm);
119
120            // Delete all old instances and create a new one with updated values
121            AlarmStateManager.deleteAllInstances(this, alarm.id);
122            setupInstance(alarm.createInstanceAfter(Calendar.getInstance()), skipUi);
123            finish();
124            return;
125        }
126
127        // Otherwise insert it and handle it
128        final String message = getMessageFromIntent(intent);
129        final DaysOfWeek daysOfWeek = getDaysFromIntent(intent);
130        final boolean vibrate = intent.getBooleanExtra(EXTRA_VIBRATE, false);
131        final String alert = intent.getStringExtra(EXTRA_RINGTONE);
132
133        Alarm alarm = new Alarm(hour, minutes);
134        alarm.enabled = true;
135        alarm.label = message;
136        alarm.daysOfWeek = daysOfWeek;
137        alarm.vibrate = vibrate;
138
139        if (alert == null) {
140            alarm.alert = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
141        } else if (VALUE_RINGTONE_SILENT.equals(alert) || alert.isEmpty()) {
142            alarm.alert = Alarm.NO_RINGTONE_URI;
143        } else {
144            alarm.alert = Uri.parse(alert);
145        }
146        alarm.deleteAfterUse = !daysOfWeek.isRepeating() && skipUi;
147
148        alarm = Alarm.addAlarm(cr, alarm);
149        setupInstance(alarm.createInstanceAfter(Calendar.getInstance()), skipUi);
150        finish();
151    }
152
153    private void handleShowAlarms() {
154        startActivity(new Intent(this, DeskClock.class)
155                .putExtra(DeskClock.SELECT_TAB_INTENT_EXTRA, DeskClock.ALARM_TAB_INDEX));
156    }
157
158    private void handleSetTimer(Intent intent) {
159        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
160        // If no length is supplied , show the timer setup view
161        if (!intent.hasExtra(EXTRA_LENGTH)) {
162            startActivity(new Intent(this, DeskClock.class)
163                  .putExtra(DeskClock.SELECT_TAB_INTENT_EXTRA, DeskClock.TIMER_TAB_INDEX)
164                  .putExtra(TimerFragment.GOTO_SETUP_VIEW, true));
165            return;
166        }
167
168        final long length = 1000l * intent.getIntExtra(EXTRA_LENGTH, 0);
169        if (length < TIMER_MIN_LENGTH || length > TIMER_MAX_LENGTH) {
170            Log.i("Invalid timer length requested: " + length);
171            return;
172        }
173        String label = getMessageFromIntent(intent);
174
175        TimerObj timer = null;
176        // Find an existing matching time
177        final ArrayList<TimerObj> timers = new ArrayList<TimerObj>();
178        TimerObj.getTimersFromSharedPrefs(prefs, timers);
179        for (TimerObj t : timers) {
180            if (t.mSetupLength == length && (TextUtils.equals(label, t.mLabel))
181                    && t.mState == TimerObj.STATE_RESTART) {
182                timer = t;
183                break;
184            }
185        }
186
187        boolean skipUi = intent.getBooleanExtra(EXTRA_SKIP_UI, false);
188        if (timer == null) {
189            // Use a new timer
190            timer = new TimerObj(length, label);
191            // Timers set without presenting UI to the user will be deleted after use
192            timer.mDeleteAfterUse = skipUi;
193        }
194
195        timer.mState = TimerObj.STATE_RUNNING;
196        timer.mStartTime = Utils.getTimeNow();
197        timer.writeToSharedPref(prefs);
198
199        // Tell TimerReceiver that the timer was started
200        sendBroadcast(new Intent().setAction(Timers.START_TIMER)
201                .putExtra(Timers.TIMER_INTENT_EXTRA, timer.mTimerId));
202
203        if (skipUi) {
204            Utils.showInUseNotifications(this);
205        } else {
206            startActivity(new Intent(this, DeskClock.class)
207                    .putExtra(DeskClock.SELECT_TAB_INTENT_EXTRA, DeskClock.TIMER_TAB_INDEX));
208        }
209    }
210
211    private void setupInstance(AlarmInstance instance, boolean skipUi) {
212        instance = AlarmInstance.addInstance(this.getContentResolver(), instance);
213        AlarmStateManager.registerInstance(this, instance, true);
214        AlarmUtils.popAlarmSetToast(this, instance.getAlarmTime().getTimeInMillis());
215        if (!skipUi) {
216            Intent showAlarm = Alarm.createIntent(this, DeskClock.class, instance.mAlarmId);
217            showAlarm.putExtra(DeskClock.SELECT_TAB_INTENT_EXTRA, DeskClock.ALARM_TAB_INDEX);
218            showAlarm.putExtra(AlarmClockFragment.SCROLL_TO_ALARM_INTENT_EXTRA, instance.mAlarmId);
219            showAlarm.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
220            startActivity(showAlarm);
221        }
222    }
223
224    private String getMessageFromIntent(Intent intent) {
225        final String message = intent.getStringExtra(EXTRA_MESSAGE);
226        return message == null ? "" : message;
227    }
228
229    private DaysOfWeek getDaysFromIntent(Intent intent) {
230        final DaysOfWeek daysOfWeek = new DaysOfWeek(0);
231        final ArrayList<Integer> days = intent.getIntegerArrayListExtra(EXTRA_DAYS);
232        if (days != null) {
233            final int[] daysArray = new int[days.size()];
234            for (int i = 0; i < days.size(); i++) {
235                daysArray[i] = days.get(i);
236            }
237            daysOfWeek.setDaysOfWeek(true, daysArray);
238        } else {
239            // API says to use an ArrayList<Integer> but we allow the user to use a int[] too.
240            final int[] daysArray = intent.getIntArrayExtra(EXTRA_DAYS);
241            if (daysArray != null) {
242                daysOfWeek.setDaysOfWeek(true, daysArray);
243            }
244        }
245        return daysOfWeek;
246    }
247
248    private void setSelectionFromIntent(
249            Intent intent,
250            int hour,
251            int minutes,
252            StringBuilder selection,
253            List<String> args) {
254        selection.append(Alarm.HOUR).append("=?");
255        args.add(String.valueOf(hour));
256        selection.append(" AND ").append(Alarm.MINUTES).append("=?");
257        args.add(String.valueOf(minutes));
258
259        if (intent.hasExtra(EXTRA_MESSAGE)) {
260            selection.append(" AND ").append(Alarm.LABEL).append("=?");
261            args.add(getMessageFromIntent(intent));
262        }
263
264        // Days is treated differently that other fields because if days is not specified, it
265        // explicitly means "not recurring".
266        selection.append(" AND ").append(Alarm.DAYS_OF_WEEK).append("=?");
267        args.add(String.valueOf(intent.hasExtra(EXTRA_DAYS)
268                ? getDaysFromIntent(intent).getBitSet() : DaysOfWeek.NO_DAYS_SET));
269
270        if (intent.hasExtra(EXTRA_VIBRATE)) {
271            selection.append(" AND ").append(Alarm.VIBRATE).append("=?");
272            args.add(intent.getBooleanExtra(EXTRA_VIBRATE, false) ? "1" : "0");
273        }
274
275        if (intent.hasExtra(EXTRA_RINGTONE)) {
276            selection.append(" AND ").append(Alarm.RINGTONE).append("=?");
277
278            String ringTone = intent.getStringExtra(EXTRA_RINGTONE);
279            if (ringTone == null) {
280                // If the intent explicitly specified a NULL ringtone, treat it as the default
281                // ringtone.
282                ringTone = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM).toString();
283            } else if (VALUE_RINGTONE_SILENT.equals(ringTone) || ringTone.isEmpty()) {
284                    ringTone = Alarm.NO_RINGTONE;
285            }
286            args.add(ringTone);
287        }
288    }
289}
290