AlertActivity.java revision 0000fa426da7fbfd35f16bf43556e861dfcf6667
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 android.annotation.SuppressLint;
20import android.app.Activity;
21import android.app.NotificationManager;
22import android.app.TaskStackBuilder;
23import android.content.ContentValues;
24import android.content.Context;
25import android.content.Intent;
26import android.database.Cursor;
27import android.net.Uri;
28import android.os.Bundle;
29import android.provider.CalendarContract;
30import android.provider.CalendarContract.CalendarAlerts;
31import android.util.Log;
32import android.view.View;
33import android.view.View.OnClickListener;
34import android.widget.AdapterView;
35import android.widget.AdapterView.OnItemClickListener;
36import android.widget.Button;
37import android.widget.ListView;
38
39import com.android.calendar.AsyncQueryService;
40import com.android.calendar.EventInfoActivity;
41import com.android.calendar.R;
42import com.android.calendar.Utils;
43import com.android.calendar.alerts.GlobalDismissManager.AlarmId;
44
45import java.util.LinkedList;
46import java.util.List;
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 implements OnClickListener {
53    private static final String TAG = "AlertActivity";
54
55    private static final String[] PROJECTION = new String[] {
56        CalendarAlerts._ID,              // 0
57        CalendarAlerts.TITLE,            // 1
58        CalendarAlerts.EVENT_LOCATION,   // 2
59        CalendarAlerts.ALL_DAY,          // 3
60        CalendarAlerts.BEGIN,            // 4
61        CalendarAlerts.END,              // 5
62        CalendarAlerts.EVENT_ID,         // 6
63        CalendarAlerts.CALENDAR_COLOR,   // 7
64        CalendarAlerts.RRULE,            // 8
65        CalendarAlerts.HAS_ALARM,        // 9
66        CalendarAlerts.STATE,            // 10
67        CalendarAlerts.ALARM_TIME,       // 11
68    };
69
70    public static final int INDEX_ROW_ID = 0;
71    public static final int INDEX_TITLE = 1;
72    public static final int INDEX_EVENT_LOCATION = 2;
73    public static final int INDEX_ALL_DAY = 3;
74    public static final int INDEX_BEGIN = 4;
75    public static final int INDEX_END = 5;
76    public static final int INDEX_EVENT_ID = 6;
77    public static final int INDEX_COLOR = 7;
78    public static final int INDEX_RRULE = 8;
79    public static final int INDEX_HAS_ALARM = 9;
80    public static final int INDEX_STATE = 10;
81    public static final int INDEX_ALARM_TIME = 11;
82
83    private static final String SELECTION = CalendarAlerts.STATE + "=?";
84    private static final String[] SELECTIONARG = new String[] {
85        Integer.toString(CalendarAlerts.STATE_FIRED)
86    };
87
88    private AlertAdapter mAdapter;
89    private QueryHandler mQueryHandler;
90    private Cursor mCursor;
91    private ListView mListView;
92    private Button mDismissAllButton;
93
94
95    private void dismissFiredAlarms() {
96        ContentValues values = new ContentValues(1 /* size */);
97        values.put(PROJECTION[INDEX_STATE], CalendarAlerts.STATE_DISMISSED);
98        String selection = CalendarAlerts.STATE + "=" + CalendarAlerts.STATE_FIRED;
99        mQueryHandler.startUpdate(0, null, CalendarAlerts.CONTENT_URI, values,
100                selection, null /* selectionArgs */, Utils.UNDO_DELAY);
101
102        if (mCursor == null) {
103            Log.e(TAG, "Unable to globally dismiss all notifications because cursor was null.");
104            return;
105        }
106        if (mCursor.isClosed()) {
107            Log.e(TAG, "Unable to globally dismiss all notifications because cursor was closed.");
108            return;
109        }
110        if (!mCursor.moveToFirst()) {
111            Log.e(TAG, "Unable to globally dismiss all notifications because cursor was empty.");
112            return;
113        }
114
115        List<AlarmId> alarmIds = new LinkedList<AlarmId>();
116        do {
117            long eventId = mCursor.getLong(INDEX_EVENT_ID);
118            long eventStart = mCursor.getLong(INDEX_BEGIN);
119            alarmIds.add(new AlarmId(eventId, eventStart));
120        } while (mCursor.moveToNext());
121        initiateGlobalDismiss(alarmIds);
122    }
123
124    private void dismissAlarm(long id, long eventId, long startTime) {
125        ContentValues values = new ContentValues(1 /* size */);
126        values.put(PROJECTION[INDEX_STATE], CalendarAlerts.STATE_DISMISSED);
127        String selection = CalendarAlerts._ID + "=" + id;
128        mQueryHandler.startUpdate(0, null, CalendarAlerts.CONTENT_URI, values,
129                selection, null /* selectionArgs */, Utils.UNDO_DELAY);
130
131        List<AlarmId> alarmIds = new LinkedList<AlarmId>();
132        alarmIds.add(new AlarmId(eventId, startTime));
133        initiateGlobalDismiss(alarmIds);
134    }
135
136    private void initiateGlobalDismiss(List<AlarmId> alarmIds) {
137        GlobalDismissManager.dismissGlobally(getApplicationContext(), alarmIds);
138    }
139
140    private class QueryHandler extends AsyncQueryService {
141        public QueryHandler(Context context) {
142            super(context);
143        }
144
145        @Override
146        protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
147            // Only set mCursor if the Activity is not finishing. Otherwise close the cursor.
148            if (!isFinishing()) {
149                mCursor = cursor;
150                mAdapter.changeCursor(cursor);
151                mListView.setSelection(cursor.getCount() - 1);
152
153                // The results are in, enable the buttons
154                mDismissAllButton.setEnabled(true);
155            } else {
156                cursor.close();
157            }
158        }
159
160        @Override
161        protected void onUpdateComplete(int token, Object cookie, int result) {
162            // Ignore
163        }
164    }
165
166    private final OnItemClickListener mViewListener = new OnItemClickListener() {
167
168        @SuppressLint("NewApi")
169        @Override
170        public void onItemClick(AdapterView<?> parent, View view, int position,
171                long i) {
172            AlertActivity alertActivity = AlertActivity.this;
173            Cursor cursor = alertActivity.getItemForView(view);
174
175            long alarmId = cursor.getLong(INDEX_ROW_ID);
176            long eventId = cursor.getLong(AlertActivity.INDEX_EVENT_ID);
177            long startMillis = cursor.getLong(AlertActivity.INDEX_BEGIN);
178
179            // Mark this alarm as DISMISSED
180            dismissAlarm(alarmId, eventId, startMillis);
181
182            // build an intent and task stack to start EventInfoActivity with AllInOneActivity
183            // as the parent activity rooted to home.
184            long endMillis = cursor.getLong(AlertActivity.INDEX_END);
185            Intent eventIntent = AlertUtils.buildEventViewIntent(AlertActivity.this, eventId,
186                    startMillis, endMillis);
187
188            if (Utils.isJellybeanOrLater()) {
189                TaskStackBuilder.create(AlertActivity.this).addParentStack(EventInfoActivity.class)
190                        .addNextIntent(eventIntent).startActivities();
191            } else {
192                alertActivity.startActivity(eventIntent);
193            }
194
195            alertActivity.finish();
196        }
197    };
198
199    @Override
200    protected void onCreate(Bundle icicle) {
201        super.onCreate(icicle);
202
203        setContentView(R.layout.alert_activity);
204        setTitle(R.string.alert_title);
205
206        mQueryHandler = new QueryHandler(this);
207        mAdapter = new AlertAdapter(this, R.layout.alert_item);
208
209        mListView = (ListView) findViewById(R.id.alert_container);
210        mListView.setItemsCanFocus(true);
211        mListView.setAdapter(mAdapter);
212        mListView.setOnItemClickListener(mViewListener);
213
214        mDismissAllButton = (Button) findViewById(R.id.dismiss_all);
215        mDismissAllButton.setOnClickListener(this);
216
217        // Disable the buttons, since they need mCursor, which is created asynchronously
218        mDismissAllButton.setEnabled(false);
219    }
220
221    @Override
222    protected void onResume() {
223        super.onResume();
224
225        // If the cursor is null, start the async handler. If it is not null just requery.
226        if (mCursor == null) {
227            Uri uri = CalendarAlerts.CONTENT_URI_BY_INSTANCE;
228            mQueryHandler.startQuery(0, null, uri, PROJECTION, SELECTION, SELECTIONARG,
229                    CalendarContract.CalendarAlerts.DEFAULT_SORT_ORDER);
230        } else {
231            if (!mCursor.requery()) {
232                Log.w(TAG, "Cursor#requery() failed.");
233                mCursor.close();
234                mCursor = null;
235            }
236        }
237    }
238
239    void closeActivityIfEmpty() {
240        if (mCursor != null && !mCursor.isClosed() && mCursor.getCount() == 0) {
241            AlertActivity.this.finish();
242        }
243    }
244
245    @Override
246    protected void onStop() {
247        super.onStop();
248        AlertService.updateAlertNotification(this);
249
250        if (mCursor != null) {
251            mCursor.deactivate();
252        }
253    }
254
255    @Override
256    protected void onDestroy() {
257        super.onDestroy();
258        if (mCursor != null) {
259            mCursor.close();
260        }
261    }
262
263    @Override
264    public void onClick(View v) {
265        if (v == mDismissAllButton) {
266            NotificationManager nm =
267                (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
268            nm.cancelAll();
269
270            dismissFiredAlarms();
271
272            finish();
273        }
274    }
275
276    public boolean isEmpty() {
277        return mCursor != null ? (mCursor.getCount() == 0) : true;
278    }
279
280    public Cursor getItemForView(View view) {
281        final int index = mListView.getPositionForView(view);
282        if (index < 0) {
283            return null;
284        }
285        return (Cursor) mListView.getAdapter().getItem(index);
286    }
287}
288