AlertActivity.java revision 23e7da3eacee7bceb105cdfc7b5329c7a43846d5
1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.calendar.alerts;
18
19import com.android.calendar.AsyncQueryService;
20import com.android.calendar.CalendarController;
21import com.android.calendar.CalendarController.EventType;
22import com.android.calendar.R;
23import com.android.calendar.R.id;
24import com.android.calendar.R.layout;
25import com.android.calendar.R.string;
26import com.android.calendar.Utils;
27
28import android.app.Activity;
29import android.app.AlarmManager;
30import android.app.NotificationManager;
31import android.content.ContentResolver;
32import android.content.ContentValues;
33import android.content.Context;
34import android.database.Cursor;
35import android.net.Uri;
36import android.os.Bundle;
37import android.provider.Calendar.CalendarAlerts;
38import android.provider.Calendar.CalendarAlertsColumns;
39import android.view.View;
40import android.view.ViewGroup;
41import android.view.WindowManager;
42import android.view.View.OnClickListener;
43import android.widget.AdapterView;
44import android.widget.Button;
45import android.widget.ListView;
46import android.widget.AdapterView.OnItemClickListener;
47
48/**
49 * The alert panel that pops up when there is a calendar event alarm.
50 * This activity is started by an intent that specifies an event id.
51  */
52public class AlertActivity extends Activity {
53
54    // The default snooze delay: 5 minutes
55    public static final long SNOOZE_DELAY = 5 * 60 * 1000L;
56
57    private static final String[] PROJECTION = new String[] {
58        CalendarAlerts._ID,              // 0
59        CalendarAlerts.TITLE,            // 1
60        CalendarAlerts.EVENT_LOCATION,   // 2
61        CalendarAlerts.ALL_DAY,          // 3
62        CalendarAlerts.BEGIN,            // 4
63        CalendarAlerts.END,              // 5
64        CalendarAlerts.EVENT_ID,         // 6
65        CalendarAlerts.COLOR,            // 7
66        CalendarAlerts.RRULE,            // 8
67        CalendarAlerts.HAS_ALARM,        // 9
68        CalendarAlerts.STATE,            // 10
69        CalendarAlerts.ALARM_TIME,       // 11
70    };
71
72    public static final int INDEX_ROW_ID = 0;
73    public static final int INDEX_TITLE = 1;
74    public static final int INDEX_EVENT_LOCATION = 2;
75    public static final int INDEX_ALL_DAY = 3;
76    public static final int INDEX_BEGIN = 4;
77    public static final int INDEX_END = 5;
78    public static final int INDEX_EVENT_ID = 6;
79    public static final int INDEX_COLOR = 7;
80    public static final int INDEX_RRULE = 8;
81    public static final int INDEX_HAS_ALARM = 9;
82    public static final int INDEX_STATE = 10;
83    public static final int INDEX_ALARM_TIME = 11;
84
85    private static final String SELECTION = CalendarAlerts.STATE + "=?";
86    private static final String[] SELECTIONARG = new String[] {
87        Integer.toString(CalendarAlerts.FIRED)
88    };
89
90    // We use one notification id for all events so that we don't clutter
91    // the notification screen.  It doesn't matter what the id is, as long
92    // as it is used consistently everywhere.
93    public static final int NOTIFICATION_ID = 0;
94
95    private ContentResolver mResolver;
96    private AlertAdapter mAdapter;
97    private QueryHandler mQueryHandler;
98    private Cursor mCursor;
99    private ListView mListView;
100    private Button mSnoozeAllButton;
101    private Button mDismissAllButton;
102
103
104    private void dismissFiredAlarms() {
105        ContentValues values = new ContentValues(1 /* size */);
106        values.put(PROJECTION[INDEX_STATE], CalendarAlerts.DISMISSED);
107        String selection = CalendarAlerts.STATE + "=" + CalendarAlerts.FIRED;
108        mQueryHandler.startUpdate(0, null, CalendarAlerts.CONTENT_URI, values,
109                selection, null /* selectionArgs */, Utils.UNDO_DELAY);
110    }
111
112    private void dismissAlarm(long id) {
113        ContentValues values = new ContentValues(1 /* size */);
114        values.put(PROJECTION[INDEX_STATE], CalendarAlerts.DISMISSED);
115        String selection = CalendarAlerts._ID + "=" + id;
116        mQueryHandler.startUpdate(0, null, CalendarAlerts.CONTENT_URI, values,
117                selection, null /* selectionArgs */, Utils.UNDO_DELAY);
118    }
119
120    private class QueryHandler extends AsyncQueryService {
121        public QueryHandler(Context context) {
122            super(context);
123        }
124
125        @Override
126        protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
127            // Only set mCursor if the Activity is not finishing. Otherwise close the cursor.
128            if (!isFinishing()) {
129                mCursor = cursor;
130                mAdapter.changeCursor(cursor);
131
132                // The results are in, enable the buttons
133                mSnoozeAllButton.setEnabled(true);
134                mDismissAllButton.setEnabled(true);
135            } else {
136                cursor.close();
137            }
138        }
139
140        @Override
141        protected void onInsertComplete(int token, Object cookie, Uri uri) {
142            if (uri != null) {
143                Long alarmTime = (Long) cookie;
144
145                if (alarmTime != 0) {
146                    // Set a new alarm to go off after the snooze delay.
147                    AlarmManager alarmManager =
148                            (AlarmManager) getSystemService(Context.ALARM_SERVICE);
149                    CalendarAlerts.scheduleAlarm(AlertActivity.this, alarmManager, alarmTime);
150                }
151            }
152        }
153
154        @Override
155        protected void onUpdateComplete(int token, Object cookie, int result) {
156            // Ignore
157        }
158    }
159
160    private static ContentValues makeContentValues(long eventId, long begin, long end,
161            long alarmTime, int minutes) {
162        ContentValues values = new ContentValues();
163        values.put(CalendarAlerts.EVENT_ID, eventId);
164        values.put(CalendarAlerts.BEGIN, begin);
165        values.put(CalendarAlerts.END, end);
166        values.put(CalendarAlerts.ALARM_TIME, alarmTime);
167        long currentTime = System.currentTimeMillis();
168        values.put(CalendarAlerts.CREATION_TIME, currentTime);
169        values.put(CalendarAlerts.RECEIVED_TIME, 0);
170        values.put(CalendarAlerts.NOTIFY_TIME, 0);
171        values.put(CalendarAlerts.STATE, CalendarAlertsColumns.SCHEDULED);
172        values.put(CalendarAlerts.MINUTES, minutes);
173        return values;
174    }
175
176    private OnItemClickListener mViewListener = new OnItemClickListener() {
177
178        public void onItemClick(AdapterView<?> parent, View view, int position,
179                long i) {
180            AlertActivity alertActivity = AlertActivity.this;
181            Cursor cursor = alertActivity.getItemForView(view);
182
183            // Mark this alarm as DISMISSED
184            dismissAlarm(cursor.getLong(INDEX_ROW_ID));
185
186            long id = cursor.getInt(AlertActivity.INDEX_EVENT_ID);
187            long startMillis = cursor.getLong(AlertActivity.INDEX_BEGIN);
188            long endMillis = cursor.getLong(AlertActivity.INDEX_END);
189            CalendarController.getInstance(alertActivity).sendEventRelatedEvent(alertActivity,
190                    EventType.VIEW_EVENT, id, startMillis, endMillis, 0, 0);
191
192            alertActivity.finish();
193        }
194    };
195
196    @Override
197    protected void onCreate(Bundle icicle) {
198        super.onCreate(icicle);
199
200        setContentView(R.layout.alert_activity);
201        setTitle(R.string.alert_title);
202
203        WindowManager.LayoutParams lp = getWindow().getAttributes();
204        lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
205        lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
206
207        getWindow().setAttributes(lp);
208
209        mResolver = getContentResolver();
210        mQueryHandler = new QueryHandler(this);
211        mAdapter = new AlertAdapter(this, R.layout.alert_item);
212
213        mListView = (ListView) findViewById(R.id.alert_container);
214        mListView.setItemsCanFocus(true);
215        mListView.setAdapter(mAdapter);
216        mListView.setOnItemClickListener(mViewListener);
217
218        mSnoozeAllButton = (Button) findViewById(R.id.snooze_all);
219        mSnoozeAllButton.setOnClickListener(mSnoozeAllListener);
220        mDismissAllButton = (Button) findViewById(R.id.dismiss_all);
221        mDismissAllButton.setOnClickListener(mDismissAllListener);
222
223        // Disable the buttons, since they need mCursor, which is created asynchronously
224        mSnoozeAllButton.setEnabled(false);
225        mDismissAllButton.setEnabled(false);
226    }
227
228    @Override
229    protected void onResume() {
230        super.onResume();
231
232        // If the cursor is null, start the async handler. If it is not null just requery.
233        if (mCursor == null) {
234            Uri uri = CalendarAlerts.CONTENT_URI_BY_INSTANCE;
235            mQueryHandler.startQuery(0, null, uri, PROJECTION, SELECTION,
236                    SELECTIONARG, CalendarAlerts.DEFAULT_SORT_ORDER);
237        } else {
238            mCursor.requery();
239        }
240    }
241
242    @Override
243    protected void onStop() {
244        super.onStop();
245        AlertService.updateAlertNotification(this);
246
247        if (mCursor != null) {
248            mCursor.deactivate();
249        }
250    }
251
252    @Override
253    protected void onDestroy() {
254        super.onDestroy();
255        if (mCursor != null) {
256            mCursor.close();
257        }
258    }
259
260    private OnClickListener mSnoozeAllListener = new OnClickListener() {
261        public void onClick(View v) {
262            long alarmTime = System.currentTimeMillis() + SNOOZE_DELAY;
263
264            NotificationManager nm =
265                (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
266            nm.cancel(NOTIFICATION_ID);
267
268            long scheduleAlarmTime = 0;
269            mCursor.moveToPosition(-1);
270            while (mCursor.moveToNext()) {
271                long eventId = mCursor.getLong(INDEX_EVENT_ID);
272                long begin = mCursor.getLong(INDEX_BEGIN);
273                long end = mCursor.getLong(INDEX_END);
274
275                // Set the "minutes" to zero to indicate this is a snoozed
276                // alarm.  There is code in AlertService.java that checks
277                // this field.
278                ContentValues values =
279                        makeContentValues(eventId, begin, end, alarmTime, 0 /* minutes */);
280
281                // Create a new alarm entry in the CalendarAlerts table
282                if (mCursor.isLast()) {
283                    scheduleAlarmTime = alarmTime;
284                }
285                mQueryHandler.startInsert(0, scheduleAlarmTime, CalendarAlerts.CONTENT_URI, values,
286                        Utils.UNDO_DELAY);
287            }
288
289            dismissFiredAlarms();
290
291            finish();
292        }
293    };
294
295    private OnClickListener mDismissAllListener = new OnClickListener() {
296        public void onClick(View v) {
297            NotificationManager nm =
298                (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
299            nm.cancel(NOTIFICATION_ID);
300
301            dismissFiredAlarms();
302
303            finish();
304        }
305    };
306
307    public boolean isEmpty() {
308        return (mCursor.getCount() == 0);
309    }
310
311    public Cursor getItemForView(View view) {
312        int index = mListView.getPositionForView(view);
313        if (index < 0) {
314            return null;
315        }
316        return (Cursor) mListView.getAdapter().getItem(index);
317    }
318}
319