EditEventView.java revision f39a39d640eb6fbb0a91ee9cecfaf07fe8d373b8
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 com.android.calendar.CalendarEventModel;
20import com.android.calendar.CalendarEventModel.Attendee;
21import com.android.calendar.CalendarEventModel.ReminderEntry;
22import com.android.calendar.EmailAddressAdapter;
23import com.android.calendar.EventInfoFragment;
24import com.android.calendar.GeneralPreferences;
25import com.android.calendar.R;
26import com.android.calendar.RecipientAdapter;
27import com.android.calendar.TimezoneAdapter;
28import com.android.calendar.TimezoneAdapter.TimezoneRow;
29import com.android.calendar.Utils;
30import com.android.calendar.event.EditEventHelper.EditDoneRunnable;
31import com.android.calendarcommon.EventRecurrence;
32import com.android.common.Rfc822InputFilter;
33import com.android.common.Rfc822Validator;
34import com.android.ex.chips.AccountSpecifier;
35import com.android.ex.chips.BaseRecipientAdapter;
36import com.android.ex.chips.ChipsUtil;
37import com.android.ex.chips.RecipientEditTextView;
38
39import android.app.Activity;
40import android.app.AlertDialog;
41import android.app.DatePickerDialog;
42import android.app.DatePickerDialog.OnDateSetListener;
43import android.app.ProgressDialog;
44import android.app.Service;
45import android.app.TimePickerDialog;
46import android.app.TimePickerDialog.OnTimeSetListener;
47import android.content.Context;
48import android.content.DialogInterface;
49import android.content.Intent;
50import android.content.SharedPreferences;
51import android.content.res.Resources;
52import android.database.Cursor;
53import android.graphics.Bitmap;
54import android.graphics.BitmapFactory;
55import android.graphics.drawable.Drawable;
56import android.provider.CalendarContract.Attendees;
57import android.provider.CalendarContract.Calendars;
58import android.provider.CalendarContract.Reminders;
59import android.provider.Settings;
60import android.text.InputFilter;
61import android.text.TextUtils;
62import android.text.format.DateFormat;
63import android.text.format.DateUtils;
64import android.text.format.Time;
65import android.text.util.Rfc822Tokenizer;
66import android.util.Log;
67import android.view.LayoutInflater;
68import android.view.View;
69import android.view.ViewGroup;
70import android.view.accessibility.AccessibilityEvent;
71import android.view.accessibility.AccessibilityManager;
72import android.widget.AdapterView;
73import android.widget.AdapterView.OnItemSelectedListener;
74import android.widget.ArrayAdapter;
75import android.widget.Button;
76import android.widget.CalendarView;
77import android.widget.CheckBox;
78import android.widget.CompoundButton;
79import android.widget.DatePicker;
80import android.widget.LinearLayout;
81import android.widget.MultiAutoCompleteTextView;
82import android.widget.RadioButton;
83import android.widget.RadioGroup;
84import android.widget.ResourceCursorAdapter;
85import android.widget.ScrollView;
86import android.widget.Spinner;
87import android.widget.TextView;
88import android.widget.TimePicker;
89
90import java.util.ArrayList;
91import java.util.Arrays;
92import java.util.Calendar;
93import java.util.Formatter;
94import java.util.HashMap;
95import java.util.Locale;
96import java.util.TimeZone;
97
98public class EditEventView implements View.OnClickListener, DialogInterface.OnCancelListener,
99        DialogInterface.OnClickListener, OnItemSelectedListener {
100    private static final String TAG = "EditEvent";
101    private static final String GOOGLE_SECONDARY_CALENDAR = "calendar.google.com";
102    private static final String PERIOD_SPACE = ". ";
103
104    ArrayList<View> mEditOnlyList = new ArrayList<View>();
105    ArrayList<View> mEditViewList = new ArrayList<View>();
106    ArrayList<View> mViewOnlyList = new ArrayList<View>();
107    TextView mLoadingMessage;
108    ScrollView mScrollView;
109    Button mStartDateButton;
110    Button mEndDateButton;
111    Button mStartTimeButton;
112    Button mEndTimeButton;
113    Button mTimezoneButton;
114    View mTimezoneRow;
115    TextView mStartTimeHome;
116    TextView mStartDateHome;
117    TextView mEndTimeHome;
118    TextView mEndDateHome;
119    CheckBox mAllDayCheckBox;
120    Spinner mCalendarsSpinner;
121    Spinner mRepeatsSpinner;
122    Spinner mAvailabilitySpinner;
123    Spinner mAccessLevelSpinner;
124    RadioGroup mResponseRadioGroup;
125    TextView mTitleTextView;
126    TextView mLocationTextView;
127    TextView mDescriptionTextView;
128    TextView mWhenView;
129    TextView mTimezoneTextView;
130    TextView mTimezoneLabel;
131    LinearLayout mRemindersContainer;
132    MultiAutoCompleteTextView mAttendeesList;
133    View mCalendarSelectorGroup;
134    View mCalendarSelectorWrapper;
135    View mCalendarStaticGroup;
136    View mLocationGroup;
137    View mDescriptionGroup;
138    View mRemindersGroup;
139    View mResponseGroup;
140    View mOrganizerGroup;
141    View mAttendeesGroup;
142    View mStartHomeGroup;
143    View mEndHomeGroup;
144
145    private int[] mOriginalPadding = new int[4];
146    private int[] mOriginalSpinnerPadding = new int[4];
147
148    private boolean mIsMultipane;
149    private ProgressDialog mLoadingCalendarsDialog;
150    private AlertDialog mNoCalendarsDialog;
151    private AlertDialog mTimezoneDialog;
152    private Activity mActivity;
153    private EditDoneRunnable mDone;
154    private View mView;
155    private CalendarEventModel mModel;
156    private Cursor mCalendarsCursor;
157    private AccountSpecifier mAddressAdapter;
158    private Rfc822Validator mEmailValidator;
159    private TimezoneAdapter mTimezoneAdapter;
160
161    private ArrayList<Integer> mRecurrenceIndexes = new ArrayList<Integer>(0);
162
163    /**
164     * Contents of the "minutes" spinner.  This has default values from the XML file, augmented
165     * with any additional values that were already associated with the event.
166     */
167    private ArrayList<Integer> mReminderMinuteValues;
168    private ArrayList<String> mReminderMinuteLabels;
169
170    /**
171     * Contents of the "methods" spinner.  The "values" list specifies the method constant
172     * (e.g. {@link Reminders#METHOD_ALERT}) associated with the labels.  Any methods that
173     * aren't allowed by the Calendar will be removed.
174     */
175    private ArrayList<Integer> mReminderMethodValues;
176    private ArrayList<String> mReminderMethodLabels;
177
178    /**
179     * Contents of the "availability" spinner. The "values" list specifies the
180     * type constant (e.g. {@link Events#AVAILABILITY_BUSY}) associated with the
181     * labels. Any types that aren't allowed by the Calendar will be removed.
182     */
183    private ArrayList<Integer> mAvailabilityValues;
184    private ArrayList<String> mAvailabilityLabels;
185
186    private int mDefaultReminderMinutes;
187
188    private boolean mSaveAfterQueryComplete = false;
189
190    private Time mStartTime;
191    private Time mEndTime;
192    private String mTimezone;
193    private int mModification = EditEventHelper.MODIFY_UNINITIALIZED;
194
195    private EventRecurrence mEventRecurrence = new EventRecurrence();
196
197    private ArrayList<LinearLayout> mReminderItems = new ArrayList<LinearLayout>(0);
198    private ArrayList<ReminderEntry> mUnsupportedReminders = new ArrayList<ReminderEntry>();
199
200    private static StringBuilder mSB = new StringBuilder(50);
201    private static Formatter mF = new Formatter(mSB, Locale.getDefault());
202
203    /* This class is used to update the time buttons. */
204    private class TimeListener implements OnTimeSetListener {
205        private View mView;
206
207        public TimeListener(View view) {
208            mView = view;
209        }
210
211        @Override
212        public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
213            // Cache the member variables locally to avoid inner class overhead.
214            Time startTime = mStartTime;
215            Time endTime = mEndTime;
216
217            // Cache the start and end millis so that we limit the number
218            // of calls to normalize() and toMillis(), which are fairly
219            // expensive.
220            long startMillis;
221            long endMillis;
222            if (mView == mStartTimeButton) {
223                // The start time was changed.
224                int hourDuration = endTime.hour - startTime.hour;
225                int minuteDuration = endTime.minute - startTime.minute;
226
227                startTime.hour = hourOfDay;
228                startTime.minute = minute;
229                startMillis = startTime.normalize(true);
230
231                // Also update the end time to keep the duration constant.
232                endTime.hour = hourOfDay + hourDuration;
233                endTime.minute = minute + minuteDuration;
234            } else {
235                // The end time was changed.
236                startMillis = startTime.toMillis(true);
237                endTime.hour = hourOfDay;
238                endTime.minute = minute;
239
240                // Move to the start time if the end time is before the start
241                // time.
242                if (endTime.before(startTime)) {
243                    endTime.monthDay = startTime.monthDay + 1;
244                }
245            }
246
247            endMillis = endTime.normalize(true);
248
249            setDate(mEndDateButton, endMillis);
250            setTime(mStartTimeButton, startMillis);
251            setTime(mEndTimeButton, endMillis);
252            updateHomeTime();
253        }
254    }
255
256    private class TimeClickListener implements View.OnClickListener {
257        private Time mTime;
258
259        public TimeClickListener(Time time) {
260            mTime = time;
261        }
262
263        @Override
264        public void onClick(View v) {
265            TimePickerDialog tp = new TimePickerDialog(mActivity, new TimeListener(v), mTime.hour,
266                    mTime.minute, DateFormat.is24HourFormat(mActivity));
267            tp.setCanceledOnTouchOutside(true);
268            tp.show();
269        }
270    }
271
272    private class DateListener implements OnDateSetListener {
273        View mView;
274
275        public DateListener(View view) {
276            mView = view;
277        }
278
279        @Override
280        public void onDateSet(DatePicker view, int year, int month, int monthDay) {
281            Log.d(TAG, "onDateSet: " + year +  " " + month +  " " + monthDay);
282            // Cache the member variables locally to avoid inner class overhead.
283            Time startTime = mStartTime;
284            Time endTime = mEndTime;
285
286            // Cache the start and end millis so that we limit the number
287            // of calls to normalize() and toMillis(), which are fairly
288            // expensive.
289            long startMillis;
290            long endMillis;
291            if (mView == mStartDateButton) {
292                // The start date was changed.
293                int yearDuration = endTime.year - startTime.year;
294                int monthDuration = endTime.month - startTime.month;
295                int monthDayDuration = endTime.monthDay - startTime.monthDay;
296
297                startTime.year = year;
298                startTime.month = month;
299                startTime.monthDay = monthDay;
300                startMillis = startTime.normalize(true);
301
302                // Also update the end date to keep the duration constant.
303                endTime.year = year + yearDuration;
304                endTime.month = month + monthDuration;
305                endTime.monthDay = monthDay + monthDayDuration;
306                endMillis = endTime.normalize(true);
307
308                // If the start date has changed then update the repeats.
309                populateRepeats();
310            } else {
311                // The end date was changed.
312                startMillis = startTime.toMillis(true);
313                endTime.year = year;
314                endTime.month = month;
315                endTime.monthDay = monthDay;
316                endMillis = endTime.normalize(true);
317
318                // Do not allow an event to have an end time before the start
319                // time.
320                if (endTime.before(startTime)) {
321                    endTime.set(startTime);
322                    endMillis = startMillis;
323                }
324            }
325
326            setDate(mStartDateButton, startMillis);
327            setDate(mEndDateButton, endMillis);
328            setTime(mEndTimeButton, endMillis); // In case end time had to be
329            // reset
330            updateHomeTime();
331        }
332    }
333
334    // Fills in the date and time fields
335    private void populateWhen() {
336        long startMillis = mStartTime.toMillis(false /* use isDst */);
337        long endMillis = mEndTime.toMillis(false /* use isDst */);
338        setDate(mStartDateButton, startMillis);
339        setDate(mEndDateButton, endMillis);
340
341        setTime(mStartTimeButton, startMillis);
342        setTime(mEndTimeButton, endMillis);
343
344        mStartDateButton.setOnClickListener(new DateClickListener(mStartTime));
345        mEndDateButton.setOnClickListener(new DateClickListener(mEndTime));
346
347        mStartTimeButton.setOnClickListener(new TimeClickListener(mStartTime));
348        mEndTimeButton.setOnClickListener(new TimeClickListener(mEndTime));
349    }
350
351    private void populateTimezone() {
352        mTimezoneButton.setOnClickListener(new View.OnClickListener() {
353            @Override
354            public void onClick(View v) {
355                showTimezoneDialog();
356            }
357        });
358        setTimezone(mTimezoneAdapter.getRowById(mTimezone));
359    }
360
361    private void showTimezoneDialog() {
362        AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
363        final Context alertDialogContext = builder.getContext();
364        mTimezoneAdapter = new TimezoneAdapter(alertDialogContext, mTimezone);
365        builder.setTitle(R.string.timezone_label);
366        builder.setSingleChoiceItems(
367                mTimezoneAdapter, mTimezoneAdapter.getRowById(mTimezone), this);
368        mTimezoneDialog = builder.create();
369
370        LayoutInflater layoutInflater = (LayoutInflater) alertDialogContext
371                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
372        final TextView timezoneFooterView = (TextView) layoutInflater.inflate(
373                R.layout.timezone_footer, null);
374
375        timezoneFooterView.setText(mActivity.getString(R.string.edit_event_show_all) + " >");
376        timezoneFooterView.setOnClickListener(new View.OnClickListener() {
377            @Override
378            public void onClick(View v) {
379                mTimezoneDialog.getListView().removeFooterView(timezoneFooterView);
380                mTimezoneAdapter.showAllTimezones();
381                final int row = mTimezoneAdapter.getRowById(mTimezone);
382                // we need to post the selection changes to have them have
383                // any effect
384                mTimezoneDialog.getListView().post(new Runnable() {
385                    @Override
386                    public void run() {
387                        mTimezoneDialog.getListView().setItemChecked(row, true);
388                        mTimezoneDialog.getListView().setSelection(row);
389                    }
390                });
391            }
392        });
393        mTimezoneDialog.getListView().addFooterView(timezoneFooterView);
394        mTimezoneDialog.setCanceledOnTouchOutside(true);
395        mTimezoneDialog.show();
396    }
397
398    private void populateRepeats() {
399        Time time = mStartTime;
400        Resources r = mActivity.getResources();
401
402        String[] days = new String[] {
403                DateUtils.getDayOfWeekString(Calendar.SUNDAY, DateUtils.LENGTH_MEDIUM),
404                DateUtils.getDayOfWeekString(Calendar.MONDAY, DateUtils.LENGTH_MEDIUM),
405                DateUtils.getDayOfWeekString(Calendar.TUESDAY, DateUtils.LENGTH_MEDIUM),
406                DateUtils.getDayOfWeekString(Calendar.WEDNESDAY, DateUtils.LENGTH_MEDIUM),
407                DateUtils.getDayOfWeekString(Calendar.THURSDAY, DateUtils.LENGTH_MEDIUM),
408                DateUtils.getDayOfWeekString(Calendar.FRIDAY, DateUtils.LENGTH_MEDIUM),
409                DateUtils.getDayOfWeekString(Calendar.SATURDAY, DateUtils.LENGTH_MEDIUM), };
410        String[] ordinals = r.getStringArray(R.array.ordinal_labels);
411
412        // Only display "Custom" in the spinner if the device does not support
413        // the recurrence functionality of the event. Only display every weekday
414        // if the event starts on a weekday.
415        boolean isCustomRecurrence = isCustomRecurrence();
416        boolean isWeekdayEvent = isWeekdayEvent();
417
418        ArrayList<String> repeatArray = new ArrayList<String>(0);
419        ArrayList<Integer> recurrenceIndexes = new ArrayList<Integer>(0);
420
421        repeatArray.add(r.getString(R.string.does_not_repeat));
422        recurrenceIndexes.add(EditEventHelper.DOES_NOT_REPEAT);
423
424        repeatArray.add(r.getString(R.string.daily));
425        recurrenceIndexes.add(EditEventHelper.REPEATS_DAILY);
426
427        if (isWeekdayEvent) {
428            repeatArray.add(r.getString(R.string.every_weekday));
429            recurrenceIndexes.add(EditEventHelper.REPEATS_EVERY_WEEKDAY);
430        }
431
432        String format = r.getString(R.string.weekly);
433        repeatArray.add(String.format(format, time.format("%A")));
434        recurrenceIndexes.add(EditEventHelper.REPEATS_WEEKLY_ON_DAY);
435
436        // Calculate whether this is the 1st, 2nd, 3rd, 4th, or last appearance
437        // of the given day.
438        int dayNumber = (time.monthDay - 1) / 7;
439        format = r.getString(R.string.monthly_on_day_count);
440        repeatArray.add(String.format(format, ordinals[dayNumber], days[time.weekDay]));
441        recurrenceIndexes.add(EditEventHelper.REPEATS_MONTHLY_ON_DAY_COUNT);
442
443        format = r.getString(R.string.monthly_on_day);
444        repeatArray.add(String.format(format, time.monthDay));
445        recurrenceIndexes.add(EditEventHelper.REPEATS_MONTHLY_ON_DAY);
446
447        long when = time.toMillis(false);
448        format = r.getString(R.string.yearly);
449        int flags = 0;
450        if (DateFormat.is24HourFormat(mActivity)) {
451            flags |= DateUtils.FORMAT_24HOUR;
452        }
453        repeatArray.add(String.format(format, DateUtils.formatDateTime(mActivity, when, flags)));
454        recurrenceIndexes.add(EditEventHelper.REPEATS_YEARLY);
455
456        if (isCustomRecurrence) {
457            repeatArray.add(r.getString(R.string.custom));
458            recurrenceIndexes.add(EditEventHelper.REPEATS_CUSTOM);
459        }
460        mRecurrenceIndexes = recurrenceIndexes;
461
462        int position = recurrenceIndexes.indexOf(EditEventHelper.DOES_NOT_REPEAT);
463        if (!TextUtils.isEmpty(mModel.mRrule)) {
464            if (isCustomRecurrence) {
465                position = recurrenceIndexes.indexOf(EditEventHelper.REPEATS_CUSTOM);
466            } else {
467                switch (mEventRecurrence.freq) {
468                    case EventRecurrence.DAILY:
469                        position = recurrenceIndexes.indexOf(EditEventHelper.REPEATS_DAILY);
470                        break;
471                    case EventRecurrence.WEEKLY:
472                        if (mEventRecurrence.repeatsOnEveryWeekDay()) {
473                            position = recurrenceIndexes.indexOf(
474                                    EditEventHelper.REPEATS_EVERY_WEEKDAY);
475                        } else {
476                            position = recurrenceIndexes.indexOf(
477                                    EditEventHelper.REPEATS_WEEKLY_ON_DAY);
478                        }
479                        break;
480                    case EventRecurrence.MONTHLY:
481                        if (mEventRecurrence.repeatsMonthlyOnDayCount()) {
482                            position = recurrenceIndexes.indexOf(
483                                    EditEventHelper.REPEATS_MONTHLY_ON_DAY_COUNT);
484                        } else {
485                            position = recurrenceIndexes.indexOf(
486                                    EditEventHelper.REPEATS_MONTHLY_ON_DAY);
487                        }
488                        break;
489                    case EventRecurrence.YEARLY:
490                        position = recurrenceIndexes.indexOf(EditEventHelper.REPEATS_YEARLY);
491                        break;
492                }
493            }
494        }
495        ArrayAdapter<String> adapter = new ArrayAdapter<String>(mActivity,
496                android.R.layout.simple_spinner_item, repeatArray);
497        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
498        mRepeatsSpinner.setAdapter(adapter);
499        mRepeatsSpinner.setSelection(position);
500
501        // Don't allow the user to make exceptions recurring events.
502        if (mModel.mOriginalSyncId != null) {
503            mRepeatsSpinner.setEnabled(false);
504        }
505    }
506
507    private boolean isCustomRecurrence() {
508
509        if (mEventRecurrence.until != null
510                || (mEventRecurrence.interval != 0 && mEventRecurrence.interval != 1)
511                || mEventRecurrence.count != 0) {
512            return true;
513        }
514
515        if (mEventRecurrence.freq == 0) {
516            return false;
517        }
518
519        switch (mEventRecurrence.freq) {
520            case EventRecurrence.DAILY:
521                return false;
522            case EventRecurrence.WEEKLY:
523                if (mEventRecurrence.repeatsOnEveryWeekDay() && isWeekdayEvent()) {
524                    return false;
525                } else if (mEventRecurrence.bydayCount == 1) {
526                    return false;
527                }
528                break;
529            case EventRecurrence.MONTHLY:
530                if (mEventRecurrence.repeatsMonthlyOnDayCount()) {
531                    /* this is a "3rd Tuesday of every month" sort of rule */
532                    return false;
533                } else if (mEventRecurrence.bydayCount == 0
534                        && mEventRecurrence.bymonthdayCount == 1
535                        && mEventRecurrence.bymonthday[0] > 0) {
536                    /* this is a "22nd day of every month" sort of rule */
537                    return false;
538                }
539                break;
540            case EventRecurrence.YEARLY:
541                return false;
542        }
543
544        return true;
545    }
546
547    private boolean isWeekdayEvent() {
548        if (mStartTime.weekDay != Time.SUNDAY && mStartTime.weekDay != Time.SATURDAY) {
549            return true;
550        }
551        return false;
552    }
553
554    private class DateClickListener implements View.OnClickListener {
555        private Time mTime;
556
557        public DateClickListener(Time time) {
558            mTime = time;
559        }
560
561        public void onClick(View v) {
562            DatePickerDialog dpd = new DatePickerDialog(
563                    mActivity, new DateListener(v), mTime.year, mTime.month, mTime.monthDay);
564            CalendarView cv = dpd.getDatePicker().getCalendarView();
565            cv.setShowWeekNumber(Utils.getShowWeekNumber(mActivity));
566            int startOfWeek = Utils.getFirstDayOfWeek(mActivity);
567            // Utils returns Time days while CalendarView wants Calendar days
568            if (startOfWeek == Time.SATURDAY) {
569                startOfWeek = Calendar.SATURDAY;
570            } else if (startOfWeek == Time.SUNDAY) {
571                startOfWeek = Calendar.SUNDAY;
572            } else {
573                startOfWeek = Calendar.MONDAY;
574            }
575            cv.setFirstDayOfWeek(startOfWeek);
576            dpd.setCanceledOnTouchOutside(true);
577            dpd.show();
578        }
579    }
580
581    static private class CalendarsAdapter extends ResourceCursorAdapter {
582        public CalendarsAdapter(Context context, Cursor c) {
583            super(context, R.layout.calendars_item, c);
584            setDropDownViewResource(R.layout.calendars_dropdown_item);
585        }
586
587        @Override
588        public void bindView(View view, Context context, Cursor cursor) {
589            View colorBar = view.findViewById(R.id.color);
590            int colorColumn = cursor.getColumnIndexOrThrow(Calendars.CALENDAR_COLOR);
591            int nameColumn = cursor.getColumnIndexOrThrow(Calendars.CALENDAR_DISPLAY_NAME);
592            int ownerColumn = cursor.getColumnIndexOrThrow(Calendars.OWNER_ACCOUNT);
593            if (colorBar != null) {
594                colorBar.setBackgroundColor(Utils.getDisplayColorFromColor(cursor
595                        .getInt(colorColumn)));
596            }
597
598            TextView name = (TextView) view.findViewById(R.id.calendar_name);
599            if (name != null) {
600                String displayName = cursor.getString(nameColumn);
601                name.setText(displayName);
602
603                TextView accountName = (TextView) view.findViewById(R.id.account_name);
604                if (accountName != null) {
605                    accountName.setText(cursor.getString(ownerColumn));
606                    accountName.setVisibility(TextView.VISIBLE);
607                }
608            }
609        }
610    }
611
612    /**
613     * Does prep steps for saving a calendar event.
614     *
615     * This triggers a parse of the attendees list and checks if the event is
616     * ready to be saved. An event is ready to be saved so long as a model
617     * exists and has a calendar it can be associated with, either because it's
618     * an existing event or we've finished querying.
619     *
620     * @return false if there is no model or no calendar had been loaded yet,
621     * true otherwise.
622     */
623    public boolean prepareForSave() {
624        if (mModel == null || (mCalendarsCursor == null && mModel.mUri == null)) {
625            return false;
626        }
627        return fillModelFromUI();
628    }
629
630    public boolean fillModelFromReadOnlyUi() {
631        if (mModel == null || (mCalendarsCursor == null && mModel.mUri == null)) {
632            return false;
633        }
634        mModel.mReminders = EventViewUtils.reminderItemsToReminders(
635                    mReminderItems, mReminderMinuteValues, mReminderMethodValues);
636        mModel.mReminders.addAll(mUnsupportedReminders);
637        mModel.normalizeReminders();
638        int status = EventInfoFragment.getResponseFromButtonId(
639                mResponseRadioGroup.getCheckedRadioButtonId());
640        if (status != Attendees.ATTENDEE_STATUS_NONE) {
641            mModel.mSelfAttendeeStatus = status;
642        }
643        return true;
644    }
645
646    // This is called if the user clicks on one of the buttons: "Save",
647    // "Discard", or "Delete". This is also called if the user clicks
648    // on the "remove reminder" button.
649    @Override
650    public void onClick(View view) {
651
652        // This must be a click on one of the "remove reminder" buttons
653        LinearLayout reminderItem = (LinearLayout) view.getParent();
654        LinearLayout parent = (LinearLayout) reminderItem.getParent();
655        parent.removeView(reminderItem);
656        mReminderItems.remove(reminderItem);
657        updateRemindersVisibility(mReminderItems.size());
658    }
659
660    // This is called if the user cancels the "No calendars" dialog.
661    // The "No calendars" dialog is shown if there are no syncable calendars.
662    @Override
663    public void onCancel(DialogInterface dialog) {
664        if (dialog == mLoadingCalendarsDialog) {
665            mLoadingCalendarsDialog = null;
666            mSaveAfterQueryComplete = false;
667        } else if (dialog == mNoCalendarsDialog) {
668            mDone.setDoneCode(Utils.DONE_REVERT);
669            mDone.run();
670            return;
671        }
672    }
673
674    // This is called if the user clicks on a dialog button.
675    @Override
676    public void onClick(DialogInterface dialog, int which) {
677        if (dialog == mNoCalendarsDialog) {
678            mDone.setDoneCode(Utils.DONE_REVERT);
679            mDone.run();
680            if (which == DialogInterface.BUTTON_POSITIVE) {
681                Intent nextIntent = new Intent(Settings.ACTION_ADD_ACCOUNT);
682                final String[] array = {"com.android.calendar"};
683                nextIntent.putExtra(Settings.EXTRA_AUTHORITIES, array);
684                nextIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
685                mActivity.startActivity(nextIntent);
686            }
687        } else if (dialog == mTimezoneDialog) {
688            if (which >= 0 && which < mTimezoneAdapter.getCount()) {
689                setTimezone(which);
690                updateHomeTime();
691                dialog.dismiss();
692            }
693        }
694    }
695
696    // Goes through the UI elements and updates the model as necessary
697    private boolean fillModelFromUI() {
698        if (mModel == null) {
699            return false;
700        }
701        mModel.mReminders = EventViewUtils.reminderItemsToReminders(mReminderItems,
702                mReminderMinuteValues, mReminderMethodValues);
703        mModel.mReminders.addAll(mUnsupportedReminders);
704        mModel.normalizeReminders();
705        mModel.mHasAlarm = mReminderItems.size() > 0;
706        mModel.mTitle = mTitleTextView.getText().toString();
707        mModel.mAllDay = mAllDayCheckBox.isChecked();
708        mModel.mLocation = mLocationTextView.getText().toString();
709        mModel.mDescription = mDescriptionTextView.getText().toString();
710        if (TextUtils.isEmpty(mModel.mLocation)) {
711            mModel.mLocation = null;
712        }
713        if (TextUtils.isEmpty(mModel.mDescription)) {
714            mModel.mDescription = null;
715        }
716
717        int status = EventInfoFragment.getResponseFromButtonId(mResponseRadioGroup
718                .getCheckedRadioButtonId());
719        if (status != Attendees.ATTENDEE_STATUS_NONE) {
720            mModel.mSelfAttendeeStatus = status;
721        }
722
723        if (mAttendeesList != null) {
724            mEmailValidator.setRemoveInvalid(true);
725            mAttendeesList.performValidation();
726            mModel.mAttendeesList.clear();
727            mModel.addAttendees(mAttendeesList.getText().toString(), mEmailValidator);
728            mEmailValidator.setRemoveInvalid(false);
729        }
730
731        // If this was a new event we need to fill in the Calendar information
732        if (mModel.mUri == null) {
733            mModel.mCalendarId = mCalendarsSpinner.getSelectedItemId();
734            int calendarCursorPosition = mCalendarsSpinner.getSelectedItemPosition();
735            if (mCalendarsCursor.moveToPosition(calendarCursorPosition)) {
736                String defaultCalendar = mCalendarsCursor.getString(
737                        EditEventHelper.CALENDARS_INDEX_OWNER_ACCOUNT);
738                Utils.setSharedPreference(
739                        mActivity, GeneralPreferences.KEY_DEFAULT_CALENDAR, defaultCalendar);
740                mModel.mOwnerAccount = defaultCalendar;
741                mModel.mOrganizer = defaultCalendar;
742                mModel.mCalendarId = mCalendarsCursor.getLong(EditEventHelper.CALENDARS_INDEX_ID);
743            }
744        }
745
746        if (mModel.mAllDay) {
747            // Reset start and end time, increment the monthDay by 1, and set
748            // the timezone to UTC, as required for all-day events.
749            mTimezone = Time.TIMEZONE_UTC;
750            mStartTime.hour = 0;
751            mStartTime.minute = 0;
752            mStartTime.second = 0;
753            mStartTime.timezone = mTimezone;
754            mModel.mStart = mStartTime.normalize(true);
755
756            mEndTime.hour = 0;
757            mEndTime.minute = 0;
758            mEndTime.second = 0;
759            mEndTime.timezone = mTimezone;
760            // When a user see the event duration as "X - Y" (e.g. Oct. 28 - Oct. 29), end time
761            // should be Y + 1 (Oct.30).
762            final long normalizedEndTimeMillis =
763                    mEndTime.normalize(true) + DateUtils.DAY_IN_MILLIS;
764            if (normalizedEndTimeMillis < mModel.mStart) {
765                // mEnd should be midnight of the next day of mStart.
766                mModel.mEnd = mModel.mStart + DateUtils.DAY_IN_MILLIS;
767            } else {
768                mModel.mEnd = normalizedEndTimeMillis;
769            }
770        } else {
771            mStartTime.timezone = mTimezone;
772            mEndTime.timezone = mTimezone;
773            mModel.mStart = mStartTime.toMillis(true);
774            mModel.mEnd = mEndTime.toMillis(true);
775        }
776        mModel.mTimezone = mTimezone;
777        mModel.mAccessLevel = mAccessLevelSpinner.getSelectedItemPosition();
778        // TODO set correct availability value
779        mModel.mAvailability = mAvailabilityValues.get(mAvailabilitySpinner
780                .getSelectedItemPosition());
781
782        int selection;
783        // If we're making an exception we don't want it to be a repeating
784        // event.
785        if (mModification == EditEventHelper.MODIFY_SELECTED) {
786            selection = EditEventHelper.DOES_NOT_REPEAT;
787        } else {
788            int position = mRepeatsSpinner.getSelectedItemPosition();
789            selection = mRecurrenceIndexes.get(position);
790        }
791
792        EditEventHelper.updateRecurrenceRule(
793                selection, mModel, Utils.getFirstDayOfWeek(mActivity) + 1);
794
795        // Save the timezone so we can display it as a standard option next time
796        if (!mModel.mAllDay) {
797            mTimezoneAdapter.saveRecentTimezone(mTimezone);
798        }
799        return true;
800    }
801
802    public EditEventView(Activity activity, View view, EditDoneRunnable done) {
803
804        mActivity = activity;
805        mView = view;
806        mDone = done;
807
808        // cache top level view elements
809        mLoadingMessage = (TextView) view.findViewById(R.id.loading_message);
810        mScrollView = (ScrollView) view.findViewById(R.id.scroll_view);
811
812        // cache all the widgets
813        mCalendarsSpinner = (Spinner) view.findViewById(R.id.calendars_spinner);
814        mTitleTextView = (TextView) view.findViewById(R.id.title);
815        mLocationTextView = (TextView) view.findViewById(R.id.location);
816        mDescriptionTextView = (TextView) view.findViewById(R.id.description);
817        mTimezoneLabel = (TextView) view.findViewById(R.id.timezone_label);
818        mStartDateButton = (Button) view.findViewById(R.id.start_date);
819        mEndDateButton = (Button) view.findViewById(R.id.end_date);
820        mWhenView = (TextView) mView.findViewById(R.id.when);
821        mTimezoneTextView = (TextView) mView.findViewById(R.id.timezone_textView);
822        mStartTimeButton = (Button) view.findViewById(R.id.start_time);
823        mEndTimeButton = (Button) view.findViewById(R.id.end_time);
824        mTimezoneButton = (Button) view.findViewById(R.id.timezone_button);
825        mTimezoneRow = view.findViewById(R.id.timezone_button_row);
826        mStartTimeHome = (TextView) view.findViewById(R.id.start_time_home_tz);
827        mStartDateHome = (TextView) view.findViewById(R.id.start_date_home_tz);
828        mEndTimeHome = (TextView) view.findViewById(R.id.end_time_home_tz);
829        mEndDateHome = (TextView) view.findViewById(R.id.end_date_home_tz);
830        mAllDayCheckBox = (CheckBox) view.findViewById(R.id.is_all_day);
831        mRepeatsSpinner = (Spinner) view.findViewById(R.id.repeats);
832        mAvailabilitySpinner = (Spinner) view.findViewById(R.id.availability);
833        mAccessLevelSpinner = (Spinner) view.findViewById(R.id.visibility);
834        mCalendarSelectorGroup = view.findViewById(R.id.calendar_selector_group);
835        mCalendarSelectorWrapper = view.findViewById(R.id.calendar_selector_wrapper);
836        mCalendarStaticGroup = view.findViewById(R.id.calendar_group);
837        mRemindersGroup = view.findViewById(R.id.reminders_row);
838        mResponseGroup = view.findViewById(R.id.response_row);
839        mOrganizerGroup = view.findViewById(R.id.organizer_row);
840        mAttendeesGroup = view.findViewById(R.id.add_attendees_row);
841        mLocationGroup = view.findViewById(R.id.where_row);
842        mDescriptionGroup = view.findViewById(R.id.description_row);
843        mStartHomeGroup = view.findViewById(R.id.from_row_home_tz);
844        mEndHomeGroup = view.findViewById(R.id.to_row_home_tz);
845        mAttendeesList = (MultiAutoCompleteTextView) view.findViewById(R.id.attendees);
846
847        mTitleTextView.setTag(mTitleTextView.getBackground());
848        mLocationTextView.setTag(mLocationTextView.getBackground());
849        mDescriptionTextView.setTag(mDescriptionTextView.getBackground());
850        mRepeatsSpinner.setTag(mRepeatsSpinner.getBackground());
851        mAttendeesList.setTag(mAttendeesList.getBackground());
852        mOriginalPadding[0] = mLocationTextView.getPaddingLeft();
853        mOriginalPadding[1] = mLocationTextView.getPaddingTop();
854        mOriginalPadding[2] = mLocationTextView.getPaddingRight();
855        mOriginalPadding[3] = mLocationTextView.getPaddingBottom();
856        mOriginalSpinnerPadding[0] = mRepeatsSpinner.getPaddingLeft();
857        mOriginalSpinnerPadding[1] = mRepeatsSpinner.getPaddingTop();
858        mOriginalSpinnerPadding[2] = mRepeatsSpinner.getPaddingRight();
859        mOriginalSpinnerPadding[3] = mRepeatsSpinner.getPaddingBottom();
860        mEditViewList.add(mTitleTextView);
861        mEditViewList.add(mLocationTextView);
862        mEditViewList.add(mDescriptionTextView);
863        mEditViewList.add(mAttendeesList);
864
865        mViewOnlyList.add(view.findViewById(R.id.when_row));
866        mViewOnlyList.add(view.findViewById(R.id.timezone_textview_row));
867
868        mEditOnlyList.add(view.findViewById(R.id.all_day_row));
869        mEditOnlyList.add(view.findViewById(R.id.availability_row));
870        mEditOnlyList.add(view.findViewById(R.id.visibility_row));
871        mEditOnlyList.add(view.findViewById(R.id.from_row));
872        mEditOnlyList.add(view.findViewById(R.id.to_row));
873        mEditOnlyList.add(mTimezoneRow);
874        mEditOnlyList.add(mStartHomeGroup);
875        mEditOnlyList.add(mEndHomeGroup);
876
877        mResponseRadioGroup = (RadioGroup) view.findViewById(R.id.response_value);
878        mRemindersContainer = (LinearLayout) view.findViewById(R.id.reminder_items_container);
879
880        mTimezone = Utils.getTimeZone(activity, null);
881        mIsMultipane = activity.getResources().getBoolean(R.bool.tablet_config);
882        mStartTime = new Time(mTimezone);
883        mEndTime = new Time(mTimezone);
884        mTimezoneAdapter = new TimezoneAdapter(mActivity, mTimezone);
885        mEmailValidator = new Rfc822Validator(null);
886        initMultiAutoCompleteTextView((RecipientEditTextView) mAttendeesList);
887
888        // Display loading screen
889        setModel(null);
890    }
891
892
893    /**
894     * Loads an integer array asset into a list.
895     */
896    private static ArrayList<Integer> loadIntegerArray(Resources r, int resNum) {
897        int[] vals = r.getIntArray(resNum);
898        int size = vals.length;
899        ArrayList<Integer> list = new ArrayList<Integer>(size);
900
901        for (int i = 0; i < size; i++) {
902            list.add(vals[i]);
903        }
904
905        return list;
906    }
907
908    /**
909     * Loads a String array asset into a list.
910     */
911    private static ArrayList<String> loadStringArray(Resources r, int resNum) {
912        String[] labels = r.getStringArray(resNum);
913        ArrayList<String> list = new ArrayList<String>(Arrays.asList(labels));
914        return list;
915    }
916
917    private void prepareAvailability() {
918        Resources r = mActivity.getResources();
919
920        mAvailabilityValues = loadIntegerArray(r, R.array.availability_values);
921        mAvailabilityLabels = loadStringArray(r, R.array.availability);
922
923        if (mModel.mCalendarAllowedAvailability != null) {
924            EventViewUtils.reduceMethodList(mAvailabilityValues, mAvailabilityLabels,
925                    mModel.mCalendarAllowedAvailability);
926        }
927
928        ArrayAdapter<String> adapter = new ArrayAdapter<String>(mActivity,
929                android.R.layout.simple_spinner_item, mAvailabilityLabels);
930        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
931        mAvailabilitySpinner.setAdapter(adapter);
932    }
933
934    /**
935     * Prepares the reminder UI elements.
936     * <p>
937     * (Re-)loads the minutes / methods lists from the XML assets, adds/removes items as
938     * needed for the current set of reminders and calendar properties, and then creates UI
939     * elements.
940     */
941    private void prepareReminders() {
942        CalendarEventModel model = mModel;
943        Resources r = mActivity.getResources();
944
945        // Load the labels and corresponding numeric values for the minutes and methods lists
946        // from the assets.  If we're switching calendars, we need to clear and re-populate the
947        // lists (which may have elements added and removed based on calendar properties).  This
948        // is mostly relevant for "methods", since we shouldn't have any "minutes" values in a
949        // new event that aren't in the default set.
950        mReminderMinuteValues = loadIntegerArray(r, R.array.reminder_minutes_values);
951        mReminderMinuteLabels = loadStringArray(r, R.array.reminder_minutes_labels);
952        mReminderMethodValues = loadIntegerArray(r, R.array.reminder_methods_values);
953        mReminderMethodLabels = loadStringArray(r, R.array.reminder_methods_labels);
954
955        // Remove any reminder methods that aren't allowed for this calendar.  If this is
956        // a new event, mCalendarAllowedReminders may not be set the first time we're called.
957        if (mModel.mCalendarAllowedReminders != null) {
958            EventViewUtils.reduceMethodList(mReminderMethodValues, mReminderMethodLabels,
959                    mModel.mCalendarAllowedReminders);
960        }
961
962        int numReminders = 0;
963        if (model.mHasAlarm) {
964            ArrayList<ReminderEntry> reminders = model.mReminders;
965            numReminders = reminders.size();
966            // Insert any minute values that aren't represented in the minutes list.
967            for (ReminderEntry re : reminders) {
968                if (mReminderMethodValues.contains(re.getMethod())) {
969                    EventViewUtils.addMinutesToList(mActivity, mReminderMinuteValues,
970                            mReminderMinuteLabels, re.getMinutes());
971                }
972            }
973
974            // Create a UI element for each reminder.  We display all of the reminders we get
975            // from the provider, even if the count exceeds the calendar maximum.  (Also, for
976            // a new event, we won't have a maxReminders value available.)
977            mUnsupportedReminders.clear();
978            for (ReminderEntry re : reminders) {
979                if (mReminderMethodValues.contains(re.getMethod())
980                        || re.getMethod() == Reminders.METHOD_DEFAULT) {
981                    EventViewUtils.addReminder(mActivity, mScrollView, this, mReminderItems,
982                            mReminderMinuteValues, mReminderMinuteLabels, mReminderMethodValues,
983                            mReminderMethodLabels, re, Integer.MAX_VALUE, null);
984                } else {
985                    // TODO figure out a way to display unsupported reminders
986                    mUnsupportedReminders.add(re);
987                }
988            }
989        }
990
991        updateRemindersVisibility(numReminders);
992    }
993
994    /**
995     * Fill in the view with the contents of the given event model. This allows
996     * an edit view to be initialized before the event has been loaded. Passing
997     * in null for the model will display a loading screen. A non-null model
998     * will fill in the view's fields with the data contained in the model.
999     *
1000     * @param model The event model to pull the data from
1001     */
1002    public void setModel(CalendarEventModel model) {
1003        mModel = model;
1004
1005        // Need to close the autocomplete adapter to prevent leaking cursors.
1006        if (mAddressAdapter != null && mAddressAdapter instanceof EmailAddressAdapter) {
1007            ((EmailAddressAdapter)mAddressAdapter).close();
1008            mAddressAdapter = null;
1009        }
1010
1011        if (model == null) {
1012            // Display loading screen
1013            mLoadingMessage.setVisibility(View.VISIBLE);
1014            mScrollView.setVisibility(View.GONE);
1015            return;
1016        }
1017
1018        boolean canRespond = EditEventHelper.canRespond(model);
1019
1020        long begin = model.mStart;
1021        long end = model.mEnd;
1022        mTimezone = model.mTimezone; // this will be UTC for all day events
1023
1024        // Set up the starting times
1025        if (begin > 0) {
1026            mStartTime.timezone = mTimezone;
1027            mStartTime.set(begin);
1028            mStartTime.normalize(true);
1029        }
1030        if (end > 0) {
1031            mEndTime.timezone = mTimezone;
1032            mEndTime.set(end);
1033            mEndTime.normalize(true);
1034        }
1035        String rrule = model.mRrule;
1036        if (!TextUtils.isEmpty(rrule)) {
1037            mEventRecurrence.parse(rrule);
1038        }
1039
1040        // If the user is allowed to change the attendees set up the view and
1041        // validator
1042        if (!model.mHasAttendeeData) {
1043            mAttendeesGroup.setVisibility(View.GONE);
1044        }
1045
1046        mAllDayCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
1047            @Override
1048            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
1049                setAllDayViewsVisibility(isChecked);
1050            }
1051        });
1052
1053        boolean prevAllDay = mAllDayCheckBox.isChecked();
1054        if (model.mAllDay) {
1055            mAllDayCheckBox.setChecked(true);
1056            // put things back in local time for all day events
1057            mTimezone = TimeZone.getDefault().getID();
1058            mStartTime.timezone = mTimezone;
1059            mStartTime.normalize(true);
1060            mEndTime.timezone = mTimezone;
1061            mEndTime.normalize(true);
1062        } else {
1063            mAllDayCheckBox.setChecked(false);
1064        }
1065        // On a rotation we need to update the views but onCheckedChanged
1066        // doesn't get called
1067        if (prevAllDay == mAllDayCheckBox.isChecked()) {
1068            setAllDayViewsVisibility(prevAllDay);
1069        }
1070
1071        mTimezoneAdapter = new TimezoneAdapter(mActivity, mTimezone);
1072        if (mTimezoneDialog != null) {
1073            mTimezoneDialog.getListView().setAdapter(mTimezoneAdapter);
1074        }
1075
1076        SharedPreferences prefs = GeneralPreferences.getSharedPreferences(mActivity);
1077        String defaultReminderString = prefs.getString(
1078                GeneralPreferences.KEY_DEFAULT_REMINDER, GeneralPreferences.NO_REMINDER_STRING);
1079        mDefaultReminderMinutes = Integer.parseInt(defaultReminderString);
1080
1081        prepareReminders();
1082        prepareAvailability();
1083
1084        View reminderAddButton = mView.findViewById(R.id.reminder_add);
1085        View.OnClickListener addReminderOnClickListener = new View.OnClickListener() {
1086            @Override
1087            public void onClick(View v) {
1088                addReminder();
1089            }
1090        };
1091        reminderAddButton.setOnClickListener(addReminderOnClickListener);
1092
1093        if (!mIsMultipane) {
1094            mView.findViewById(R.id.is_all_day_label).setOnClickListener(
1095                    new View.OnClickListener() {
1096                        @Override
1097                        public void onClick(View v) {
1098                            mAllDayCheckBox.setChecked(!mAllDayCheckBox.isChecked());
1099                        }
1100                    });
1101        }
1102
1103        if (model.mTitle != null) {
1104            mTitleTextView.setTextKeepState(model.mTitle);
1105        }
1106
1107        if (model.mIsOrganizer || TextUtils.isEmpty(model.mOrganizer)
1108                || model.mOrganizer.endsWith(GOOGLE_SECONDARY_CALENDAR)) {
1109            mView.findViewById(R.id.organizer_label).setVisibility(View.GONE);
1110            mView.findViewById(R.id.organizer).setVisibility(View.GONE);
1111            mOrganizerGroup.setVisibility(View.GONE);
1112        } else {
1113            ((TextView) mView.findViewById(R.id.organizer)).setText(model.mOrganizerDisplayName);
1114        }
1115
1116        if (model.mLocation != null) {
1117            mLocationTextView.setTextKeepState(model.mLocation);
1118        }
1119
1120        if (model.mDescription != null) {
1121            mDescriptionTextView.setTextKeepState(model.mDescription);
1122        }
1123
1124        int availIndex = mAvailabilityValues.indexOf(model.mAvailability);
1125        if (availIndex != -1) {
1126            mAvailabilitySpinner.setSelection(availIndex);
1127        }
1128        mAccessLevelSpinner.setSelection(model.mAccessLevel);
1129
1130        View responseLabel = mView.findViewById(R.id.response_label);
1131        if (canRespond) {
1132            int buttonToCheck = EventInfoFragment
1133                    .findButtonIdForResponse(model.mSelfAttendeeStatus);
1134            mResponseRadioGroup.check(buttonToCheck); // -1 clear all radio buttons
1135            mResponseRadioGroup.setVisibility(View.VISIBLE);
1136            responseLabel.setVisibility(View.VISIBLE);
1137        } else {
1138            responseLabel.setVisibility(View.GONE);
1139            mResponseRadioGroup.setVisibility(View.GONE);
1140            mResponseGroup.setVisibility(View.GONE);
1141        }
1142
1143        int displayColor = Utils.getDisplayColorFromColor(model.mCalendarColor);
1144        if (model.mUri != null) {
1145            // This is an existing event so hide the calendar spinner
1146            // since we can't change the calendar.
1147            View calendarGroup = mView.findViewById(R.id.calendar_selector_group);
1148            calendarGroup.setVisibility(View.GONE);
1149            TextView tv = (TextView) mView.findViewById(R.id.calendar_textview);
1150            tv.setText(model.mCalendarDisplayName);
1151            tv = (TextView) mView.findViewById(R.id.calendar_textview_secondary);
1152            if (tv != null) {
1153                tv.setText(model.mOwnerAccount);
1154            }
1155            if (mIsMultipane) {
1156                mView.findViewById(R.id.calendar_textview).setBackgroundColor(displayColor);
1157            } else {
1158                mView.findViewById(R.id.calendar_group).setBackgroundColor(displayColor);
1159            }
1160        } else {
1161            View calendarGroup = mView.findViewById(R.id.calendar_group);
1162            calendarGroup.setVisibility(View.GONE);
1163        }
1164
1165        populateTimezone();
1166        populateWhen();
1167        populateRepeats();
1168        updateAttendees(model.mAttendeesList);
1169
1170        updateView();
1171        mScrollView.setVisibility(View.VISIBLE);
1172        mLoadingMessage.setVisibility(View.GONE);
1173        sendAccessibilityEvent();
1174    }
1175
1176    private void sendAccessibilityEvent() {
1177        AccessibilityManager am =
1178            (AccessibilityManager) mActivity.getSystemService(Service.ACCESSIBILITY_SERVICE);
1179        if (!am.isEnabled() || mModel == null) {
1180            return;
1181        }
1182        StringBuilder b = new StringBuilder();
1183        addFieldsRecursive(b, mView);
1184        CharSequence msg = b.toString();
1185
1186        AccessibilityEvent event = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_FOCUSED);
1187        event.setClassName(getClass().getName());
1188        event.setPackageName(mActivity.getPackageName());
1189        event.getText().add(msg);
1190        event.setAddedCount(msg.length());
1191
1192        am.sendAccessibilityEvent(event);
1193    }
1194
1195    private void addFieldsRecursive(StringBuilder b, View v) {
1196        if (v == null || v.getVisibility() != View.VISIBLE) {
1197            return;
1198        }
1199        if (v instanceof TextView) {
1200            CharSequence tv = ((TextView) v).getText();
1201            if (!TextUtils.isEmpty(tv.toString().trim())) {
1202                b.append(tv + PERIOD_SPACE);
1203            }
1204        } else if (v instanceof RadioGroup) {
1205            RadioGroup rg = (RadioGroup) v;
1206            int id = rg.getCheckedRadioButtonId();
1207            if (id != View.NO_ID) {
1208                b.append(((RadioButton) (v.findViewById(id))).getText() + PERIOD_SPACE);
1209            }
1210        } else if (v instanceof Spinner) {
1211            Spinner s = (Spinner) v;
1212            if (s.getSelectedItem() instanceof String) {
1213                String str = ((String) (s.getSelectedItem())).trim();
1214                if (!TextUtils.isEmpty(str)) {
1215                    b.append(str + PERIOD_SPACE);
1216                }
1217            }
1218        } else if (v instanceof ViewGroup) {
1219            ViewGroup vg = (ViewGroup) v;
1220            int children = vg.getChildCount();
1221            for (int i = 0; i < children; i++) {
1222                addFieldsRecursive(b, vg.getChildAt(i));
1223            }
1224        }
1225    }
1226
1227    /**
1228     * Creates a single line string for the time/duration
1229     */
1230    protected void setWhenString() {
1231        String when;
1232        int flags = DateUtils.FORMAT_SHOW_DATE;
1233        String tz = mTimezone;
1234        if (mModel.mAllDay) {
1235            flags |= DateUtils.FORMAT_SHOW_WEEKDAY;
1236            tz = Time.TIMEZONE_UTC;
1237        } else {
1238            flags |= DateUtils.FORMAT_SHOW_TIME;
1239            if (DateFormat.is24HourFormat(mActivity)) {
1240                flags |= DateUtils.FORMAT_24HOUR;
1241            }
1242        }
1243        long startMillis = mStartTime.normalize(true);
1244        long endMillis = mEndTime.normalize(true);
1245        mSB.setLength(0);
1246        when = DateUtils
1247                .formatDateRange(mActivity, mF, startMillis, endMillis, flags, tz).toString();
1248        mWhenView.setText(when);
1249    }
1250
1251    /**
1252     * Configures the Calendars spinner.  This is only done for new events, because only new
1253     * events allow you to select a calendar while editing an event.
1254     * <p>
1255     * We tuck a reference to a Cursor with calendar database data into the spinner, so that
1256     * we can easily extract calendar-specific values when the value changes (the spinner's
1257     * onItemSelected callback is configured).
1258     */
1259    public void setCalendarsCursor(Cursor cursor, boolean userVisible) {
1260        // If there are no syncable calendars, then we cannot allow
1261        // creating a new event.
1262        mCalendarsCursor = cursor;
1263        if (cursor == null || cursor.getCount() == 0) {
1264            // Cancel the "loading calendars" dialog if it exists
1265            if (mSaveAfterQueryComplete) {
1266                mLoadingCalendarsDialog.cancel();
1267            }
1268            if (!userVisible) {
1269                return;
1270            }
1271            // Create an error message for the user that, when clicked,
1272            // will exit this activity without saving the event.
1273            AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
1274            builder.setTitle(R.string.no_syncable_calendars).setIconAttribute(
1275                    android.R.attr.alertDialogIcon).setMessage(R.string.no_calendars_found)
1276                    .setPositiveButton(R.string.add_account, this)
1277                    .setNegativeButton(android.R.string.no, this).setOnCancelListener(this);
1278            mNoCalendarsDialog = builder.show();
1279            return;
1280        }
1281
1282        int defaultCalendarPosition = findDefaultCalendarPosition(cursor);
1283
1284        // populate the calendars spinner
1285        CalendarsAdapter adapter = new CalendarsAdapter(mActivity, cursor);
1286        mCalendarsSpinner.setAdapter(adapter);
1287        mCalendarsSpinner.setSelection(defaultCalendarPosition);
1288        mCalendarsSpinner.setOnItemSelectedListener(this);
1289
1290        if (mSaveAfterQueryComplete) {
1291            mLoadingCalendarsDialog.cancel();
1292            if (prepareForSave() && fillModelFromUI()) {
1293                int exit = userVisible ? Utils.DONE_EXIT : 0;
1294                mDone.setDoneCode(Utils.DONE_SAVE | exit);
1295                mDone.run();
1296            } else if (userVisible) {
1297                mDone.setDoneCode(Utils.DONE_EXIT);
1298                mDone.run();
1299            } else if (Log.isLoggable(TAG, Log.DEBUG)) {
1300                Log.d(TAG, "SetCalendarsCursor:Save failed and unable to exit view");
1301            }
1302            return;
1303        }
1304    }
1305
1306    /**
1307     * Updates the view based on {@link #mModification} and {@link #mModel}
1308     */
1309    public void updateView() {
1310        if (mModel == null) {
1311            return;
1312        }
1313        if (EditEventHelper.canModifyEvent(mModel)) {
1314            setViewStates(mModification);
1315        } else {
1316            setViewStates(Utils.MODIFY_UNINITIALIZED);
1317        }
1318    }
1319
1320    private void setViewStates(int mode) {
1321        // Extra canModify check just in case
1322        if (mode == Utils.MODIFY_UNINITIALIZED || !EditEventHelper.canModifyEvent(mModel)) {
1323            setWhenString();
1324
1325            for (View v : mViewOnlyList) {
1326                v.setVisibility(View.VISIBLE);
1327            }
1328            for (View v : mEditOnlyList) {
1329                v.setVisibility(View.GONE);
1330            }
1331            for (View v : mEditViewList) {
1332                v.setEnabled(false);
1333                v.setBackgroundDrawable(null);
1334            }
1335            mCalendarSelectorGroup.setVisibility(View.GONE);
1336            mCalendarStaticGroup.setVisibility(View.VISIBLE);
1337            mRepeatsSpinner.setEnabled(false);
1338            mRepeatsSpinner.setBackgroundDrawable(null);
1339            setAllDayViewsVisibility(mAllDayCheckBox.isChecked());
1340            if (EditEventHelper.canAddReminders(mModel)) {
1341                mRemindersGroup.setVisibility(View.VISIBLE);
1342            } else {
1343                mRemindersGroup.setVisibility(View.GONE);
1344            }
1345            if (TextUtils.isEmpty(mLocationTextView.getText())) {
1346                mLocationGroup.setVisibility(View.GONE);
1347            }
1348            if (TextUtils.isEmpty(mDescriptionTextView.getText())) {
1349                mDescriptionGroup.setVisibility(View.GONE);
1350            }
1351        } else {
1352            for (View v : mViewOnlyList) {
1353                v.setVisibility(View.GONE);
1354            }
1355            for (View v : mEditOnlyList) {
1356                v.setVisibility(View.VISIBLE);
1357            }
1358            for (View v : mEditViewList) {
1359                v.setEnabled(true);
1360                if (v.getTag() != null) {
1361                    v.setBackgroundDrawable((Drawable) v.getTag());
1362                    v.setPadding(mOriginalPadding[0], mOriginalPadding[1], mOriginalPadding[2],
1363                            mOriginalPadding[3]);
1364                }
1365            }
1366            if (mModel.mUri == null) {
1367                mCalendarSelectorGroup.setVisibility(View.VISIBLE);
1368                mCalendarStaticGroup.setVisibility(View.GONE);
1369            } else {
1370                mCalendarSelectorGroup.setVisibility(View.GONE);
1371                mCalendarStaticGroup.setVisibility(View.VISIBLE);
1372            }
1373            mRepeatsSpinner.setBackgroundDrawable((Drawable) mRepeatsSpinner.getTag());
1374            mRepeatsSpinner.setPadding(mOriginalSpinnerPadding[0], mOriginalSpinnerPadding[1],
1375                    mOriginalSpinnerPadding[2], mOriginalSpinnerPadding[3]);
1376            if (mModel.mOriginalSyncId == null) {
1377                mRepeatsSpinner.setEnabled(true);
1378            } else {
1379                mRepeatsSpinner.setEnabled(false);
1380            }
1381            mRemindersGroup.setVisibility(View.VISIBLE);
1382
1383            mLocationGroup.setVisibility(View.VISIBLE);
1384            mDescriptionGroup.setVisibility(View.VISIBLE);
1385        }
1386    }
1387
1388    public void setModification(int modifyWhich) {
1389        mModification = modifyWhich;
1390        updateView();
1391        updateHomeTime();
1392    }
1393
1394    // Find the calendar position in the cursor that matches calendar in
1395    // preference
1396    private int findDefaultCalendarPosition(Cursor calendarsCursor) {
1397        if (calendarsCursor.getCount() <= 0) {
1398            return -1;
1399        }
1400
1401        String defaultCalendar = Utils.getSharedPreference(
1402                mActivity, GeneralPreferences.KEY_DEFAULT_CALENDAR, null);
1403
1404        if (defaultCalendar == null) {
1405            return 0;
1406        }
1407        int calendarsOwnerColumn = calendarsCursor.getColumnIndexOrThrow(Calendars.OWNER_ACCOUNT);
1408        int position = 0;
1409        calendarsCursor.moveToPosition(-1);
1410        while (calendarsCursor.moveToNext()) {
1411            if (defaultCalendar.equals(calendarsCursor.getString(calendarsOwnerColumn))) {
1412                return position;
1413            }
1414            position++;
1415        }
1416        return 0;
1417    }
1418
1419    private void updateAttendees(HashMap<String, Attendee> attendeesList) {
1420        if (attendeesList == null || attendeesList.isEmpty()) {
1421            return;
1422        }
1423        mAttendeesList.setText(null);
1424        for (Attendee attendee : attendeesList.values()) {
1425            mAttendeesList.append(attendee.mEmail);
1426        }
1427    }
1428
1429    private void updateRemindersVisibility(int numReminders) {
1430        if (numReminders == 0) {
1431            mRemindersContainer.setVisibility(View.GONE);
1432        } else {
1433            mRemindersContainer.setVisibility(View.VISIBLE);
1434        }
1435    }
1436
1437    /**
1438     * Add a new reminder when the user hits the "add reminder" button.  We use the default
1439     * reminder time and method.
1440     */
1441    private void addReminder() {
1442        // TODO: when adding a new reminder, make it different from the
1443        // last one in the list (if any).
1444        if (mDefaultReminderMinutes == GeneralPreferences.NO_REMINDER) {
1445            EventViewUtils.addReminder(mActivity, mScrollView, this, mReminderItems,
1446                    mReminderMinuteValues, mReminderMinuteLabels,
1447                    mReminderMethodValues, mReminderMethodLabels,
1448                    ReminderEntry.valueOf(GeneralPreferences.REMINDER_DEFAULT_TIME),
1449                    mModel.mCalendarMaxReminders, null);
1450        } else {
1451            EventViewUtils.addReminder(mActivity, mScrollView, this, mReminderItems,
1452                    mReminderMinuteValues, mReminderMinuteLabels,
1453                    mReminderMethodValues, mReminderMethodLabels,
1454                    ReminderEntry.valueOf(mDefaultReminderMinutes),
1455                    mModel.mCalendarMaxReminders, null);
1456        }
1457        updateRemindersVisibility(mReminderItems.size());
1458    }
1459
1460    // From com.google.android.gm.ComposeActivity
1461    private MultiAutoCompleteTextView initMultiAutoCompleteTextView(RecipientEditTextView list) {
1462        if (ChipsUtil.supportsChipsUi()) {
1463            mAddressAdapter = new RecipientAdapter(mActivity);
1464            list.setAdapter((BaseRecipientAdapter) mAddressAdapter);
1465            list.setOnFocusListShrinkRecipients(false);
1466        } else {
1467            mAddressAdapter = new EmailAddressAdapter(mActivity);
1468            list.setAdapter((EmailAddressAdapter)mAddressAdapter);
1469        }
1470        list.setTokenizer(new Rfc822Tokenizer());
1471        list.setValidator(mEmailValidator);
1472
1473        // NOTE: assumes no other filters are set
1474        list.setFilters(sRecipientFilters);
1475
1476        return list;
1477    }
1478
1479    /**
1480     * From com.google.android.gm.ComposeActivity Implements special address
1481     * cleanup rules: The first space key entry following an "@" symbol that is
1482     * followed by any combination of letters and symbols, including one+ dots
1483     * and zero commas, should insert an extra comma (followed by the space).
1484     */
1485    private static InputFilter[] sRecipientFilters = new InputFilter[] { new Rfc822InputFilter() };
1486
1487    private void setDate(TextView view, long millis) {
1488        int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR
1489                | DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_ABBREV_MONTH
1490                | DateUtils.FORMAT_ABBREV_WEEKDAY;
1491
1492        // Unfortunately, DateUtils doesn't support a timezone other than the
1493        // default timezone provided by the system, so we have this ugly hack
1494        // here to trick it into formatting our time correctly. In order to
1495        // prevent all sorts of craziness, we synchronize on the TimeZone class
1496        // to prevent other threads from reading an incorrect timezone from
1497        // calls to TimeZone#getDefault()
1498        // TODO fix this if/when DateUtils allows for passing in a timezone
1499        String dateString;
1500        synchronized (TimeZone.class) {
1501            TimeZone.setDefault(TimeZone.getTimeZone(mTimezone));
1502            dateString = DateUtils.formatDateTime(mActivity, millis, flags);
1503            // setting the default back to null restores the correct behavior
1504            TimeZone.setDefault(null);
1505        }
1506        view.setText(dateString);
1507    }
1508
1509    private void setTime(TextView view, long millis) {
1510        int flags = DateUtils.FORMAT_SHOW_TIME;
1511        if (DateFormat.is24HourFormat(mActivity)) {
1512            flags |= DateUtils.FORMAT_24HOUR;
1513        }
1514
1515        // Unfortunately, DateUtils doesn't support a timezone other than the
1516        // default timezone provided by the system, so we have this ugly hack
1517        // here to trick it into formatting our time correctly. In order to
1518        // prevent all sorts of craziness, we synchronize on the TimeZone class
1519        // to prevent other threads from reading an incorrect timezone from
1520        // calls to TimeZone#getDefault()
1521        // TODO fix this if/when DateUtils allows for passing in a timezone
1522        String timeString;
1523        synchronized (TimeZone.class) {
1524            TimeZone.setDefault(TimeZone.getTimeZone(mTimezone));
1525            timeString = DateUtils.formatDateTime(mActivity, millis, flags);
1526            TimeZone.setDefault(null);
1527        }
1528        view.setText(timeString);
1529    }
1530
1531    private void setTimezone(int i) {
1532        if (i < 0 || i >= mTimezoneAdapter.getCount()) {
1533            return; // do nothing
1534        }
1535        TimezoneRow timezone = mTimezoneAdapter.getItem(i);
1536        mTimezoneTextView.setText(timezone.toString());
1537        mTimezoneButton.setText(timezone.toString());
1538        mTimezone = timezone.mId;
1539        mStartTime.timezone = mTimezone;
1540        mStartTime.normalize(true);
1541        mEndTime.timezone = mTimezone;
1542        mEndTime.normalize(true);
1543        mTimezoneAdapter.setCurrentTimezone(mTimezone);
1544    }
1545
1546    /**
1547     * @param isChecked
1548     */
1549    protected void setAllDayViewsVisibility(boolean isChecked) {
1550        if (isChecked) {
1551            if (mEndTime.hour == 0 && mEndTime.minute == 0) {
1552                mEndTime.monthDay--;
1553                long endMillis = mEndTime.normalize(true);
1554
1555                // Do not allow an event to have an end time
1556                // before the
1557                // start time.
1558                if (mEndTime.before(mStartTime)) {
1559                    mEndTime.set(mStartTime);
1560                    endMillis = mEndTime.normalize(true);
1561                }
1562                setDate(mEndDateButton, endMillis);
1563                setTime(mEndTimeButton, endMillis);
1564            }
1565
1566            mStartTimeButton.setVisibility(View.GONE);
1567            mEndTimeButton.setVisibility(View.GONE);
1568            mTimezoneRow.setVisibility(View.GONE);
1569        } else {
1570            if (mEndTime.hour == 0 && mEndTime.minute == 0) {
1571                mEndTime.monthDay++;
1572                long endMillis = mEndTime.normalize(true);
1573                setDate(mEndDateButton, endMillis);
1574                setTime(mEndTimeButton, endMillis);
1575            }
1576            mStartTimeButton.setVisibility(View.VISIBLE);
1577            mEndTimeButton.setVisibility(View.VISIBLE);
1578            mTimezoneRow.setVisibility(View.VISIBLE);
1579        }
1580        updateHomeTime();
1581    }
1582
1583    @Override
1584    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1585        // This is only used for the Calendar spinner in new events, and only fires when the
1586        // calendar selection changes or on screen rotation
1587        Cursor c = (Cursor) parent.getItemAtPosition(position);
1588        if (c == null) {
1589            // TODO: can this happen? should we drop this check?
1590            Log.w(TAG, "Cursor not set on calendar item");
1591            return;
1592        }
1593
1594        int colorColumn = c.getColumnIndexOrThrow(Calendars.CALENDAR_COLOR);
1595        int color = c.getInt(colorColumn);
1596        int displayColor = Utils.getDisplayColorFromColor(color);
1597
1598        if (mIsMultipane) {
1599            mCalendarSelectorWrapper.setBackgroundColor(displayColor);
1600        } else {
1601            mCalendarSelectorGroup.setBackgroundColor(displayColor);
1602        }
1603
1604        // Do nothing if the selection didn't change so that reminders will not get lost
1605        int idColumn = c.getColumnIndexOrThrow(Calendars._ID);
1606        long calendarId = c.getLong(idColumn);
1607        if (calendarId == mModel.mCalendarId) {
1608            return;
1609        }
1610        mModel.mCalendarId = calendarId;
1611        mModel.mCalendarColor = color;
1612        // Update the max/allowed reminders with the new calendar properties.
1613        int maxRemindersColumn = c.getColumnIndexOrThrow(Calendars.MAX_REMINDERS);
1614        mModel.mCalendarMaxReminders = c.getInt(maxRemindersColumn);
1615        int allowedRemindersColumn = c.getColumnIndexOrThrow(Calendars.ALLOWED_REMINDERS);
1616        mModel.mCalendarAllowedReminders = c.getString(allowedRemindersColumn);
1617        int allowedAttendeeTypesColumn = c.getColumnIndexOrThrow(Calendars.ALLOWED_ATTENDEE_TYPES);
1618        mModel.mCalendarAllowedAttendeeTypes = c.getString(allowedAttendeeTypesColumn);
1619        int allowedAvailabilityColumn = c.getColumnIndexOrThrow(Calendars.ALLOWED_AVAILABILITY);
1620        mModel.mCalendarAllowedAvailability = c.getString(allowedAvailabilityColumn);
1621
1622        // Discard the current reminders and replace them with the model's default reminder set.
1623        // We could attempt to save & restore the reminders that have been added, but that's
1624        // probably more trouble than it's worth.
1625        mModel.mReminders.clear();
1626        mModel.mReminders.addAll(mModel.mDefaultReminders);
1627        mModel.mHasAlarm = mModel.mReminders.size() != 0;
1628
1629        // Update the UI elements.
1630        mReminderItems.clear();
1631        LinearLayout reminderLayout =
1632            (LinearLayout) mScrollView.findViewById(R.id.reminder_items_container);
1633        reminderLayout.removeAllViews();
1634        prepareReminders();
1635        prepareAvailability();
1636    }
1637
1638    /**
1639     * Checks if the start and end times for this event should be displayed in
1640     * the Calendar app's time zone as well and formats and displays them.
1641     */
1642    private void updateHomeTime() {
1643        String tz = Utils.getTimeZone(mActivity, null);
1644        if (!mAllDayCheckBox.isChecked() && !TextUtils.equals(tz, mTimezone)
1645                && mModification != EditEventHelper.MODIFY_UNINITIALIZED) {
1646            int flags = DateUtils.FORMAT_SHOW_TIME;
1647            boolean is24Format = DateFormat.is24HourFormat(mActivity);
1648            if (is24Format) {
1649                flags |= DateUtils.FORMAT_24HOUR;
1650            }
1651            long millisStart = mStartTime.toMillis(false);
1652            long millisEnd = mEndTime.toMillis(false);
1653
1654            boolean isDSTStart = mStartTime.isDst != 0;
1655            boolean isDSTEnd = mEndTime.isDst != 0;
1656
1657            // First update the start date and times
1658            String tzDisplay = TimeZone.getTimeZone(tz).getDisplayName(
1659                    isDSTStart, TimeZone.SHORT, Locale.getDefault());
1660            StringBuilder time = new StringBuilder();
1661
1662            mSB.setLength(0);
1663            time.append(DateUtils
1664                    .formatDateRange(mActivity, mF, millisStart, millisStart, flags, tz))
1665                    .append(" ").append(tzDisplay);
1666            mStartTimeHome.setText(time.toString());
1667
1668            flags = DateUtils.FORMAT_ABBREV_ALL | DateUtils.FORMAT_SHOW_DATE
1669                    | DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_SHOW_WEEKDAY;
1670            mSB.setLength(0);
1671            mStartDateHome
1672                    .setText(DateUtils.formatDateRange(
1673                            mActivity, mF, millisStart, millisStart, flags, tz).toString());
1674
1675            // Make any adjustments needed for the end times
1676            if (isDSTEnd != isDSTStart) {
1677                tzDisplay = TimeZone.getTimeZone(tz).getDisplayName(
1678                        isDSTEnd, TimeZone.SHORT, Locale.getDefault());
1679            }
1680            flags = DateUtils.FORMAT_SHOW_TIME;
1681            if (is24Format) {
1682                flags |= DateUtils.FORMAT_24HOUR;
1683            }
1684
1685            // Then update the end times
1686            time.setLength(0);
1687            mSB.setLength(0);
1688            time.append(DateUtils.formatDateRange(
1689                    mActivity, mF, millisEnd, millisEnd, flags, tz)).append(" ").append(tzDisplay);
1690            mEndTimeHome.setText(time.toString());
1691
1692            flags = DateUtils.FORMAT_ABBREV_ALL | DateUtils.FORMAT_SHOW_DATE
1693                    | DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_SHOW_WEEKDAY;
1694            mSB.setLength(0);
1695            mEndDateHome.setText(DateUtils.formatDateRange(
1696                            mActivity, mF, millisEnd, millisEnd, flags, tz).toString());
1697
1698            mStartHomeGroup.setVisibility(View.VISIBLE);
1699            mEndHomeGroup.setVisibility(View.VISIBLE);
1700        } else {
1701            mStartHomeGroup.setVisibility(View.GONE);
1702            mEndHomeGroup.setVisibility(View.GONE);
1703        }
1704    }
1705
1706    @Override
1707    public void onNothingSelected(AdapterView<?> parent) {
1708    }
1709}
1710