Initial Contribution
[android/platform/packages/apps/Calendar.git] / src / com / android / calendar / AlertActivity.java
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
17 package com.android.calendar;
18
19 import static android.provider.Calendar.EVENT_BEGIN_TIME;
20 import static android.provider.Calendar.EVENT_END_TIME;
21
22 import android.app.Activity;
23 import android.app.AlarmManager;
24 import android.app.NotificationManager;
25 import android.app.PendingIntent;
26 import android.content.AsyncQueryHandler;
27 import android.content.ContentResolver;
28 import android.content.ContentUris;
29 import android.content.Context;
30 import android.content.Intent;
31 import android.content.res.TypedArray;
32 import android.database.Cursor;
33 import android.graphics.PixelFormat;
34 import android.net.Uri;
35 import android.os.Bundle;
36 import android.provider.Calendar;
37 import android.provider.Calendar.CalendarAlerts;
38 import android.provider.Calendar.Events;
39 import android.view.View;
40 import android.view.ViewGroup;
41 import android.view.Window;
42 import android.view.WindowManager;
43 import android.view.View.OnClickListener;
44 import android.widget.AdapterView;
45 import android.widget.Button;
46 import android.widget.ListView;
47 import android.widget.AdapterView.OnItemClickListener;
48
49 /**
50  * The alert panel that pops up when there is a calendar event alarm.
51  * This activity is started by an intent that specifies an event id.
52   */
53 public class AlertActivity extends Activity {
54     
55     // The default snooze delay: 5 minutes
56     public static final long SNOOZE_DELAY = 5 * 60 * 1000L;
57     
58     private static final String[] PROJECTION = new String[] { 
59         CalendarAlerts._ID,              // 0
60         CalendarAlerts.TITLE,            // 1
61         CalendarAlerts.EVENT_LOCATION,   // 2
62         CalendarAlerts.ALL_DAY,          // 3
63         CalendarAlerts.BEGIN,            // 4
64         CalendarAlerts.END,              // 5
65         CalendarAlerts.EVENT_ID,         // 6
66         CalendarAlerts.COLOR,            // 7
67         CalendarAlerts.RRULE,            // 8
68         CalendarAlerts.HAS_ALARM,        // 9
69         CalendarAlerts.STATE,            // 10
70         CalendarAlerts.ALARM_TIME,       // 11
71     };
72     
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     // We use one notification id for all events so that we don't clutter
86     // the notification screen.  It doesn't matter what the id is, as long
87     // as it is used consistently everywhere.
88     public static final int NOTIFICATION_ID = 0;
89     
90     private ContentResolver mResolver;
91     private AlertAdapter mAdapter;
92     private QueryHandler mQueryHandler;
93     private Cursor mCursor;
94     private ListView mListView;
95     private Button mSnoozeAllButton;
96     private Button mDismissAllButton;
97     
98     private class QueryHandler extends AsyncQueryHandler {
99         public QueryHandler(ContentResolver cr) {
100             super(cr);
101         }
102
103         @Override
104         protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
105             // Only set mCursor if the Activity is not finishing. Otherwise close the cursor.
106             if (!isFinishing()) {
107                 mCursor = cursor;
108                 mAdapter.changeCursor(cursor);
109                 
110                 // The results are in, enable the buttons
111                 mSnoozeAllButton.setEnabled(true);
112                 mDismissAllButton.setEnabled(true);
113             } else {
114                 cursor.close();
115             }
116         }
117         
118     }
119     
120     private OnItemClickListener mViewListener = new OnItemClickListener() {
121
122         public void onItemClick(AdapterView parent, View view, int position,
123                 long i) {
124             AlertActivity alertActivity = AlertActivity.this;
125             Cursor cursor = alertActivity.getItemForView(view);
126             
127             long id = cursor.getInt(AlertActivity.INDEX_EVENT_ID);
128             long startMillis = cursor.getLong(AlertActivity.INDEX_BEGIN);
129             long endMillis = cursor.getLong(AlertActivity.INDEX_END);
130             
131             Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, id);
132             Intent intent = new Intent(Intent.ACTION_VIEW, uri);
133             intent.setClass(alertActivity, EventInfoActivity.class);
134             intent.putExtra(EVENT_BEGIN_TIME, startMillis);
135             intent.putExtra(EVENT_END_TIME, endMillis);
136             
137             // Mark this alarm as DISMISSED
138             cursor.updateInt(INDEX_STATE, CalendarAlerts.DISMISSED);
139             cursor.commitUpdates();
140             
141             startActivity(intent);
142             alertActivity.finish();
143         }
144     };
145     
146     @Override
147     protected void onCreate(Bundle icicle) {
148         super.onCreate(icicle);
149         
150         requestWindowFeature(Window.FEATURE_NO_TITLE);
151         setContentView(R.layout.alert_activity);
152         
153         WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
154                 ViewGroup.LayoutParams.FILL_PARENT,             // width
155                 ViewGroup.LayoutParams.FILL_PARENT,             // height
156                 WindowManager.LayoutParams.TYPE_TOAST,          // type
157                 WindowManager.LayoutParams.FLAG_BLUR_BEHIND,    // flags
158                 PixelFormat.TRANSLUCENT);                       // format
159         
160         // Get the dim amount from the theme
161         TypedArray a = obtainStyledAttributes(com.android.internal.R.styleable.Theme);
162         lp.dimAmount = a.getFloat(android.R.styleable.Theme_backgroundDimAmount, 0.5f);
163         a.recycle();
164
165         getWindow().setAttributes(lp);
166         
167         mResolver = getContentResolver();
168         mQueryHandler = new QueryHandler(mResolver);
169         mAdapter = new AlertAdapter(this, R.layout.alert_item);
170         
171         mListView = (ListView) findViewById(R.id.alert_container);
172         mListView.setItemsCanFocus(true);
173         mListView.setAdapter(mAdapter);
174         mListView.setOnItemClickListener(mViewListener);
175         
176         mSnoozeAllButton = (Button) findViewById(R.id.snooze_all);
177         mSnoozeAllButton.setOnClickListener(mSnoozeAllListener);
178         mDismissAllButton = (Button) findViewById(R.id.dismiss_all);
179         mDismissAllButton.setOnClickListener(mDismissAllListener);
180
181         // Disable the buttons, since they need mCursor, which is created asynchronously
182         mSnoozeAllButton.setEnabled(false);
183         mDismissAllButton.setEnabled(false);
184     }
185     
186     @Override
187     protected void onResume() {
188         super.onResume();
189         
190         // If the cursor is null, start the async handler. If it is not null just requery.
191         if (mCursor == null) {
192             Uri uri = CalendarAlerts.CONTENT_URI_BY_INSTANCE;
193             String selection = CalendarAlerts.STATE + "=" + CalendarAlerts.FIRED;
194             mQueryHandler.startQuery(0, null, uri, PROJECTION, selection, 
195                     null /* selection args */, CalendarAlerts.DEFAULT_SORT_ORDER);
196         } else {
197             mCursor.requery();
198         }
199     }
200     
201     @Override
202     protected void onStop() {
203         super.onStop();
204         AlertReceiver.updateAlertNotification(this);
205         
206         if (mCursor != null) {
207             mCursor.deactivate();
208         }
209     }
210     
211     @Override
212     protected void onDestroy() {
213         super.onDestroy();
214         if (mCursor != null) {
215             mCursor.close();
216         }
217     }
218     
219     private OnClickListener mSnoozeAllListener = new OnClickListener() {
220         public void onClick(View v) {
221             NotificationManager nm =
222                 (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
223             nm.cancel(NOTIFICATION_ID);
224             mCursor.moveToPosition(-1);
225             while (mCursor.moveToNext()) {
226                 long eventId = mCursor.getLong(INDEX_EVENT_ID);
227                 long begin = mCursor.getLong(INDEX_BEGIN);
228                 long end = mCursor.getLong(INDEX_END);
229                 long alarmTime = mCursor.getLong(INDEX_ALARM_TIME);
230
231                 // Mark this alarm as DISMISSED
232                 mCursor.updateInt(INDEX_STATE, CalendarAlerts.DISMISSED);
233                 
234                 // Create a new alarm entry in the CalendarAlerts table
235                 long now = System.currentTimeMillis();
236                 alarmTime = now + SNOOZE_DELAY;
237                 
238                 // Set the "minutes" to zero to indicate this is a snoozed
239                 // alarm.  There is code in AlertService.java that checks
240                 // this field.
241                 Uri uri = CalendarAlerts.insert(mResolver, eventId,
242                         begin, end, alarmTime, 0 /* minutes */);
243                 
244                 // Set a new alarm to go off after the snooze delay.
245                 Intent intent = new Intent(Calendar.EVENT_REMINDER_ACTION);
246                 intent.setData(uri);
247                 intent.putExtra(Calendar.EVENT_BEGIN_TIME, begin);
248                 intent.putExtra(Calendar.EVENT_END_TIME, end);
249                 
250                 PendingIntent sender = PendingIntent.getBroadcast(AlertActivity.this,
251                         0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
252                 Object service = getSystemService(Context.ALARM_SERVICE);
253                 AlarmManager alarmManager = (AlarmManager) service;
254                 alarmManager.set(AlarmManager.RTC_WAKEUP, alarmTime, sender);
255             }
256             mCursor.commitUpdates();
257             finish();
258         }
259     };
260     
261     private OnClickListener mDismissAllListener = new OnClickListener() {
262         public void onClick(View v) {
263             NotificationManager nm =
264                 (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
265             nm.cancel(NOTIFICATION_ID);
266             mCursor.moveToPosition(-1);
267             while (mCursor.moveToNext()) {
268                 mCursor.updateInt(INDEX_STATE, CalendarAlerts.DISMISSED);
269             }
270             mCursor.commitUpdates();
271             finish();
272         }
273     };
274     
275     public boolean isEmpty() {
276         return (mCursor.getCount() == 0);
277     }
278     
279     public Cursor getItemForView(View view) {
280         int index = mListView.getPositionForView(view);
281         if (index < 0) {
282             return null;
283         }
284         return (Cursor) mListView.getAdapter().getItem(index);
285     }
286 }