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