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