EditEventFragment.java revision d845fbe558d5229102b58cce70a1b29fe6cb6967
1/* 2 * Copyright (C) 2010 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.event; 18 19import static android.provider.Calendar.EVENT_BEGIN_TIME; 20import static android.provider.Calendar.EVENT_END_TIME; 21 22import com.android.calendar.AbstractCalendarActivity; 23import com.android.calendar.CalendarEventModel; 24import com.android.calendar.CalendarEventModel.Attendee; 25import com.android.calendar.DeleteEventHelper; 26import com.android.calendar.R; 27import com.android.calendar.Utils; 28 29import android.app.Activity; 30import android.app.AlertDialog; 31import android.app.Fragment; 32import android.content.AsyncQueryHandler; 33import android.content.ContentResolver; 34import android.content.DialogInterface; 35import android.content.DialogInterface.OnCancelListener; 36import android.content.DialogInterface.OnClickListener; 37import android.content.Intent; 38import android.database.Cursor; 39import android.database.MatrixCursor; 40import android.net.Uri; 41import android.os.Bundle; 42import android.provider.Calendar.Attendees; 43import android.provider.Calendar.Calendars; 44import android.provider.Calendar.Reminders; 45import android.text.TextUtils; 46import android.util.Log; 47import android.view.LayoutInflater; 48import android.view.View; 49import android.view.ViewGroup; 50import android.widget.LinearLayout; 51import android.widget.Toast; 52 53public class EditEventFragment extends Fragment { 54 private static final String TAG = "EditEventActivity"; 55 56 private static final boolean DEBUG = false; 57 58 private static final int TOKEN_EVENT = 0; 59 private static final int TOKEN_ATTENDEES = 1; 60 private static final int TOKEN_REMINDERS = 2; 61 private static final int TOKEN_CALENDARS = 3; 62 63 EditEventHelper mHelper; 64 CalendarEventModel mModel; 65 CalendarEventModel mOriginalModel; 66 EditEventView mView; 67 QueryHandler mHandler; 68 69 private AlertDialog mModifyDialog; 70 int mModification = Utils.MODIFY_UNINITIALIZED; 71 72 private Intent mIntent; 73 private Uri mUri; 74 private long mBegin; 75 private long mEnd; 76 private boolean mFullscreen; 77 78 private Activity mContext; 79 80 private class QueryHandler extends AsyncQueryHandler { 81 public QueryHandler(ContentResolver cr) { 82 super(cr); 83 } 84 85 @Override 86 protected void onQueryComplete(int token, Object cookie, Cursor cursor) { 87 // If the query didn't return a cursor for some reason return 88 if (cursor == null) { 89 return; 90 } 91 92 // If the Activity is finishing, then close the cursor. 93 // Otherwise, use the new cursor in the adapter. 94 final Activity activity = EditEventFragment.this.getActivity(); 95 if (activity == null || activity.isFinishing()) { 96 cursor.close(); 97 return; 98 } 99 long eventId; 100 switch (token) { 101 case TOKEN_EVENT: 102 if (cursor.getCount() == 0) { 103 // The cursor is empty. This can happen if the event 104 // was deleted. 105 cursor.close(); 106 mContext.finish(); 107 return; 108 } 109 mOriginalModel = new CalendarEventModel(); 110 EditEventHelper.setModelFromCursor(mOriginalModel, cursor); 111 EditEventHelper.setModelFromCursor(mModel, cursor); 112 cursor.close(); 113 114 mOriginalModel.mUri = mUri; 115 116 mModel.mUri = mUri; 117 mModel.mOriginalStart = mBegin; 118 mModel.mOriginalEnd = mEnd; 119 mModel.mIsFirstEventInSeries = mBegin == mOriginalModel.mStart; 120 mModel.mStart = mBegin; 121 mModel.mEnd = mEnd; 122 123 displayEditWhichDialogue(); 124 125 eventId = mModel.mId; 126 // If there are attendees or alarms query for them 127 // We only query one table at a time so that we can easily 128 // tell if we are finished with all our queries. At a later 129 // point we might want to parallelize this and keep track of 130 // which queries are done. 131 if (mModel.mHasAttendeeData && eventId != -1) { 132 Uri attUri = Attendees.CONTENT_URI; 133 String[] whereArgs = { 134 Long.toString(eventId) 135 }; 136 mHandler.startQuery(TOKEN_ATTENDEES, null, attUri, 137 EditEventHelper.ATTENDEES_PROJECTION, 138 EditEventHelper.ATTENDEES_WHERE /* selection */, 139 whereArgs /* selection args */, null /* sort order */); 140 } else if (mModel.mHasAlarm) { 141 Uri rUri = Reminders.CONTENT_URI; 142 String[] remArgs = { 143 Long.toString(eventId), Integer.toString(Reminders.METHOD_ALERT), 144 Integer.toString(Reminders.METHOD_DEFAULT) 145 }; 146 mHandler 147 .startQuery(TOKEN_REMINDERS, null, rUri, 148 EditEventHelper.REMINDERS_PROJECTION, 149 EditEventHelper.REMINDERS_WHERE /* selection */, 150 remArgs /* selection args */, null /* sort order */); 151 } else { 152 // Set the model if there are no more queries to 153 // make 154 mView.setModel(mModel); 155 } 156 break; 157 case TOKEN_ATTENDEES: 158 try { 159 while (cursor.moveToNext()) { 160 String name = cursor.getString(EditEventHelper.ATTENDEES_INDEX_NAME); 161 String email = cursor.getString(EditEventHelper.ATTENDEES_INDEX_EMAIL); 162 int status = cursor.getInt(EditEventHelper.ATTENDEES_INDEX_STATUS); 163 int relationship = cursor 164 .getInt(EditEventHelper.ATTENDEES_INDEX_RELATIONSHIP); 165 if (email != null) { 166 if (relationship == Attendees.RELATIONSHIP_ORGANIZER) { 167 mModel.mOrganizer = email; 168 } 169 if (mModel.mOwnerAccount != null && 170 mModel.mOwnerAccount.equalsIgnoreCase(email)) { 171 int attendeeId = 172 cursor.getInt(EditEventHelper.ATTENDEES_INDEX_ID); 173 mModel.mOwnerAttendeeId = attendeeId; 174 mModel.mSelfAttendeeStatus = status; 175 mOriginalModel.mOwnerAttendeeId = attendeeId; 176 mOriginalModel.mSelfAttendeeStatus = status; 177 continue; 178 } 179 } 180 Attendee attendee = new Attendee(name, email); 181 attendee.mStatus = status; 182 mModel.addAttendee(attendee); 183 mOriginalModel.addAttendee(attendee); 184 } 185 } finally { 186 cursor.close(); 187 } 188 // This is done after attendees so we know when our 189 // model is filled out 190 eventId = mModel.mId; 191 boolean hasAlarm = mModel.mHasAlarm; 192 if (hasAlarm) { 193 Uri rUri = Reminders.CONTENT_URI; 194 String[] remArgs = { 195 Long.toString(eventId), Integer.toString(Reminders.METHOD_ALERT), 196 Integer.toString(Reminders.METHOD_DEFAULT) 197 }; 198 mHandler 199 .startQuery(TOKEN_REMINDERS, null, rUri, 200 EditEventHelper.REMINDERS_PROJECTION, 201 EditEventHelper.REMINDERS_WHERE /* selection */, 202 remArgs /* selection args */, null /* sort order */); 203 } else { 204 // Set the model if there are no more queries to 205 // make 206 mView.setModel(mModel); 207 } 208 break; 209 case TOKEN_REMINDERS: 210 try { 211 // Add all reminders to the models 212 while (cursor.moveToNext()) { 213 int minutes = cursor.getInt(EditEventHelper.REMINDERS_INDEX_MINUTES); 214 mModel.mReminderMinutes.add(minutes); 215 mOriginalModel.mReminderMinutes.add(minutes); 216 } 217 } finally { 218 cursor.close(); 219 } 220 // Set the model after we finish all the necessary 221 // queries. 222 mView.setModel(mModel); 223 break; 224 case TOKEN_CALENDARS: 225 // startManagingCursor(cursor); 226 MatrixCursor matrixCursor = Utils.matrixCursorFromCursor(cursor); 227 228 // Stop the spinner 229// mContext.getWindow().setFeatureInt(Window.FEATURE_INDETERMINATE_PROGRESS, 230// Window.PROGRESS_VISIBILITY_OFF); 231 232 if (DEBUG) { 233 Log.d(TAG, "onQueryComplete: setting cursor with " 234 + matrixCursor.getCount() + " calendars"); 235 } 236 mView.setCalendarsCursor(matrixCursor); 237 cursor.close(); 238 break; 239 } 240 } 241 } 242 243 public EditEventFragment(boolean fullscreen) { 244 mFullscreen = fullscreen; 245 } 246 247 private void startQuery() { 248 Intent intent = mIntent; 249 mUri = intent.getData(); 250 251 mBegin = intent.getLongExtra(EVENT_BEGIN_TIME, -1); 252 if (mBegin <= 0) { 253 // use a default value instead 254 mBegin = mHelper.constructDefaultStartTime(System.currentTimeMillis()); 255 } 256 mEnd = intent.getLongExtra(EVENT_END_TIME, -1); 257 if (mEnd < mBegin) { 258 // use a default value instead 259 mEnd = mHelper.constructDefaultEndTime(mBegin); 260 } 261 262 // Kick off the query for the event 263 boolean newEvent = mUri == null; 264 if (!newEvent) { 265 if (DEBUG) { 266 Log.d(TAG, "onCreate: uri for event is " + mUri.toString()); 267 } 268 mHandler.startQuery(TOKEN_EVENT, null, mUri, EditEventHelper.EVENT_PROJECTION, 269 null /* selection */, null /* selection args */, null /* sort order */); 270 } else { 271 if (DEBUG) { 272 Log.d(TAG, "onCreate: Editing a new event."); 273 } 274 mModel.mStart = mBegin; 275 mModel.mEnd = mEnd; 276 mModel.mSelfAttendeeStatus = Attendees.ATTENDEE_STATUS_ACCEPTED; 277 mView.setModel(mModel); 278 279 // Start a query in the background to read the list of calendars 280 mHandler.startQuery(TOKEN_CALENDARS, null, Calendars.CONTENT_URI, 281 EditEventHelper.CALENDARS_PROJECTION, 282 EditEventHelper.CALENDARS_WHERE_WRITEABLE_VISIBLE, null /* selection args */, 283 null /* sort order */); 284 } 285 } 286 287 @Override 288 public void onAttach(Activity activity) { 289 super.onAttach(activity); 290 mContext = activity; 291 mIntent = activity.getIntent(); 292 293 mHelper = new EditEventHelper((AbstractCalendarActivity) activity, null); 294 mHandler = new QueryHandler(activity.getContentResolver()); 295 if (mIntent != null) { 296 mModel = new CalendarEventModel(activity, mIntent); 297 } else { 298 mModel = new CalendarEventModel(activity); 299 } 300 } 301 302 @Override 303 public View onCreateView(LayoutInflater inflater, ViewGroup container, 304 Bundle savedInstanceState) { 305// mContext.requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); 306 View view = inflater.inflate(R.layout.edit_event, null); 307 if (!mFullscreen) { 308 LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(280, 250); 309 params.leftMargin = 100; 310 params.topMargin = 150; 311 params.setMargins(50, 100, 50, 50); 312 view.setLayoutParams(params); 313 } 314 mView = new EditEventView(mContext, view, new Done()); 315 startQuery(); 316 return view; 317 } 318 319 @Override 320 public void onCreate(Bundle savedInstanceState) { 321 super.onCreate(savedInstanceState); 322 323 } 324// 325// @Override 326// public boolean onCreateOptionsMenu(Menu menu) { 327// MenuItem item; 328// item = menu.add(MENU_GROUP_ADD_REMINDER, MENU_ADD_REMINDER, 0, R.string.add_new_reminder); 329// item.setIcon(R.drawable.ic_menu_reminder); 330// item.setAlphabeticShortcut('r'); 331// 332// return super.onCreateOptionsMenu(menu); 333// } 334// 335// @Override 336// public boolean onPrepareOptionsMenu(Menu menu) { 337// int numReminders = mView.getReminderCount(); 338// if (numReminders < EditEventHelper.MAX_REMINDERS) { 339// menu.setGroupEnabled(MENU_GROUP_ADD_REMINDER, true); 340// menu.setGroupVisible(MENU_GROUP_ADD_REMINDER, true); 341// } else { 342// menu.setGroupEnabled(MENU_GROUP_ADD_REMINDER, false); 343// menu.setGroupVisible(MENU_GROUP_ADD_REMINDER, false); 344// } 345// 346// return super.onPrepareOptionsMenu(menu); 347// } 348// 349// @Override 350// public boolean onOptionsItemSelected(MenuItem item) { 351// switch (item.getItemId()) { 352// case MENU_ADD_REMINDER: 353// mView.addReminder(); 354// return true; 355// } 356// return super.onOptionsItemSelected(item); 357// } 358// 359 protected void displayEditWhichDialogue() { 360 if (!TextUtils.isEmpty(mModel.mRrule) && mModification == Utils.MODIFY_UNINITIALIZED) { 361 // If this event has not been synced, then don't allow deleting 362 // or changing a single instance. 363 String mSyncId = mModel.mSyncId; 364 boolean isFirstEventInSeries = mModel.mIsFirstEventInSeries; 365 366 // If we haven't synced this repeating event yet, then don't 367 // allow the user to change just one instance. 368 int itemIndex = 0; 369 CharSequence[] items; 370 if (mSyncId == null) { 371 if (isFirstEventInSeries) { 372 // Still display the option so the user knows all events are 373 // changing 374 items = new CharSequence[1]; 375 } else { 376 items = new CharSequence[2]; 377 } 378 } else { 379 if (isFirstEventInSeries) { 380 items = new CharSequence[2]; 381 } else { 382 items = new CharSequence[3]; 383 } 384 items[itemIndex++] = mContext.getText(R.string.modify_event); 385 } 386 items[itemIndex++] = mContext.getText(R.string.modify_all); 387 388 // Do one more check to make sure this remains at the end of the list 389 if (!isFirstEventInSeries) { 390 items[itemIndex++] = mContext.getText(R.string.modify_all_following); 391 } 392 393 // Display the modification dialog. 394 if (mModifyDialog != null) { 395 mModifyDialog.dismiss(); 396 mModifyDialog = null; 397 } 398 mModifyDialog = new AlertDialog.Builder(mContext).setOnCancelListener( 399 new OnCancelListener() { 400 public void onCancel(DialogInterface dialog) { 401 mContext.finish(); 402 } 403 }).setTitle(R.string.edit_event_label).setItems(items, new OnClickListener() { 404 public void onClick(DialogInterface dialog, int which) { 405 if (which == 0) { 406 mModification = (mModel.mSyncId == null) ? Utils.MODIFY_ALL 407 : Utils.MODIFY_SELECTED; 408 } else if (which == 1) { 409 mModification = (mModel.mSyncId == null) ? Utils.MODIFY_ALL_FOLLOWING 410 : Utils.MODIFY_ALL; 411 } else if (which == 2) { 412 mModification = Utils.MODIFY_ALL_FOLLOWING; 413 } 414 415 mView.setModification(mModification); 416 } 417 }).show(); 418 } 419 } 420 421 class Done implements EditEventHelper.EditDoneRunnable { 422 private int mCode = -1; 423 424 public void setDoneCode(int code) { 425 mCode = code; 426 } 427 428 public void run() { 429 switch (mCode) { 430 case Utils.DONE_REVERT: 431 mContext.finish(); 432 break; 433 case Utils.DONE_SAVE: 434 if (mModel != null && !mModel.equals(mOriginalModel)) { 435 if (mHelper.saveEvent(mModel, mOriginalModel, mModification)) { 436 if (mModel.mUri != null) { 437 Toast.makeText(mContext, R.string.saving_event, Toast.LENGTH_SHORT) 438 .show(); 439 } else { 440 Toast.makeText(mContext, R.string.creating_event, 441 Toast.LENGTH_SHORT).show(); 442 } 443 } 444 } 445 mContext.finish(); 446 break; 447 case Utils.DONE_DELETE: 448 long begin = mModel.mStart; 449 long end = mModel.mEnd; 450 int which = -1; 451 switch (mModification) { 452 case Utils.MODIFY_SELECTED: 453 which = DeleteEventHelper.DELETE_SELECTED; 454 break; 455 case Utils.MODIFY_ALL_FOLLOWING: 456 which = DeleteEventHelper.DELETE_ALL_FOLLOWING; 457 break; 458 case Utils.MODIFY_ALL: 459 which = DeleteEventHelper.DELETE_ALL; 460 break; 461 } 462 DeleteEventHelper deleteHelper = new DeleteEventHelper(mContext, mContext, 463 true /* exitWhenDone */); 464 // TODO update delete helper to use the model instead of the cursor 465 deleteHelper.delete(begin, end, mModel, which); 466 break; 467 default: 468 Log.e(TAG, "done: Unrecognized exit code."); 469 mContext.finish(); 470 break; 471 } 472 } 473 } 474 475 @Override 476 public void onDestroy() { 477 super.onDestroy(); 478 479 if (mModifyDialog != null) { 480 mModifyDialog.dismiss(); 481 mModifyDialog = null; 482 } 483 } 484} 485