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