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