1 /*
2  * Copyright (C) 2015 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.app.Activity;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.os.Bundle;
23 
24 import com.android.deskclock.data.City;
25 import com.android.deskclock.data.DataModel;
26 import com.android.deskclock.data.Timer;
27 import com.android.deskclock.events.Events;
28 import com.android.deskclock.worldclock.CitySelectionActivity;
29 
30 import java.util.List;
31 import java.util.Set;
32 
33 public class HandleDeskClockApiCalls extends Activity {
34     private Context mAppContext;
35 
36     private static final String ACTION_PREFIX = "com.android.deskclock.action.";
37 
38     // shows the tab with world clocks
39     public static final String ACTION_SHOW_CLOCK = ACTION_PREFIX + "SHOW_CLOCK";
40     // extra for ACTION_SHOW_CLOCK indicating the clock is being displayed from tapping the widget
41     public static final String EXTRA_FROM_WIDGET = "com.android.deskclock.extra.clock.FROM_WIDGET";
42     // add a clock of a selected city, if no city is specified opens the city selection screen
43     public static final String ACTION_ADD_CLOCK = ACTION_PREFIX + "ADD_CLOCK";
44     // delete a clock of a selected city, if no city is specified shows CitiesActivity for the user
45     // to choose a city
46     public static final String ACTION_DELETE_CLOCK = ACTION_PREFIX + "DELETE_CLOCK";
47     // extra for ACTION_ADD_CLOCK and ACTION_DELETE_CLOCK
48     public static final String EXTRA_CITY = "com.android.deskclock.extra.clock.CITY";
49 
50     // shows the tab with the stopwatch
51     public static final String ACTION_SHOW_STOPWATCH = ACTION_PREFIX + "SHOW_STOPWATCH";
52     // starts the current stopwatch
53     public static final String ACTION_START_STOPWATCH = ACTION_PREFIX + "START_STOPWATCH";
54     // pauses the current stopwatch that's currently running
55     public static final String ACTION_PAUSE_STOPWATCH = ACTION_PREFIX + "PAUSE_STOPWATCH";
56     // laps the stopwatch that's currently running
57     public static final String ACTION_LAP_STOPWATCH = ACTION_PREFIX + "LAP_STOPWATCH";
58     // resets the stopwatch if it's stopped
59     public static final String ACTION_RESET_STOPWATCH = ACTION_PREFIX + "RESET_STOPWATCH";
60 
61     // shows the tab with timers; optionally scrolls to a specific timer
62     public static final String ACTION_SHOW_TIMERS = ACTION_PREFIX + "SHOW_TIMERS";
63     // pauses running timers; resets expired timers
64     public static final String ACTION_PAUSE_TIMER = ACTION_PREFIX + "PAUSE_TIMER";
65     // starts the sole timer
66     public static final String ACTION_START_TIMER = ACTION_PREFIX + "START_TIMER";
67     // resets the timer
68     public static final String ACTION_RESET_TIMER = ACTION_PREFIX + "RESET_TIMER";
69     // adds an extra minute to the timer
70     public static final String ACTION_ADD_MINUTE_TIMER = ACTION_PREFIX + "ADD_MINUTE_TIMER";
71 
72     // extra for many actions specific to a given timer
73     public static final String EXTRA_TIMER_ID =
74             "com.android.deskclock.extra.TIMER_ID";
75 
76     // Describes the entity responsible for the action being performed.
77     public static final String EXTRA_EVENT_LABEL = "com.android.deskclock.extra.EVENT_LABEL";
78 
79     @Override
onCreate(Bundle icicle)80     protected void onCreate(Bundle icicle) {
81         try {
82             super.onCreate(icicle);
83             mAppContext = getApplicationContext();
84 
85             final Intent intent = getIntent();
86             if (intent == null) {
87                 return;
88             }
89 
90             final String action = intent.getAction();
91             LogUtils.i("HandleDeskClockApiCalls " + action);
92 
93             switch (action) {
94                 case ACTION_START_STOPWATCH:
95                 case ACTION_PAUSE_STOPWATCH:
96                 case ACTION_LAP_STOPWATCH:
97                 case ACTION_SHOW_STOPWATCH:
98                 case ACTION_RESET_STOPWATCH:
99                     handleStopwatchIntent(intent);
100                     break;
101                 case ACTION_SHOW_TIMERS:
102                 case ACTION_RESET_TIMER:
103                 case ACTION_PAUSE_TIMER:
104                 case ACTION_START_TIMER:
105                     handleTimerIntent(intent);
106                     break;
107                 case ACTION_SHOW_CLOCK:
108                 case ACTION_ADD_CLOCK:
109                 case ACTION_DELETE_CLOCK:
110                     handleClockIntent(intent);
111                     break;
112             }
113         } finally {
114             finish();
115         }
116     }
117 
handleStopwatchIntent(Intent intent)118     private void handleStopwatchIntent(Intent intent) {
119         final String action = intent.getAction();
120 
121         // Determine where this intent originated.
122         final int eventLabel = intent.getIntExtra(EXTRA_EVENT_LABEL, R.string.label_intent);
123 
124         if (ACTION_SHOW_STOPWATCH.equals(action)) {
125             Events.sendStopwatchEvent(R.string.action_show, eventLabel);
126         } else {
127             final String reason;
128             boolean fail = false;
129             switch (action) {
130                 case ACTION_START_STOPWATCH: {
131                     DataModel.getDataModel().startStopwatch();
132                     Events.sendStopwatchEvent(R.string.action_start, eventLabel);
133                     reason = getString(R.string.stopwatch_started);
134                     break;
135                 }
136                 case ACTION_PAUSE_STOPWATCH: {
137                     DataModel.getDataModel().pauseStopwatch();
138                     Events.sendStopwatchEvent(R.string.action_pause, eventLabel);
139                     reason = getString(R.string.stopwatch_paused);
140                     break;
141                 }
142                 case ACTION_RESET_STOPWATCH: {
143                     DataModel.getDataModel().clearLaps();
144                     DataModel.getDataModel().resetStopwatch();
145                     Events.sendStopwatchEvent(R.string.action_reset, eventLabel);
146                     reason = getString(R.string.stopwatch_reset);
147                     break;
148                 }
149                 case ACTION_LAP_STOPWATCH: {
150                     if (!DataModel.getDataModel().getStopwatch().isRunning()) {
151                         fail = true;
152                         reason = getString(R.string.stopwatch_isnt_running);
153                     } else {
154                         DataModel.getDataModel().addLap();
155                         Events.sendStopwatchEvent(R.string.action_lap, eventLabel);
156                         reason = getString(R.string.stopwatch_lapped);
157                     }
158                     break;
159                 }
160                 default:
161                     throw new IllegalArgumentException("unknown stopwatch action: " + action);
162             }
163 
164             if (fail) {
165                 Voice.notifyFailure(this, reason);
166             } else {
167                 Voice.notifySuccess(this, reason);
168             }
169             LogUtils.i(reason);
170         }
171 
172         // Open the UI to the stopwatch.
173         final Intent stopwatchIntent = new Intent(mAppContext, DeskClock.class)
174                 .putExtra(DeskClock.SELECT_TAB_INTENT_EXTRA, DeskClock.STOPWATCH_TAB_INDEX);
175         startActivity(stopwatchIntent);
176     }
177 
handleTimerIntent(Intent intent)178     private void handleTimerIntent(Intent intent) {
179         final String action = intent.getAction();
180 
181         // Determine where this intent originated.
182         final int eventLabel = intent.getIntExtra(EXTRA_EVENT_LABEL, R.string.label_intent);
183         int timerId = intent.getIntExtra(EXTRA_TIMER_ID, -1);
184         Timer timer = null;
185 
186         if (ACTION_SHOW_TIMERS.equals(action)) {
187             Events.sendTimerEvent(R.string.action_show, eventLabel);
188         } else {
189             String reason = null;
190             if (timerId == -1) {
191                 // No timer id was given explicitly, so check if only one timer exists.
192                 final List<Timer> timers =  DataModel.getDataModel().getTimers();
193                 if (timers.isEmpty()) {
194                     // No timers exist to control.
195                     reason = getString(R.string.no_timers_exist);
196                 } else if (timers.size() > 1) {
197                     // Many timers exist so the control command is ambiguous.
198                     reason = getString(R.string.too_many_timers_exist);
199                 } else {
200                     timer = timers.get(0);
201                 }
202             } else {
203                 // Verify that the given timer does exist.
204                 timer = DataModel.getDataModel().getTimer(timerId);
205                 if (timer == null) {
206                     reason = getString(R.string.timer_does_not_exist);
207                 }
208             }
209 
210             if (timer == null) {
211                 Voice.notifyFailure(this, reason);
212             } else {
213                 timerId = timer.getId();
214 
215                 // Otherwise the control command can be honored.
216                 switch (action) {
217                     case ACTION_RESET_TIMER: {
218                         DataModel.getDataModel().resetOrDeleteTimer(timer, eventLabel);
219                         if (timer.isExpired() && timer.getDeleteAfterUse()) {
220                             timerId = -1;
221                             reason = getString(R.string.timer_deleted);
222                         } else {
223                             reason = getString(R.string.timer_was_reset);
224                         }
225                         break;
226                     }
227                     case ACTION_START_TIMER: {
228                         DataModel.getDataModel().startTimer(timer);
229                         Events.sendTimerEvent(R.string.action_start, eventLabel);
230                         reason = getString(R.string.timer_started);
231                         break;
232                     }
233                     case ACTION_PAUSE_TIMER: {
234                         DataModel.getDataModel().pauseTimer(timer);
235                         Events.sendTimerEvent(R.string.action_pause, eventLabel);
236                         reason = getString(R.string.timer_paused);
237                         break;
238                     }
239                     default:
240                         throw new IllegalArgumentException("unknown timer action: " + action);
241                 }
242 
243                 Voice.notifySuccess(this, reason);
244             }
245 
246             LogUtils.i(reason);
247         }
248 
249         // Open the UI to the timers.
250         final Intent showTimers = new Intent(mAppContext, DeskClock.class)
251                 .putExtra(DeskClock.SELECT_TAB_INTENT_EXTRA, DeskClock.TIMER_TAB_INDEX);
252         if (timerId != -1) {
253             showTimers.putExtra(EXTRA_TIMER_ID, timerId);
254         }
255         startActivity(showTimers);
256     }
257 
handleClockIntent(Intent intent)258     private void handleClockIntent(Intent intent) {
259         final String action = intent.getAction();
260 
261         if (ACTION_SHOW_CLOCK.equals(action)) {
262             final boolean fromWidget = intent.getBooleanExtra(EXTRA_FROM_WIDGET, false);
263             final int label = fromWidget ? R.string.label_widget : R.string.label_intent;
264             Events.sendClockEvent(R.string.action_show, label);
265         } else {
266             final String cityName = intent.getStringExtra(EXTRA_CITY);
267 
268             final String reason;
269             boolean fail = false;
270 
271             // If no city was given, start the city chooser.
272             if (cityName == null) {
273                 reason = getString(R.string.no_city_selected);
274                 LogUtils.i(reason);
275                 Voice.notifySuccess(this, reason);
276                 startActivity(new Intent(this, CitySelectionActivity.class)
277                         .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
278                 switch (action) {
279                     case ACTION_ADD_CLOCK:
280                         Events.sendClockEvent(R.string.action_add, R.string.label_intent);
281                         break;
282                     case ACTION_DELETE_CLOCK:
283                         Events.sendClockEvent(R.string.action_delete, R.string.label_intent);
284                         break;
285                 }
286                 return;
287             }
288 
289             // If a city was given, ensure it can be located.
290             final City city = DataModel.getDataModel().getCity(cityName);
291             if (city == null) {
292                 reason = getString(R.string.the_city_you_specified_is_not_available);
293                 LogUtils.i(reason);
294                 Voice.notifyFailure(this, reason);
295                 switch (action) {
296                     case ACTION_ADD_CLOCK:
297                         Events.sendClockEvent(R.string.action_add, R.string.label_intent);
298                         break;
299                     case ACTION_DELETE_CLOCK:
300                         Events.sendClockEvent(R.string.action_delete, R.string.label_intent);
301                         break;
302                 }
303                 return;
304             }
305 
306             final Set<City> selectedCities =
307                     Utils.newArraySet(DataModel.getDataModel().getSelectedCities());
308 
309             switch (action) {
310                 case ACTION_ADD_CLOCK: {
311                     // Fail if the city is already present.
312                     if (!selectedCities.add(city)) {
313                         fail = true;
314                         reason = getString(R.string.the_city_already_added);
315                         break;
316                     }
317 
318                     // Otherwise report the success.
319                     DataModel.getDataModel().setSelectedCities(selectedCities);
320                     reason = getString(R.string.city_added, city.getName());
321                     Events.sendClockEvent(R.string.action_add, R.string.label_intent);
322                     break;
323                 }
324                 case ACTION_DELETE_CLOCK: {
325                     // Fail if the city is not present.
326                     if (!selectedCities.remove(city)) {
327                         fail = true;
328                         reason = getString(R.string.the_city_you_specified_is_not_available);
329                         break;
330                     }
331 
332                     // Otherwise report the success.
333                     DataModel.getDataModel().setSelectedCities(selectedCities);
334                     reason = getString(R.string.city_deleted, city.getName());
335                     Events.sendClockEvent(R.string.action_delete, R.string.label_intent);
336                     break;
337                 }
338                 default:
339                     throw new IllegalArgumentException("unknown clock action: " + action);
340             }
341 
342             if (fail) {
343                 Voice.notifyFailure(this, reason);
344             } else {
345                 Voice.notifySuccess(this, reason);
346             }
347             LogUtils.i(reason);
348         }
349 
350         // Opens the UI for clocks
351         final Intent clockIntent = new Intent(mAppContext, DeskClock.class)
352                 .setAction(action)
353                 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
354                 .putExtra(DeskClock.SELECT_TAB_INTENT_EXTRA, DeskClock.CLOCK_TAB_INDEX);
355         startActivity(clockIntent);
356     }
357 }