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 android.app.Activity; 20import android.app.AlertDialog; 21import android.app.Fragment; 22import android.content.AsyncQueryHandler; 23import android.content.ContentProviderOperation; 24import android.content.ContentResolver; 25import android.content.ContentUris; 26import android.content.ContentValues; 27import android.content.Context; 28import android.content.DialogInterface; 29import android.content.DialogInterface.OnCancelListener; 30import android.content.DialogInterface.OnClickListener; 31import android.content.Intent; 32import android.database.Cursor; 33import android.database.MatrixCursor; 34import android.net.Uri; 35import android.os.Bundle; 36import android.provider.CalendarContract.Attendees; 37import android.provider.CalendarContract.Calendars; 38import android.provider.CalendarContract.Events; 39import android.provider.CalendarContract.Reminders; 40import android.text.TextUtils; 41import android.text.format.Time; 42import android.util.Log; 43import android.view.LayoutInflater; 44import android.view.Menu; 45import android.view.MenuInflater; 46import android.view.MenuItem; 47import android.view.View; 48import android.view.ViewGroup; 49import android.view.inputmethod.InputMethodManager; 50import android.widget.LinearLayout; 51import android.widget.Toast; 52 53import com.android.calendar.AsyncQueryService; 54import com.android.calendar.CalendarController; 55import com.android.calendar.CalendarController.EventHandler; 56import com.android.calendar.CalendarController.EventInfo; 57import com.android.calendar.CalendarController.EventType; 58import com.android.calendar.CalendarEventModel; 59import com.android.calendar.CalendarEventModel.Attendee; 60import com.android.calendar.CalendarEventModel.ReminderEntry; 61import com.android.calendar.DeleteEventHelper; 62import com.android.calendar.R; 63import com.android.calendar.Utils; 64 65import java.io.Serializable; 66import java.util.ArrayList; 67import java.util.Collections; 68 69public class EditEventFragment extends Fragment implements EventHandler { 70 private static final String TAG = "EditEventActivity"; 71 72 private static final String BUNDLE_KEY_MODEL = "key_model"; 73 private static final String BUNDLE_KEY_EDIT_STATE = "key_edit_state"; 74 private static final String BUNDLE_KEY_EVENT = "key_event"; 75 private static final String BUNDLE_KEY_READ_ONLY = "key_read_only"; 76 private static final String BUNDLE_KEY_EDIT_ON_LAUNCH = "key_edit_on_launch"; 77 78 private static final boolean DEBUG = false; 79 80 private static final int TOKEN_EVENT = 1; 81 private static final int TOKEN_ATTENDEES = 1 << 1; 82 private static final int TOKEN_REMINDERS = 1 << 2; 83 private static final int TOKEN_CALENDARS = 1 << 3; 84 private static final int TOKEN_ALL = TOKEN_EVENT | TOKEN_ATTENDEES | TOKEN_REMINDERS 85 | TOKEN_CALENDARS; 86 private static final int TOKEN_UNITIALIZED = 1 << 31; 87 88 /** 89 * A bitfield of TOKEN_* to keep track which query hasn't been completed 90 * yet. Once all queries have returned, the model can be applied to the 91 * view. 92 */ 93 private int mOutstandingQueries = TOKEN_UNITIALIZED; 94 95 EditEventHelper mHelper; 96 CalendarEventModel mModel; 97 CalendarEventModel mOriginalModel; 98 CalendarEventModel mRestoreModel; 99 EditEventView mView; 100 QueryHandler mHandler; 101 102 private AlertDialog mModifyDialog; 103 int mModification = Utils.MODIFY_UNINITIALIZED; 104 105 private final EventInfo mEvent; 106 private EventBundle mEventBundle; 107 private Uri mUri; 108 private long mBegin; 109 private long mEnd; 110 111 private Activity mContext; 112 private final Done mOnDone = new Done(); 113 114 private boolean mSaveOnDetach = true; 115 private boolean mIsReadOnly = false; 116 public boolean mShowModifyDialogOnLaunch = false; 117 118 private InputMethodManager mInputMethodManager; 119 120 private final Intent mIntent; 121 122 private boolean mUseCustomActionBar; 123 124 private final View.OnClickListener mActionBarListener = new View.OnClickListener() { 125 @Override 126 public void onClick(View v) { 127 onActionBarItemSelected(v.getId()); 128 } 129 }; 130 131 // TODO turn this into a helper function in EditEventHelper for building the 132 // model 133 private class QueryHandler extends AsyncQueryHandler { 134 public QueryHandler(ContentResolver cr) { 135 super(cr); 136 } 137 138 @Override 139 protected void onQueryComplete(int token, Object cookie, Cursor cursor) { 140 // If the query didn't return a cursor for some reason return 141 if (cursor == null) { 142 return; 143 } 144 145 // If the Activity is finishing, then close the cursor. 146 // Otherwise, use the new cursor in the adapter. 147 final Activity activity = EditEventFragment.this.getActivity(); 148 if (activity == null || activity.isFinishing()) { 149 cursor.close(); 150 return; 151 } 152 long eventId; 153 switch (token) { 154 case TOKEN_EVENT: 155 if (cursor.getCount() == 0) { 156 // The cursor is empty. This can happen if the event 157 // was deleted. 158 cursor.close(); 159 mOnDone.setDoneCode(Utils.DONE_EXIT); 160 mSaveOnDetach = false; 161 mOnDone.run(); 162 return; 163 } 164 mOriginalModel = new CalendarEventModel(); 165 EditEventHelper.setModelFromCursor(mOriginalModel, cursor); 166 EditEventHelper.setModelFromCursor(mModel, cursor); 167 cursor.close(); 168 169 mOriginalModel.mUri = mUri.toString(); 170 171 mModel.mUri = mUri.toString(); 172 mModel.mOriginalStart = mBegin; 173 mModel.mOriginalEnd = mEnd; 174 mModel.mIsFirstEventInSeries = mBegin == mOriginalModel.mStart; 175 mModel.mStart = mBegin; 176 mModel.mEnd = mEnd; 177 178 eventId = mModel.mId; 179 180 // TOKEN_ATTENDEES 181 if (mModel.mHasAttendeeData && eventId != -1) { 182 Uri attUri = Attendees.CONTENT_URI; 183 String[] whereArgs = { 184 Long.toString(eventId) 185 }; 186 mHandler.startQuery(TOKEN_ATTENDEES, null, attUri, 187 EditEventHelper.ATTENDEES_PROJECTION, 188 EditEventHelper.ATTENDEES_WHERE /* selection */, 189 whereArgs /* selection args */, null /* sort order */); 190 } else { 191 setModelIfDone(TOKEN_ATTENDEES); 192 } 193 194 // TOKEN_REMINDERS 195 if (mModel.mHasAlarm) { 196 Uri rUri = Reminders.CONTENT_URI; 197 String[] remArgs = { 198 Long.toString(eventId) 199 }; 200 mHandler.startQuery(TOKEN_REMINDERS, null, rUri, 201 EditEventHelper.REMINDERS_PROJECTION, 202 EditEventHelper.REMINDERS_WHERE /* selection */, 203 remArgs /* selection args */, null /* sort order */); 204 } else { 205 setModelIfDone(TOKEN_REMINDERS); 206 } 207 208 // TOKEN_CALENDARS 209 String[] selArgs = { 210 Long.toString(mModel.mCalendarId) 211 }; 212 mHandler.startQuery(TOKEN_CALENDARS, null, Calendars.CONTENT_URI, 213 EditEventHelper.CALENDARS_PROJECTION, EditEventHelper.CALENDARS_WHERE, 214 selArgs /* selection args */, null /* sort order */); 215 216 setModelIfDone(TOKEN_EVENT); 217 break; 218 case TOKEN_ATTENDEES: 219 try { 220 while (cursor.moveToNext()) { 221 String name = cursor.getString(EditEventHelper.ATTENDEES_INDEX_NAME); 222 String email = cursor.getString(EditEventHelper.ATTENDEES_INDEX_EMAIL); 223 int status = cursor.getInt(EditEventHelper.ATTENDEES_INDEX_STATUS); 224 int relationship = cursor 225 .getInt(EditEventHelper.ATTENDEES_INDEX_RELATIONSHIP); 226 if (relationship == Attendees.RELATIONSHIP_ORGANIZER) { 227 if (email != null) { 228 mModel.mOrganizer = email; 229 mModel.mIsOrganizer = mModel.mOwnerAccount 230 .equalsIgnoreCase(email); 231 mOriginalModel.mOrganizer = email; 232 mOriginalModel.mIsOrganizer = mOriginalModel.mOwnerAccount 233 .equalsIgnoreCase(email); 234 } 235 236 if (TextUtils.isEmpty(name)) { 237 mModel.mOrganizerDisplayName = mModel.mOrganizer; 238 mOriginalModel.mOrganizerDisplayName = 239 mOriginalModel.mOrganizer; 240 } else { 241 mModel.mOrganizerDisplayName = name; 242 mOriginalModel.mOrganizerDisplayName = name; 243 } 244 } 245 246 if (email != null) { 247 if (mModel.mOwnerAccount != null && 248 mModel.mOwnerAccount.equalsIgnoreCase(email)) { 249 int attendeeId = 250 cursor.getInt(EditEventHelper.ATTENDEES_INDEX_ID); 251 mModel.mOwnerAttendeeId = attendeeId; 252 mModel.mSelfAttendeeStatus = status; 253 mOriginalModel.mOwnerAttendeeId = attendeeId; 254 mOriginalModel.mSelfAttendeeStatus = status; 255 continue; 256 } 257 } 258 Attendee attendee = new Attendee(name, email); 259 attendee.mStatus = status; 260 mModel.addAttendee(attendee); 261 mOriginalModel.addAttendee(attendee); 262 } 263 } finally { 264 cursor.close(); 265 } 266 267 setModelIfDone(TOKEN_ATTENDEES); 268 break; 269 case TOKEN_REMINDERS: 270 try { 271 // Add all reminders to the models 272 while (cursor.moveToNext()) { 273 int minutes = cursor.getInt(EditEventHelper.REMINDERS_INDEX_MINUTES); 274 int method = cursor.getInt(EditEventHelper.REMINDERS_INDEX_METHOD); 275 ReminderEntry re = ReminderEntry.valueOf(minutes, method); 276 mModel.mReminders.add(re); 277 mOriginalModel.mReminders.add(re); 278 } 279 280 // Sort appropriately for display 281 Collections.sort(mModel.mReminders); 282 Collections.sort(mOriginalModel.mReminders); 283 } finally { 284 cursor.close(); 285 } 286 287 setModelIfDone(TOKEN_REMINDERS); 288 break; 289 case TOKEN_CALENDARS: 290 try { 291 if (mModel.mCalendarId == -1) { 292 // Populate Calendar spinner only if no calendar is set e.g. new event 293 MatrixCursor matrixCursor = Utils.matrixCursorFromCursor(cursor); 294 if (DEBUG) { 295 Log.d(TAG, "onQueryComplete: setting cursor with " 296 + matrixCursor.getCount() + " calendars"); 297 } 298 mView.setCalendarsCursor(matrixCursor, isAdded() && isResumed()); 299 } else { 300 // Populate model for an existing event 301 EditEventHelper.setModelFromCalendarCursor(mModel, cursor); 302 EditEventHelper.setModelFromCalendarCursor(mOriginalModel, cursor); 303 } 304 } finally { 305 cursor.close(); 306 } 307 308 setModelIfDone(TOKEN_CALENDARS); 309 break; 310 default: 311 cursor.close(); 312 break; 313 } 314 } 315 } 316 317 private void setModelIfDone(int queryType) { 318 synchronized (this) { 319 mOutstandingQueries &= ~queryType; 320 if (mOutstandingQueries == 0) { 321 if (mRestoreModel != null) { 322 mModel = mRestoreModel; 323 } 324 if (mShowModifyDialogOnLaunch && mModification == Utils.MODIFY_UNINITIALIZED) { 325 if (!TextUtils.isEmpty(mModel.mRrule)) { 326 displayEditWhichDialog(); 327 } else { 328 mModification = Utils.MODIFY_ALL; 329 } 330 331 } 332 mView.setModel(mModel); 333 mView.setModification(mModification); 334 } 335 } 336 } 337 338 public EditEventFragment() { 339 this(null, false, null); 340 } 341 342 public EditEventFragment(EventInfo event, boolean readOnly, Intent intent) { 343 mEvent = event; 344 mIsReadOnly = readOnly; 345 mIntent = intent; 346 setHasOptionsMenu(true); 347 } 348 349 private void startQuery() { 350 mUri = null; 351 mBegin = -1; 352 mEnd = -1; 353 if (mEvent != null) { 354 if (mEvent.id != -1) { 355 mModel.mId = mEvent.id; 356 mUri = ContentUris.withAppendedId(Events.CONTENT_URI, mEvent.id); 357 } else { 358 // New event. All day? 359 mModel.mAllDay = mEvent.extraLong == CalendarController.EXTRA_CREATE_ALL_DAY; 360 } 361 if (mEvent.startTime != null) { 362 mBegin = mEvent.startTime.toMillis(true); 363 } 364 if (mEvent.endTime != null) { 365 mEnd = mEvent.endTime.toMillis(true); 366 } 367 } else if (mEventBundle != null) { 368 if (mEventBundle.id != -1) { 369 mModel.mId = mEventBundle.id; 370 mUri = ContentUris.withAppendedId(Events.CONTENT_URI, mEventBundle.id); 371 } 372 mBegin = mEventBundle.start; 373 mEnd = mEventBundle.end; 374 } 375 376 if (mBegin <= 0) { 377 // use a default value instead 378 mBegin = mHelper.constructDefaultStartTime(System.currentTimeMillis()); 379 } 380 if (mEnd < mBegin) { 381 // use a default value instead 382 mEnd = mHelper.constructDefaultEndTime(mBegin); 383 } 384 385 // Kick off the query for the event 386 boolean newEvent = mUri == null; 387 if (!newEvent) { 388 mModel.mCalendarAccessLevel = Calendars.CAL_ACCESS_NONE; 389 mOutstandingQueries = TOKEN_ALL; 390 if (DEBUG) { 391 Log.d(TAG, "startQuery: uri for event is " + mUri.toString()); 392 } 393 mHandler.startQuery(TOKEN_EVENT, null, mUri, EditEventHelper.EVENT_PROJECTION, 394 null /* selection */, null /* selection args */, null /* sort order */); 395 } else { 396 mOutstandingQueries = TOKEN_CALENDARS; 397 if (DEBUG) { 398 Log.d(TAG, "startQuery: Editing a new event."); 399 } 400 mModel.mStart = mBegin; 401 mModel.mEnd = mEnd; 402 mModel.mSelfAttendeeStatus = Attendees.ATTENDEE_STATUS_ACCEPTED; 403 404 // Start a query in the background to read the list of calendars 405 mHandler.startQuery(TOKEN_CALENDARS, null, Calendars.CONTENT_URI, 406 EditEventHelper.CALENDARS_PROJECTION, 407 EditEventHelper.CALENDARS_WHERE_WRITEABLE_VISIBLE, null /* selection args */, 408 null /* sort order */); 409 410 mModification = Utils.MODIFY_ALL; 411 mView.setModification(mModification); 412 } 413 } 414 415 @Override 416 public void onAttach(Activity activity) { 417 super.onAttach(activity); 418 mContext = activity; 419 420 mHelper = new EditEventHelper(activity, null); 421 mHandler = new QueryHandler(activity.getContentResolver()); 422 mModel = new CalendarEventModel(activity, mIntent); 423 mInputMethodManager = (InputMethodManager) 424 activity.getSystemService(Context.INPUT_METHOD_SERVICE); 425 426 mUseCustomActionBar = !Utils.getConfigBool(mContext, R.bool.multiple_pane_config); 427 } 428 429 @Override 430 public View onCreateView(LayoutInflater inflater, ViewGroup container, 431 Bundle savedInstanceState) { 432// mContext.requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); 433 View view; 434 if (mIsReadOnly) { 435 view = inflater.inflate(R.layout.edit_event_single_column, null); 436 } else { 437 view = inflater.inflate(R.layout.edit_event, null); 438 } 439 mView = new EditEventView(mContext, view, mOnDone); 440 startQuery(); 441 442 if (mUseCustomActionBar) { 443 View actionBarButtons = inflater.inflate(R.layout.edit_event_custom_actionbar, 444 new LinearLayout(mContext), false); 445 View cancelActionView = actionBarButtons.findViewById(R.id.action_cancel); 446 cancelActionView.setOnClickListener(mActionBarListener); 447 View doneActionView = actionBarButtons.findViewById(R.id.action_done); 448 doneActionView.setOnClickListener(mActionBarListener); 449 450 mContext.getActionBar().setCustomView(actionBarButtons); 451 } 452 453 return view; 454 } 455 456 @Override 457 public void onDestroyView() { 458 super.onDestroyView(); 459 460 if (mUseCustomActionBar) { 461 mContext.getActionBar().setCustomView(null); 462 } 463 } 464 465 @Override 466 public void onCreate(Bundle savedInstanceState) { 467 super.onCreate(savedInstanceState); 468 if (savedInstanceState != null) { 469 if (savedInstanceState.containsKey(BUNDLE_KEY_MODEL)) { 470 mRestoreModel = (CalendarEventModel) savedInstanceState.getSerializable( 471 BUNDLE_KEY_MODEL); 472 } 473 if (savedInstanceState.containsKey(BUNDLE_KEY_EDIT_STATE)) { 474 mModification = savedInstanceState.getInt(BUNDLE_KEY_EDIT_STATE); 475 } 476 if (savedInstanceState.containsKey(BUNDLE_KEY_EDIT_ON_LAUNCH)) { 477 mShowModifyDialogOnLaunch = savedInstanceState 478 .getBoolean(BUNDLE_KEY_EDIT_ON_LAUNCH); 479 } 480 if (savedInstanceState.containsKey(BUNDLE_KEY_EVENT)) { 481 mEventBundle = (EventBundle) savedInstanceState.getSerializable(BUNDLE_KEY_EVENT); 482 } 483 if (savedInstanceState.containsKey(BUNDLE_KEY_READ_ONLY)) { 484 mIsReadOnly = savedInstanceState.getBoolean(BUNDLE_KEY_READ_ONLY); 485 } 486 } 487 } 488 489 490 @Override 491 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 492 super.onCreateOptionsMenu(menu, inflater); 493 494 if (!mUseCustomActionBar) { 495 inflater.inflate(R.menu.edit_event_title_bar, menu); 496 } 497 } 498 499 @Override 500 public boolean onOptionsItemSelected(MenuItem item) { 501 return onActionBarItemSelected(item.getItemId()); 502 } 503 504 /** 505 * Handles menu item selections, whether they come from our custom action bar buttons or from 506 * the standard menu items. Depends on the menu item ids matching the custom action bar button 507 * ids. 508 * 509 * @param itemId the button or menu item id 510 * @return whether the event was handled here 511 */ 512 private boolean onActionBarItemSelected(int itemId) { 513 if (itemId == R.id.action_done) { 514 if (EditEventHelper.canModifyEvent(mModel) || EditEventHelper.canRespond(mModel)) { 515 if (mView != null && mView.prepareForSave()) { 516 if (mModification == Utils.MODIFY_UNINITIALIZED) { 517 mModification = Utils.MODIFY_ALL; 518 } 519 mOnDone.setDoneCode(Utils.DONE_SAVE | Utils.DONE_EXIT); 520 mOnDone.run(); 521 } else { 522 mOnDone.setDoneCode(Utils.DONE_REVERT); 523 mOnDone.run(); 524 } 525 } else if (EditEventHelper.canAddReminders(mModel) && mModel.mId != -1 526 && mOriginalModel != null && mView.prepareForSave()) { 527 saveReminders(); 528 mOnDone.setDoneCode(Utils.DONE_EXIT); 529 mOnDone.run(); 530 } else { 531 mOnDone.setDoneCode(Utils.DONE_REVERT); 532 mOnDone.run(); 533 } 534 } else if (itemId == R.id.action_cancel) { 535 mOnDone.setDoneCode(Utils.DONE_REVERT); 536 mOnDone.run(); 537 } 538 return true; 539 } 540 541 private void saveReminders() { 542 ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(3); 543 boolean changed = EditEventHelper.saveReminders(ops, mModel.mId, mModel.mReminders, 544 mOriginalModel.mReminders, false /* no force save */); 545 546 if (!changed) { 547 return; 548 } 549 550 AsyncQueryService service = new AsyncQueryService(getActivity()); 551 service.startBatch(0, null, Calendars.CONTENT_URI.getAuthority(), ops, 0); 552 // Update the "hasAlarm" field for the event 553 Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, mModel.mId); 554 int len = mModel.mReminders.size(); 555 boolean hasAlarm = len > 0; 556 if (hasAlarm != mOriginalModel.mHasAlarm) { 557 ContentValues values = new ContentValues(); 558 values.put(Events.HAS_ALARM, hasAlarm ? 1 : 0); 559 service.startUpdate(0, null, uri, values, null, null, 0); 560 } 561 562 Toast.makeText(mContext, R.string.saving_event, Toast.LENGTH_SHORT).show(); 563 } 564 565 protected void displayEditWhichDialog() { 566 if (mModification == Utils.MODIFY_UNINITIALIZED) { 567 final boolean notSynced = TextUtils.isEmpty(mModel.mSyncId); 568 boolean isFirstEventInSeries = mModel.mIsFirstEventInSeries; 569 int itemIndex = 0; 570 CharSequence[] items; 571 572 if (notSynced) { 573 // If this event has not been synced, then don't allow deleting 574 // or changing a single instance. 575 if (isFirstEventInSeries) { 576 // Still display the option so the user knows all events are 577 // changing 578 items = new CharSequence[1]; 579 } else { 580 items = new CharSequence[2]; 581 } 582 } else { 583 if (isFirstEventInSeries) { 584 items = new CharSequence[2]; 585 } else { 586 items = new CharSequence[3]; 587 } 588 items[itemIndex++] = mContext.getText(R.string.modify_event); 589 } 590 items[itemIndex++] = mContext.getText(R.string.modify_all); 591 592 // Do one more check to make sure this remains at the end of the list 593 if (!isFirstEventInSeries) { 594 items[itemIndex++] = mContext.getText(R.string.modify_all_following); 595 } 596 597 // Display the modification dialog. 598 if (mModifyDialog != null) { 599 mModifyDialog.dismiss(); 600 mModifyDialog = null; 601 } 602 mModifyDialog = new AlertDialog.Builder(mContext).setTitle(R.string.edit_event_label) 603 .setItems(items, new OnClickListener() { 604 public void onClick(DialogInterface dialog, int which) { 605 if (which == 0) { 606 // Update this if we start allowing exceptions 607 // to unsynced events in the app 608 mModification = notSynced ? Utils.MODIFY_ALL 609 : Utils.MODIFY_SELECTED; 610 if (mModification == Utils.MODIFY_SELECTED) { 611 mModel.mOriginalSyncId = notSynced ? null : mModel.mSyncId; 612 mModel.mOriginalId = mModel.mId; 613 } 614 } else if (which == 1) { 615 mModification = notSynced ? Utils.MODIFY_ALL_FOLLOWING 616 : Utils.MODIFY_ALL; 617 } else if (which == 2) { 618 mModification = Utils.MODIFY_ALL_FOLLOWING; 619 } 620 621 mView.setModification(mModification); 622 } 623 }).show(); 624 625 mModifyDialog.setOnCancelListener(new OnCancelListener() { 626 @Override 627 public void onCancel(DialogInterface dialog) { 628 Activity a = EditEventFragment.this.getActivity(); 629 if (a != null) { 630 a.finish(); 631 } 632 } 633 }); 634 } 635 } 636 637 class Done implements EditEventHelper.EditDoneRunnable { 638 private int mCode = -1; 639 640 public void setDoneCode(int code) { 641 mCode = code; 642 } 643 644 public void run() { 645 // We only want this to get called once, either because the user 646 // pressed back/home or one of the buttons on screen 647 mSaveOnDetach = false; 648 if (mModification == Utils.MODIFY_UNINITIALIZED) { 649 // If this is uninitialized the user hit back, the only 650 // changeable item is response to default to all events. 651 mModification = Utils.MODIFY_ALL; 652 } 653 654 if ((mCode & Utils.DONE_SAVE) != 0 && mModel != null 655 && (EditEventHelper.canRespond(mModel) 656 || EditEventHelper.canModifyEvent(mModel)) 657 && mView.prepareForSave() 658 && !isEmptyNewEvent() 659 && mModel.normalizeReminders() 660 && mHelper.saveEvent(mModel, mOriginalModel, mModification)) { 661 int stringResource; 662 if (!mModel.mAttendeesList.isEmpty()) { 663 if (mModel.mUri != null) { 664 stringResource = R.string.saving_event_with_guest; 665 } else { 666 stringResource = R.string.creating_event_with_guest; 667 } 668 } else { 669 if (mModel.mUri != null) { 670 stringResource = R.string.saving_event; 671 } else { 672 stringResource = R.string.creating_event; 673 } 674 } 675 Toast.makeText(mContext, stringResource, Toast.LENGTH_SHORT).show(); 676 } else if ((mCode & Utils.DONE_SAVE) != 0 && mModel != null && isEmptyNewEvent()) { 677 Toast.makeText(mContext, R.string.empty_event, Toast.LENGTH_SHORT).show(); 678 } 679 680 if ((mCode & Utils.DONE_DELETE) != 0 && mOriginalModel != null 681 && EditEventHelper.canModifyCalendar(mOriginalModel)) { 682 long begin = mModel.mStart; 683 long end = mModel.mEnd; 684 int which = -1; 685 switch (mModification) { 686 case Utils.MODIFY_SELECTED: 687 which = DeleteEventHelper.DELETE_SELECTED; 688 break; 689 case Utils.MODIFY_ALL_FOLLOWING: 690 which = DeleteEventHelper.DELETE_ALL_FOLLOWING; 691 break; 692 case Utils.MODIFY_ALL: 693 which = DeleteEventHelper.DELETE_ALL; 694 break; 695 } 696 DeleteEventHelper deleteHelper = new DeleteEventHelper( 697 mContext, mContext, !mIsReadOnly /* exitWhenDone */); 698 deleteHelper.delete(begin, end, mOriginalModel, which); 699 } 700 701 if ((mCode & Utils.DONE_EXIT) != 0) { 702 // This will exit the edit event screen, should be called 703 // when we want to return to the main calendar views 704 if ((mCode & Utils.DONE_SAVE) != 0) { 705 if (mContext != null) { 706 long start = mModel.mStart; 707 long end = mModel.mEnd; 708 if (mModel.mAllDay) { 709 // For allday events we want to go to the day in the 710 // user's current tz 711 String tz = Utils.getTimeZone(mContext, null); 712 Time t = new Time(Time.TIMEZONE_UTC); 713 t.set(start); 714 t.timezone = tz; 715 start = t.toMillis(true); 716 717 t.timezone = Time.TIMEZONE_UTC; 718 t.set(end); 719 t.timezone = tz; 720 end = t.toMillis(true); 721 } 722 CalendarController.getInstance(mContext).launchViewEvent(-1, start, end, 723 Attendees.ATTENDEE_STATUS_NONE); 724 } 725 } 726 Activity a = EditEventFragment.this.getActivity(); 727 if (a != null) { 728 a.finish(); 729 } 730 } 731 732 // Hide a software keyboard so that user won't see it even after this Fragment's 733 // disappearing. 734 final View focusedView = mContext.getCurrentFocus(); 735 if (focusedView != null) { 736 mInputMethodManager.hideSoftInputFromWindow(focusedView.getWindowToken(), 0); 737 focusedView.clearFocus(); 738 } 739 } 740 } 741 742 boolean isEmptyNewEvent() { 743 if (mOriginalModel != null) { 744 // Not new 745 return false; 746 } 747 748 return isEmpty(); 749 } 750 751 private boolean isEmpty() { 752 if (mModel.mTitle != null) { 753 String title = mModel.mTitle.trim(); 754 if (title.length() > 0) { 755 return false; 756 } 757 } 758 759 if (mModel.mLocation != null) { 760 String location = mModel.mLocation.trim(); 761 if (location.length() > 0) { 762 return false; 763 } 764 } 765 766 if (mModel.mDescription != null) { 767 String description = mModel.mDescription.trim(); 768 if (description.length() > 0) { 769 return false; 770 } 771 } 772 773 return true; 774 } 775 776 @Override 777 public void onPause() { 778 Activity act = getActivity(); 779 if (mSaveOnDetach && act != null && !mIsReadOnly && !act.isChangingConfigurations() 780 && mView.prepareForSave()) { 781 mOnDone.setDoneCode(Utils.DONE_SAVE); 782 mOnDone.run(); 783 } 784 super.onPause(); 785 } 786 787 @Override 788 public void onDestroy() { 789 if (mView != null) { 790 mView.setModel(null); 791 } 792 if (mModifyDialog != null) { 793 mModifyDialog.dismiss(); 794 mModifyDialog = null; 795 } 796 super.onDestroy(); 797 } 798 799 @Override 800 public void eventsChanged() { 801 // TODO Requery to see if event has changed 802 } 803 804 @Override 805 public void onSaveInstanceState(Bundle outState) { 806 mView.prepareForSave(); 807 outState.putSerializable(BUNDLE_KEY_MODEL, mModel); 808 outState.putInt(BUNDLE_KEY_EDIT_STATE, mModification); 809 if (mEventBundle == null && mEvent != null) { 810 mEventBundle = new EventBundle(); 811 mEventBundle.id = mEvent.id; 812 if (mEvent.startTime != null) { 813 mEventBundle.start = mEvent.startTime.toMillis(true); 814 } 815 if (mEvent.endTime != null) { 816 mEventBundle.end = mEvent.startTime.toMillis(true); 817 } 818 } 819 outState.putBoolean(BUNDLE_KEY_EDIT_ON_LAUNCH, mShowModifyDialogOnLaunch); 820 outState.putSerializable(BUNDLE_KEY_EVENT, mEventBundle); 821 outState.putBoolean(BUNDLE_KEY_READ_ONLY, mIsReadOnly); 822 } 823 824 @Override 825 public long getSupportedEventTypes() { 826 return EventType.USER_HOME; 827 } 828 829 @Override 830 public void handleEvent(EventInfo event) { 831 // It's currently unclear if we want to save the event or not when home 832 // is pressed. When creating a new event we shouldn't save since we 833 // can't get the id of the new event easily. 834 if ((false && event.eventType == EventType.USER_HOME) || (event.eventType == EventType.GO_TO 835 && mSaveOnDetach)) { 836 if (mView != null && mView.prepareForSave()) { 837 mOnDone.setDoneCode(Utils.DONE_SAVE); 838 mOnDone.run(); 839 } 840 } 841 } 842 843 private static class EventBundle implements Serializable { 844 private static final long serialVersionUID = 1L; 845 long id = -1; 846 long start = -1; 847 long end = -1; 848 } 849} 850