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