diff options
Diffstat (limited to 'src/com/android/calendar/EditEvent.java')
-rw-r--r-- | src/com/android/calendar/EditEvent.java | 1456 |
1 files changed, 1456 insertions, 0 deletions
diff --git a/src/com/android/calendar/EditEvent.java b/src/com/android/calendar/EditEvent.java new file mode 100644 index 00000000..8c11974b --- /dev/null +++ b/src/com/android/calendar/EditEvent.java @@ -0,0 +1,1456 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.calendar; + +import static android.provider.Calendar.EVENT_BEGIN_TIME; +import static android.provider.Calendar.EVENT_END_TIME; +import android.app.Activity; +import android.app.AlertDialog; +import android.app.DatePickerDialog; +import android.app.TimePickerDialog; +import android.app.DatePickerDialog.OnDateSetListener; +import android.app.TimePickerDialog.OnTimeSetListener; +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.DialogInterface.OnCancelListener; +import android.content.res.Resources; +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.pim.DateFormat; +import android.pim.DateUtils; +import android.pim.EventRecurrence; +import android.pim.Time; +import android.preference.PreferenceManager; +import android.provider.Calendar.Calendars; +import android.provider.Calendar.Events; +import android.provider.Calendar.Reminders; +import android.text.TextUtils; +import android.util.Log; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.DatePicker; +import android.widget.ImageButton; +import android.widget.LinearLayout; +import android.widget.ResourceCursorAdapter; +import android.widget.Spinner; +import android.widget.TextView; +import android.widget.TimePicker; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; + +public class EditEvent extends Activity implements View.OnClickListener { + /** + * This is the symbolic name for the key used to pass in the boolean + * for creating all-day events that is part of the extra data of the intent. + * This is used only for creating new events and is set to true if + * the default for the new event should be an all-day event. + */ + public static final String EVENT_ALL_DAY = "allDay"; + + private static final int MAX_REMINDERS = 5; + + private static final int MENU_GROUP_REMINDER = 1; + private static final int MENU_GROUP_SHOW_OPTIONS = 2; + private static final int MENU_GROUP_HIDE_OPTIONS = 3; + + private static final int MENU_ADD_REMINDER = 1; + private static final int MENU_SHOW_EXTRA_OPTIONS = 2; + private static final int MENU_HIDE_EXTRA_OPTIONS = 3; + + private static final String[] EVENT_PROJECTION = new String[] { + Events._ID, // 0 + Events.TITLE, // 1 + Events.DESCRIPTION, // 2 + Events.EVENT_LOCATION, // 3 + Events.ALL_DAY, // 4 + Events.HAS_ALARM, // 5 + Events.CALENDAR_ID, // 6 + Events.DTSTART, // 7 + Events.DURATION, // 8 + Events.EVENT_TIMEZONE, // 9 + Events.RRULE, // 10 + Events._SYNC_ID, // 11 + Events.TRANSPARENCY, // 12 + Events.VISIBILITY, // 13 + }; + private static final int EVENT_INDEX_ID = 0; + private static final int EVENT_INDEX_TITLE = 1; + private static final int EVENT_INDEX_DESCRIPTION = 2; + private static final int EVENT_INDEX_EVENT_LOCATION = 3; + private static final int EVENT_INDEX_ALL_DAY = 4; + private static final int EVENT_INDEX_HAS_ALARM = 5; + private static final int EVENT_INDEX_CALENDAR_ID = 6; + private static final int EVENT_INDEX_DTSTART = 7; + private static final int EVENT_INDEX_DURATION = 8; + private static final int EVENT_INDEX_TIMEZONE = 9; + private static final int EVENT_INDEX_RRULE = 10; + private static final int EVENT_INDEX_SYNC_ID = 11; + private static final int EVENT_INDEX_TRANSPARENCY = 12; + private static final int EVENT_INDEX_VISIBILITY = 13; + + private static final String[] CALENDARS_PROJECTION = new String[] { + Calendars._ID, // 0 + Calendars.DISPLAY_NAME, // 1 + Calendars.TIMEZONE, // 2 + }; + private static final int CALENDARS_INDEX_DISPLAY_NAME = 1; + private static final int CALENDARS_INDEX_TIMEZONE = 2; + private static final String CALENDARS_WHERE = Calendars.ACCESS_LEVEL + ">=" + + Calendars.CONTRIBUTOR_ACCESS; + + private static final String[] REMINDERS_PROJECTION = new String[] { + Reminders._ID, // 0 + Reminders.MINUTES, // 1 + }; + private static final int REMINDERS_INDEX_MINUTES = 1; + private static final String REMINDERS_WHERE = Reminders.EVENT_ID + "=%d AND (" + + Reminders.METHOD + "=" + Reminders.METHOD_ALERT + " OR " + Reminders.METHOD + "=" + + Reminders.METHOD_DEFAULT + ")"; + + private static final int DOES_NOT_REPEAT = 0; + private static final int REPEATS_DAILY = 1; + private static final int REPEATS_EVERY_WEEKDAY = 2; + private static final int REPEATS_WEEKLY_ON_DAY = 3; + private static final int REPEATS_MONTHLY_ON_DAY_COUNT = 4; + private static final int REPEATS_MONTHLY_ON_DAY = 5; + private static final int REPEATS_YEARLY = 6; + private static final int REPEATS_CUSTOM = 7; + + private static final int MODIFY_UNINITIALIZED = 0; + private static final int MODIFY_SELECTED = 1; + private static final int MODIFY_ALL = 2; + private static final int MODIFY_ALL_FOLLOWING = 3; + + private int mFirstDayOfWeek; // cached in onCreate + private Uri mUri; + private Cursor mEventCursor; + private Cursor mCalendarsCursor; + + private Button mStartDateButton; + private Button mEndDateButton; + private Button mStartTimeButton; + private Button mEndTimeButton; + private Button mSaveButton; + private Button mDeleteButton; + private Button mDiscardButton; + private CheckBox mAllDayCheckBox; + private Spinner mCalendarsSpinner; + private Spinner mRepeatsSpinner; + private Spinner mAvailabilitySpinner; + private Spinner mVisibilitySpinner; + private TextView mTitleTextView; + private TextView mLocationTextView; + private TextView mDescriptionTextView; + private View mRemindersSeparator; + private LinearLayout mRemindersContainer; + private LinearLayout mExtraOptions; + private ArrayList<Integer> mOriginalMinutes = new ArrayList<Integer>(); + private ArrayList<LinearLayout> mReminderItems = new ArrayList<LinearLayout>(0); + + private EventRecurrence mEventRecurrence = new EventRecurrence(); + private String mRrule; + private ContentValues mInitialValues; + + /** + * If the repeating event is created on the phone and it hasn't been + * synced yet to the web server, then there is a bug where you can't + * delete or change an instance of the repeating event. This case + * can be detected with mSyncId. If mSyncId == null, then the repeating + * event has not been synced to the phone, in which case we won't allow + * the user to change one instance. + */ + private String mSyncId; + + private ArrayList<Integer> mRecurrenceIndexes = new ArrayList<Integer> (0); + private ArrayList<Integer> mReminderValues; + private ArrayList<String> mReminderLabels; + + private Time mStartTime; + private Time mEndTime; + private int mModification = MODIFY_UNINITIALIZED; + private int mDefaultReminderMinutes; + + private DeleteEventHelper mDeleteEventHelper; + + /* This class is used to update the time buttons. */ + private class TimeListener implements OnTimeSetListener { + private View mView; + + public TimeListener(View view) { + mView = view; + } + + public void onTimeSet(TimePicker view, int hourOfDay, int minute) { + // Cache the member variables locally to avoid inner class overhead. + Time startTime = mStartTime; + Time endTime = mEndTime; + + // Cache the start and end millis so that we limit the number + // of calls to normalize() and toMillis(), which are fairly + // expensive. + long startMillis; + long endMillis; + if (mView == mStartTimeButton) { + // The start time was changed. + int hourDuration = endTime.hour - startTime.hour; + int minuteDuration = endTime.minute - startTime.minute; + + startTime.hour = hourOfDay; + startTime.minute = minute; + startMillis = startTime.normalize(true); + + // Also update the end time to keep the duration constant. + endTime.hour = hourOfDay + hourDuration; + endTime.minute = minute + minuteDuration; + endMillis = endTime.normalize(true); + } else { + // The end time was changed. + startMillis = startTime.toMillis(true); + endTime.hour = hourOfDay; + endTime.minute = minute; + endMillis = endTime.normalize(true); + + // Do not allow an event to have an end time before the start time. + if (endTime.before(startTime)) { + endTime.set(startTime); + endMillis = startMillis; + } + } + + setDate(mEndDateButton, endMillis); + setTime(mStartTimeButton, startMillis); + setTime(mEndTimeButton, endMillis); + } + } + + private class TimeClickListener implements View.OnClickListener { + private Time mTime; + + public TimeClickListener(Time time) { + mTime = time; + } + + public void onClick(View v) { + new TimePickerDialog(EditEvent.this, new TimeListener(v), + mTime.hour, mTime.minute, + DateFormat.is24HourFormat(EditEvent.this)).show(); + } + } + + private class DateListener implements OnDateSetListener { + View mView; + + public DateListener(View view) { + mView = view; + } + + public void onDateSet(DatePicker view, int year, int month, int monthDay) { + // Cache the member variables locally to avoid inner class overhead. + Time startTime = mStartTime; + Time endTime = mEndTime; + + // Cache the start and end millis so that we limit the number + // of calls to normalize() and toMillis(), which are fairly + // expensive. + long startMillis; + long endMillis; + if (mView == mStartDateButton) { + // The start date was changed. + int yearDuration = endTime.year - startTime.year; + int monthDuration = endTime.month - startTime.month; + int monthDayDuration = endTime.monthDay - startTime.monthDay; + + startTime.year = year; + startTime.month = month; + startTime.monthDay = monthDay; + startMillis = startTime.normalize(true); + + // Also update the end date to keep the duration constant. + endTime.year = year + yearDuration; + endTime.month = month + monthDuration; + endTime.monthDay = monthDay + monthDayDuration; + endMillis = endTime.normalize(true); + + // If the start date has changed then update the repeats. + populateRepeats(); + } else { + // The end date was changed. + startMillis = startTime.toMillis(true); + endTime.year = year; + endTime.month = month; + endTime.monthDay = monthDay; + endMillis = endTime.normalize(true); + + // Do not allow an event to have an end time before the start time. + if (endTime.before(startTime)) { + endTime.set(startTime); + endMillis = startMillis; + } + } + + setDate(mStartDateButton, startMillis); + setDate(mEndDateButton, endMillis); + setTime(mEndTimeButton, endMillis); // In case end time had to be reset + } + } + + private class DateClickListener implements View.OnClickListener { + private Time mTime; + + public DateClickListener(Time time) { + mTime = time; + } + + public void onClick(View v) { + new DatePickerDialog(EditEvent.this, new DateListener(v), mTime.year, + mTime.month, mTime.monthDay).show(); + } + } + + private class CalendarsAdapter extends ResourceCursorAdapter { + public CalendarsAdapter(Context context, Cursor c) { + super(context, R.layout.calendars_item, c); + setDropDownViewResource(R.layout.calendars_dropdown_item); + } + + @Override + public void bindView(View view, Context context, Cursor cursor) { + TextView name = (TextView) view.findViewById(R.id.calendar_name); + name.setText(cursor.getString(CALENDARS_INDEX_DISPLAY_NAME)); + } + } + + // This is called if the user clicks on one of the buttons: "Save", + // "Discard", or "Delete". This is also called if the user clicks + // on the "remove reminder" button. + public void onClick(View v) { + if (v == mSaveButton) { + save(); + finish(); + return; + } + + if (v == mDeleteButton) { + long begin = mStartTime.toMillis(false /* use isDst */); + long end = mEndTime.toMillis(false /* use isDst */); + int which = -1; + switch (mModification) { + case MODIFY_SELECTED: + which = DeleteEventHelper.DELETE_SELECTED; + break; + case MODIFY_ALL_FOLLOWING: + which = DeleteEventHelper.DELETE_ALL_FOLLOWING; + break; + case MODIFY_ALL: + which = DeleteEventHelper.DELETE_ALL; + break; + } + mDeleteEventHelper.delete(begin, end, mEventCursor, which); + return; + } + + if (v == mDiscardButton) { + finish(); + return; + } + + // This must be a click on one of the "remove reminder" buttons + LinearLayout reminderItem = (LinearLayout) v.getParent(); + LinearLayout parent = (LinearLayout) reminderItem.getParent(); + parent.removeView(reminderItem); + mReminderItems.remove(reminderItem); + updateRemindersVisibility(); + } + + @Override + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + setContentView(R.layout.edit_event); + + mFirstDayOfWeek = Calendar.getInstance().getFirstDayOfWeek(); + + mStartTime = new Time(); + mEndTime = new Time(); + + Intent intent = getIntent(); + mUri = intent.getData(); + + if (mUri != null) { + mEventCursor = managedQuery(mUri, EVENT_PROJECTION, null, null); + } + + long begin = intent.getLongExtra(EVENT_BEGIN_TIME, 0); + long end = intent.getLongExtra(EVENT_END_TIME, 0); + + boolean allDay = false; + if (mEventCursor != null) { + // The event already exists so fetch the all-day status + mEventCursor.moveToFirst(); + allDay = mEventCursor.getInt(EVENT_INDEX_ALL_DAY) != 0; + String rrule = mEventCursor.getString(EVENT_INDEX_RRULE); + String timezone = mEventCursor.getString(EVENT_INDEX_TIMEZONE); + + // Remember the initial values + mInitialValues = new ContentValues(); + mInitialValues.put(EVENT_BEGIN_TIME, begin); + mInitialValues.put(EVENT_END_TIME, end); + mInitialValues.put(Events.ALL_DAY, allDay); + mInitialValues.put(Events.RRULE, rrule); + mInitialValues.put(Events.EVENT_TIMEZONE, timezone); + } else { + // We are creating a new event, so set the default from the + // intent (if specified). + allDay = intent.getBooleanExtra(EVENT_ALL_DAY, false); + } + + // If the event is all-day, read the times in UTC timezone + if (begin != 0) { + if (allDay) { + String tz = mStartTime.timezone; + mStartTime.timezone = Time.TIMEZONE_UTC; + mStartTime.set(begin); + mStartTime.timezone = tz; + + // Calling normalize to calculate isDst + mStartTime.normalize(true); + } else { + mStartTime.set(begin); + } + } + + if (end != 0) { + if (allDay) { + String tz = mStartTime.timezone; + mEndTime.timezone = Time.TIMEZONE_UTC; + mEndTime.set(end); + mEndTime.timezone = tz; + + // Calling normalize to calculate isDst + mEndTime.normalize(true); + } else { + mEndTime.set(end); + } + } + + mCalendarsCursor = managedQuery(Calendars.CONTENT_URI, CALENDARS_PROJECTION, + CALENDARS_WHERE, null); + + // cache all the widgets + mTitleTextView = (TextView) findViewById(R.id.title); + mLocationTextView = (TextView) findViewById(R.id.location); + mDescriptionTextView = (TextView) findViewById(R.id.description); + mStartDateButton = (Button) findViewById(R.id.start_date); + mEndDateButton = (Button) findViewById(R.id.end_date); + mStartTimeButton = (Button) findViewById(R.id.start_time); + mEndTimeButton = (Button) findViewById(R.id.end_time); + mAllDayCheckBox = (CheckBox) findViewById(R.id.is_all_day); + mCalendarsSpinner = (Spinner) findViewById(R.id.calendars); + mRepeatsSpinner = (Spinner) findViewById(R.id.repeats); + mAvailabilitySpinner = (Spinner) findViewById(R.id.availability); + mVisibilitySpinner = (Spinner) findViewById(R.id.visibility); + mRemindersSeparator = findViewById(R.id.reminders_separator); + mRemindersContainer = (LinearLayout) findViewById(R.id.reminders_container); + mExtraOptions = (LinearLayout) findViewById(R.id.extra_options_container); + + mAllDayCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if (isChecked) { + if (mEndTime.hour == 0 && mEndTime.minute == 0) { + mEndTime.monthDay--; + long endMillis = mEndTime.normalize(true); + + // Do not allow an event to have an end time before the start time. + if (mEndTime.before(mStartTime)) { + mEndTime.set(mStartTime); + endMillis = mEndTime.normalize(true); + } + setDate(mEndDateButton, endMillis); + setTime(mEndTimeButton, endMillis); + } + + mStartTimeButton.setVisibility(View.GONE); + mEndTimeButton.setVisibility(View.GONE); + } else { + if (mEndTime.hour == 0 && mEndTime.minute == 0) { + mEndTime.monthDay++; + long endMillis = mEndTime.normalize(true); + setDate(mEndDateButton, endMillis); + setTime(mEndTimeButton, endMillis); + } + + mStartTimeButton.setVisibility(View.VISIBLE); + mEndTimeButton.setVisibility(View.VISIBLE); + } + } + }); + + if (allDay) { + mAllDayCheckBox.setChecked(true); + } else { + mAllDayCheckBox.setChecked(false); + } + + mSaveButton = (Button) findViewById(R.id.save); + mSaveButton.setOnClickListener(this); + + mDeleteButton = (Button) findViewById(R.id.delete); + mDeleteButton.setOnClickListener(this); + + mDiscardButton = (Button) findViewById(R.id.discard); + mDiscardButton.setOnClickListener(this); + + // Initialize the reminder values array. + Resources r = getResources(); + String[] strings = r.getStringArray(R.array.reminder_minutes_values); + int size = strings.length; + ArrayList<Integer> list = new ArrayList<Integer>(size); + for (int i = 0 ; i < size ; i++) { + list.add(Integer.parseInt(strings[i])); + } + mReminderValues = list; + String[] labels = r.getStringArray(R.array.reminder_minutes_labels); + mReminderLabels = new ArrayList<String>(Arrays.asList(labels)); + + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); + String durationString = + prefs.getString(CalendarPreferenceActivity.KEY_DEFAULT_REMINDER, "0"); + mDefaultReminderMinutes = Integer.parseInt(durationString); + + // Reminders cursor + boolean hasAlarm = (mEventCursor != null) + && (mEventCursor.getInt(EVENT_INDEX_HAS_ALARM) != 0); + if (hasAlarm) { + Uri uri = Reminders.CONTENT_URI; + long eventId = mEventCursor.getLong(EVENT_INDEX_ID); + String where = String.format(REMINDERS_WHERE, eventId); + ContentResolver cr = getContentResolver(); + Cursor reminderCursor = cr.query(uri, REMINDERS_PROJECTION, where, null, null); + try { + // First pass: collect all the custom reminder minutes (e.g., + // a reminder of 8 minutes) into a global list. + while (reminderCursor.moveToNext()) { + int minutes = reminderCursor.getInt(REMINDERS_INDEX_MINUTES); + EditEvent.addMinutesToList(this, mReminderValues, mReminderLabels, minutes); + } + + // Second pass: create the reminder spinners + reminderCursor.moveToPosition(-1); + while (reminderCursor.moveToNext()) { + int minutes = reminderCursor.getInt(REMINDERS_INDEX_MINUTES); + mOriginalMinutes.add(minutes); + EditEvent.addReminder(this, this, mReminderItems, mReminderValues, + mReminderLabels, minutes); + } + } finally { + reminderCursor.close(); + } + } + updateRemindersVisibility(); + + mDeleteEventHelper = new DeleteEventHelper(this, true /* exit when done */); + } + + @Override + protected void onResume() { + super.onResume(); + + // populate the calendars spinner + mCalendarsSpinner = (Spinner) findViewById(R.id.calendars); + CalendarsAdapter adapter = new CalendarsAdapter(this, mCalendarsCursor); + mCalendarsSpinner.setAdapter(adapter); + + if (mEventCursor != null) { + Cursor cursor = mEventCursor; + cursor.moveToFirst(); + + mRrule = cursor.getString(EVENT_INDEX_RRULE); + + String title = cursor.getString(EVENT_INDEX_TITLE); + String description = cursor.getString(EVENT_INDEX_DESCRIPTION); + String location = cursor.getString(EVENT_INDEX_EVENT_LOCATION); + long calendarId = cursor.getLong(EVENT_INDEX_CALENDAR_ID); + int availability = cursor.getInt(EVENT_INDEX_TRANSPARENCY); + int visibility = cursor.getInt(EVENT_INDEX_VISIBILITY); + if (visibility > 0) { + // For now we the array contains the values 0, 2, and 3. We subtract one to match. + visibility--; + } + + if (!TextUtils.isEmpty(mRrule) && mModification == MODIFY_UNINITIALIZED) { + // If this event has not been synced, then don't allow deleting + // or changing a single instance. + mSyncId = cursor.getString(EVENT_INDEX_SYNC_ID); + mEventRecurrence.parse(mRrule); + + // If we haven't synced this repeating event yet, then don't + // allow the user to change just one instance. + int itemIndex = 0; + CharSequence[] items; + if (mSyncId == null) { + items = new CharSequence[2]; + } else { + items = new CharSequence[3]; + items[itemIndex++] = getText(R.string.modify_event); + } + items[itemIndex++] = getText(R.string.modify_all); + items[itemIndex++] = getText(R.string.modify_all_following); + + // Display the modification dialog. + new AlertDialog.Builder(this) + .setOnCancelListener(new OnCancelListener() { + public void onCancel(DialogInterface dialog) { + finish(); + } + }) + .setTitle(R.string.edit_event_label) + .setItems(items, new OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + if (which == 0) { + mModification = + (mSyncId == null) ? MODIFY_ALL : MODIFY_SELECTED; + } else if (which == 1) { + mModification = + (mSyncId == null) ? MODIFY_ALL_FOLLOWING : MODIFY_ALL; + } else if (which == 2) { + mModification = MODIFY_ALL_FOLLOWING; + } + + // If we are modifying all the events in a + // series then disable and ignore the date. + if (mModification == MODIFY_ALL) { + mStartDateButton.setEnabled(false); + mEndDateButton.setEnabled(false); + } else if (mModification == MODIFY_SELECTED) { + mRepeatsSpinner.setEnabled(false); + } else { + // We could allow changing the Rrule for + // all following instances but we'll + // keep it simple for now. + mRepeatsSpinner.setEnabled(false); + } + } + }) + .show(); + } + + mTitleTextView.setText(title); + mLocationTextView.setText(location); + mDescriptionTextView.setText(description); + mAvailabilitySpinner.setSelection(availability); + mVisibilitySpinner.setSelection(visibility); + + // If there is a calendarId set, move the spinner to the proper + // position and hide the spinner, since this is an existing event. + if (calendarId != -1) { + int count = adapter.getCount(); + for (int pos = 0 ; pos < count ; pos++) { + long rowID = adapter.getItemId(pos); + if (rowID == calendarId) { + mCalendarsSpinner.setSelection(pos); + } + } + } + View calendarSeparator = findViewById(R.id.calendar_separator); + View calendarLabel = findViewById(R.id.calendar_label); + calendarSeparator.setVisibility(View.GONE); + calendarLabel.setVisibility(View.GONE); + mCalendarsSpinner.setVisibility(View.GONE); + } else if (Time.isEpoch(mStartTime) && Time.isEpoch(mEndTime)) { + mStartTime.setToNow(); + + // Round the time to the nearest half hour. + mStartTime.second = 0; + int minute = mStartTime.minute; + if (minute > 0 && minute <= 30) { + mStartTime.minute = 30; + } else { + mStartTime.minute = 0; + mStartTime.hour += 1; + } + + long startMillis = mStartTime.normalize(true /* ignore isDst */); + mEndTime.set(startMillis + DateUtils.HOUR_IN_MILLIS); + } else { + // New event - set the default reminder + if (mDefaultReminderMinutes != 0) { + addReminder(this, this, mReminderItems, mReminderValues, + mReminderLabels, mDefaultReminderMinutes); + } + + // Hide delete button + mDeleteButton.setVisibility(View.GONE); + } + + updateRemindersVisibility(); + populateWhen(); + populateRepeats(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuItem item; + item = menu.add(MENU_GROUP_REMINDER, MENU_ADD_REMINDER, 0, + R.string.add_new_reminder); + item.setIcon(R.drawable.ic_menu_reminder); + item.setAlphabeticShortcut('r'); + + item = menu.add(MENU_GROUP_SHOW_OPTIONS, MENU_SHOW_EXTRA_OPTIONS, 0, + R.string.edit_event_show_extra_options); + item.setIcon(R.drawable.ic_menu_show_list); + item = menu.add(MENU_GROUP_HIDE_OPTIONS, MENU_HIDE_EXTRA_OPTIONS, 0, + R.string.edit_event_hide_extra_options); + item.setIcon(R.drawable.ic_menu_show_list); + + return super.onCreateOptionsMenu(menu); + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + if (mReminderItems.size() < MAX_REMINDERS) { + menu.setGroupVisible(MENU_GROUP_REMINDER, true); + menu.setGroupEnabled(MENU_GROUP_REMINDER, true); + } else { + menu.setGroupVisible(MENU_GROUP_REMINDER, false); + menu.setGroupEnabled(MENU_GROUP_REMINDER, false); + } + + if (mExtraOptions.getVisibility() == View.VISIBLE) { + menu.setGroupVisible(MENU_GROUP_SHOW_OPTIONS, false); + menu.setGroupVisible(MENU_GROUP_HIDE_OPTIONS, true); + } else { + menu.setGroupVisible(MENU_GROUP_SHOW_OPTIONS, true); + menu.setGroupVisible(MENU_GROUP_HIDE_OPTIONS, false); + } + + return super.onPrepareOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case MENU_ADD_REMINDER: + // TODO: when adding a new reminder, make it different from the + // last one in the list (if any). + if (mDefaultReminderMinutes == 0) { + addReminder(this, this, mReminderItems, mReminderValues, + mReminderLabels, 10 /* minutes */); + } else { + addReminder(this, this, mReminderItems, mReminderValues, + mReminderLabels, mDefaultReminderMinutes); + } + updateRemindersVisibility(); + return true; + case MENU_SHOW_EXTRA_OPTIONS: + mExtraOptions.setVisibility(View.VISIBLE); + return true; + case MENU_HIDE_EXTRA_OPTIONS: + mExtraOptions.setVisibility(View.GONE); + return true; + } + return super.onOptionsItemSelected(item); + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + switch (keyCode) { + case KeyEvent.KEYCODE_BACK: + // If we are creating a new event, do not create it if the + // title, location and description are all empty, in order to + // prevent accidental "no subject" event creations. + if (mUri != null || !isEmpty()) { + save(); + } + break; + } + + return super.onKeyDown(keyCode, event); + } + + private void populateWhen() { + long startMillis = mStartTime.toMillis(false /* use isDst */); + long endMillis = mEndTime.toMillis(false /* use isDst */); + setDate(mStartDateButton, startMillis); + setDate(mEndDateButton, endMillis); + + setTime(mStartTimeButton, startMillis); + setTime(mEndTimeButton, endMillis); + + mStartDateButton.setOnClickListener(new DateClickListener(mStartTime)); + mEndDateButton.setOnClickListener(new DateClickListener(mEndTime)); + + mStartTimeButton.setOnClickListener(new TimeClickListener(mStartTime)); + mEndTimeButton.setOnClickListener(new TimeClickListener(mEndTime)); + } + + private void populateRepeats() { + Time time = mStartTime; + Resources r = getResources(); + int resource = android.R.layout.simple_spinner_item; + + String[] days = r.getStringArray(R.array.day_labels); + String[] ordinals = r.getStringArray(R.array.ordinal_labels); + + // Only display "Custom" in the spinner if the device does not support the + // recurrence functionality of the event. Only display every weekday if + // the event starts on a weekday. + boolean isCustomRecurrence = isCustomRecurrence(); + boolean isWeekdayEvent = isWeekdayEvent(); + + ArrayList<String> repeatArray = new ArrayList<String>(0); + ArrayList<Integer> recurrenceIndexes = new ArrayList<Integer>(0); + + repeatArray.add(r.getString(R.string.does_not_repeat)); + recurrenceIndexes.add(DOES_NOT_REPEAT); + + repeatArray.add(r.getString(R.string.daily)); + recurrenceIndexes.add(REPEATS_DAILY); + + if (isWeekdayEvent) { + repeatArray.add(r.getString(R.string.every_weekday)); + recurrenceIndexes.add(REPEATS_EVERY_WEEKDAY); + } + + String format = r.getString(R.string.weekly); + repeatArray.add(String.format(format, time.format("%A"))); + recurrenceIndexes.add(REPEATS_WEEKLY_ON_DAY); + + // Calculate whether this is the 1st, 2nd, 3rd, 4th, or last appearance of the given day. + int dayNumber = (time.monthDay - 1) / 7; + format = r.getString(R.string.monthly_on_day_count); + repeatArray.add(String.format(format, ordinals[dayNumber], days[time.weekDay])); + recurrenceIndexes.add(REPEATS_MONTHLY_ON_DAY_COUNT); + + format = r.getString(R.string.monthly_on_day); + repeatArray.add(String.format(format, time.monthDay)); + recurrenceIndexes.add(REPEATS_MONTHLY_ON_DAY); + + long when = time.toMillis(false); + format = r.getString(R.string.yearly); + int flags = 0; + if (DateFormat.is24HourFormat(this)) { + flags |= DateUtils.FORMAT_24HOUR; + } + repeatArray.add(String.format(format, DateUtils.formatDateRange(when, when, flags))); + recurrenceIndexes.add(REPEATS_YEARLY); + + if (isCustomRecurrence) { + repeatArray.add(r.getString(R.string.custom)); + recurrenceIndexes.add(REPEATS_CUSTOM); + } + mRecurrenceIndexes = recurrenceIndexes; + + int position = recurrenceIndexes.indexOf(DOES_NOT_REPEAT); + if (mRrule != null) { + if (isCustomRecurrence) { + position = recurrenceIndexes.indexOf(REPEATS_CUSTOM); + } else { + switch (mEventRecurrence.freq) { + case EventRecurrence.DAILY: + position = recurrenceIndexes.indexOf(REPEATS_DAILY); + break; + case EventRecurrence.WEEKLY: + if (mEventRecurrence.repeatsOnEveryWeekDay()) { + position = recurrenceIndexes.indexOf(REPEATS_EVERY_WEEKDAY); + } else { + position = recurrenceIndexes.indexOf(REPEATS_WEEKLY_ON_DAY); + } + break; + case EventRecurrence.MONTHLY: + if (mEventRecurrence.repeatsMonthlyOnDayCount()) { + position = recurrenceIndexes.indexOf(REPEATS_MONTHLY_ON_DAY_COUNT); + } else { + position = recurrenceIndexes.indexOf(REPEATS_MONTHLY_ON_DAY); + } + break; + case EventRecurrence.YEARLY: + position = recurrenceIndexes.indexOf(REPEATS_YEARLY); + break; + } + } + } + ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, resource, repeatArray); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + mRepeatsSpinner.setAdapter(adapter); + mRepeatsSpinner.setSelection(position); + } + + // Adds a reminder to the displayed list of reminders. + // Returns true if successfully added reminder, false if no reminders can + // be added. + static boolean addReminder(Activity activity, View.OnClickListener listener, + ArrayList<LinearLayout> items, ArrayList<Integer> values, + ArrayList<String> labels, int minutes) { + + if (items.size() >= MAX_REMINDERS) { + return false; + } + + LayoutInflater inflater = activity.getLayoutInflater(); + LinearLayout parent = (LinearLayout) activity.findViewById(R.id.reminder_items_container); + LinearLayout reminderItem = (LinearLayout) inflater.inflate(R.layout.edit_reminder_item, null); + parent.addView(reminderItem); + + Spinner spinner = (Spinner) reminderItem.findViewById(R.id.reminder_value); + Resources res = activity.getResources(); + int resource = android.R.layout.simple_spinner_item; + ArrayAdapter<String> adapter = new ArrayAdapter<String>(activity, resource, labels); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + spinner.setAdapter(adapter); + + ImageButton reminderRemoveButton; + reminderRemoveButton = (ImageButton) reminderItem.findViewById(R.id.reminder_remove); + reminderRemoveButton.setOnClickListener(listener); + + int index = findMinutesInReminderList(values, minutes); + spinner.setSelection(index); + items.add(reminderItem); + + return true; + } + + static void addMinutesToList(Context context, ArrayList<Integer> values, + ArrayList<String> labels, int minutes) { + int index = values.indexOf(minutes); + if (index != -1) { + return; + } + + // The requested "minutes" does not exist in the list, so insert it + // into the list. + + String label = constructReminderLabel(context, minutes, false); + int len = values.size(); + for (int i = 0; i < len; i++) { + if (minutes < values.get(i)) { + values.add(i, minutes); + labels.add(i, label); + return; + } + } + + values.add(minutes); + labels.add(len, label); + } + + /** + * Finds the index of the given "minutes" in the "values" list. + * + * @param values the list of minutes corresponding to the spinner choices + * @param minutes the minutes to search for in the values list + * @return the index of "minutes" in the "values" list + */ + private static int findMinutesInReminderList(ArrayList<Integer> values, int minutes) { + int index = values.indexOf(minutes); + if (index == -1) { + // This should never happen. + Log.e("Cal", "Cannot find minutes (" + minutes + ") in list"); + return 0; + } + return index; + } + + // Constructs a label given an arbitrary number of minutes. For example, + // if the given minutes is 63, then this returns the string "63 minutes". + // As another example, if the given minutes is 120, then this returns + // "2 hours". + static String constructReminderLabel(Context context, int minutes, boolean abbrev) { + Resources resources = context.getResources(); + int value, resId; + + if (minutes % 60 != 0) { + value = minutes; + if (abbrev) { + resId = R.plurals.Nmins; + } else { + resId = R.plurals.Nminutes; + } + } else if (minutes % (24 * 60) != 0) { + value = minutes / 60; + resId = R.plurals.Nhours; + } else { + value = minutes / ( 24 * 60); + resId = R.plurals.Ndays; + } + + String format = resources.getQuantityString(resId, value); + return String.format(format, value); + } + + private void updateRemindersVisibility() { + if (mReminderItems.size() == 0) { + mRemindersSeparator.setVisibility(View.GONE); + mRemindersContainer.setVisibility(View.GONE); + } else { + mRemindersSeparator.setVisibility(View.VISIBLE); + mRemindersContainer.setVisibility(View.VISIBLE); + } + } + + private void setDate(TextView view, long millis) { + int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR | + DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_ABBREV_MONTH | + DateUtils.FORMAT_ABBREV_WEEKDAY; + view.setText(DateUtils.formatDateRange(millis, millis, flags)); + } + + private void setTime(TextView view, long millis) { + int flags = DateUtils.FORMAT_SHOW_TIME; + if (DateFormat.is24HourFormat(this)) { + flags |= DateUtils.FORMAT_24HOUR; + } + view.setText(DateUtils.formatDateRange(millis, millis, flags)); + } + + private void save() { + // Avoid saving if the calendars cursor is empty. This shouldn't ever + // happen since the setup wizard should ensure the user has a calendar. + if (mCalendarsCursor == null || mCalendarsCursor.getCount() == 0) { + Log.w("Cal", "The calendars table does not contain any calendars. New event was not " + + "created."); + return; + } + + ContentResolver cr = getContentResolver(); + ContentValues values = getContentValuesFromUi(); + Uri uri = mUri; + + // For recurring events, we must make sure that we use duration rather + // than dtend. + if (uri == null) { + // Create new event with new contents + addRecurrenceRule(values); + uri = cr.insert(Events.CONTENT_URI, values); + + } else if (mRrule == null) { + // Modify contents of a non-repeating event + addRecurrenceRule(values); + checkTimeDependentFields(values); + cr.update(uri, values, null, null); + + } else if (mInitialValues.getAsString(Events.RRULE) == null) { + // This event was changed from a non-repeating event to a + // repeating event. + addRecurrenceRule(values); + values.remove(Events.DTEND); + cr.update(uri, values, null, null); + + } else if (mModification == MODIFY_SELECTED) { + // Modify contents of the current instance of repeating event + + // Create a recurrence exception + long begin = mInitialValues.getAsLong(EVENT_BEGIN_TIME); + values.put(Events.ORIGINAL_EVENT, mEventCursor.getString(EVENT_INDEX_SYNC_ID)); + values.put(Events.ORIGINAL_INSTANCE_TIME, begin); + + uri = cr.insert(Events.CONTENT_URI, values); + + } else if (mModification == MODIFY_ALL_FOLLOWING) { + // Modify contents of all future instances of repeating event + + // Update the current repeating event to end at the new start time + updatePastEvents(cr, uri); + + // Create a new event that has a begin time of now + mEventRecurrence.parse(mRrule); + addRecurrenceRule(values); + values.remove(Events.DTEND); + uri = cr.insert(Events.CONTENT_URI, values); + + } else if (mModification == MODIFY_ALL) { + + // Modify all instances of repeating event + addRecurrenceRule(values); + + if (mRrule == null) { + + // We've changed a recurring event to non recurring + // End the previous events and create a new event + // If we're the first even though we just delete and + // create a new one. + if (isFirstEventInSeries()) { + cr.delete(uri, null, null); + } else { + updatePastEvents(cr, uri); + } + uri = cr.insert(Events.CONTENT_URI, values); + } else { + checkTimeDependentFields(values); + values.remove(Events.DTEND); + cr.update(uri, values, null, null); + } + } + + if (uri != null) { + long eventId = ContentUris.parseId(uri); + ArrayList<Integer> reminderMinutes = reminderItemsToMinutes(mReminderItems, + mReminderValues); + saveReminders(cr, eventId, reminderMinutes, mOriginalMinutes); + } + } + + private boolean isFirstEventInSeries() { + int dtStart = mEventCursor.getColumnIndexOrThrow(Events.DTSTART); + long start = mEventCursor.getLong(dtStart); + return start == mStartTime.toMillis(true); + } + + private void updatePastEvents(ContentResolver cr, Uri uri) { + long oldStartMillis = mEventCursor.getLong(EVENT_INDEX_DTSTART); + String oldDuration = mEventCursor.getString(EVENT_INDEX_DURATION); + + Time oldUntilTime = new Time(); + long begin = mInitialValues.getAsLong(EVENT_BEGIN_TIME); + if (mInitialValues.getAsBoolean(Events.ALL_DAY)) { + oldUntilTime.timezone = Time.TIMEZONE_UTC; + } + oldUntilTime.set(begin); + oldUntilTime.second--; + oldUntilTime.normalize(false); + mEventRecurrence.until = oldUntilTime.format2445(); + + ContentValues oldValues = new ContentValues(); + oldValues.put(Events.DTSTART, oldStartMillis); + oldValues.put(Events.DURATION, oldDuration); + oldValues.put(Events.RRULE, mEventRecurrence.toString()); + cr.update(uri, oldValues, null, null); + } + + private void checkTimeDependentFields(ContentValues values) { + long oldBegin = mInitialValues.getAsLong(EVENT_BEGIN_TIME); + long oldEnd = mInitialValues.getAsLong(EVENT_END_TIME); + boolean oldAllDay = mInitialValues.getAsBoolean(Events.ALL_DAY); + String oldRrule = mInitialValues.getAsString(Events.RRULE); + String oldTimezone = mInitialValues.getAsString(Events.EVENT_TIMEZONE); + + long newBegin = values.getAsLong(Events.DTSTART); + long newEnd = values.getAsLong(Events.DTEND); + boolean newAllDay = values.getAsInteger(Events.ALL_DAY) == 1; + String newRrule = values.getAsString(Events.RRULE); + String newTimezone = values.getAsString(Events.EVENT_TIMEZONE); + + // If none of the time-dependent fields changed, then remove them. + if (oldBegin == newBegin && oldEnd == newEnd && oldAllDay == newAllDay + && TextUtils.equals(oldRrule, newRrule) + && TextUtils.equals(oldTimezone, newTimezone)) { + values.remove(Events.DTSTART); + values.remove(Events.DTEND); + values.remove(Events.DURATION); + values.remove(Events.ALL_DAY); + values.remove(Events.RRULE); + values.remove(Events.EVENT_TIMEZONE); + return; + } + + if (oldRrule == null || newRrule == null) { + return; + } + + // If we are modifying all events then we need to set DTSTART to the + // start time of the first event in the series, not the current + // date and time. If the start time of the event was changed + // (from, say, 3pm to 4pm), then we want to add the time difference + // to the start time of the first event in the series (the DTSTART + // value). If we are modifying one instance or all following instances, + // then we leave the DTSTART field alone. + if (mModification == MODIFY_ALL) { + long oldStartMillis = mEventCursor.getLong(EVENT_INDEX_DTSTART); + if (oldBegin != newBegin) { + // The user changed the start time of this event + long offset = newBegin - oldBegin; + oldStartMillis += offset; + } + values.put(Events.DTSTART, oldStartMillis); + } + } + + static ArrayList<Integer> reminderItemsToMinutes(ArrayList<LinearLayout> reminderItems, + ArrayList<Integer> reminderValues) { + int len = reminderItems.size(); + ArrayList<Integer> reminderMinutes = new ArrayList<Integer>(len); + for (int index = 0; index < len; index++) { + LinearLayout layout = reminderItems.get(index); + Spinner spinner = (Spinner) layout.findViewById(R.id.reminder_value); + int minutes = reminderValues.get(spinner.getSelectedItemPosition()); + reminderMinutes.add(minutes); + } + return reminderMinutes; + } + + static void saveReminders(ContentResolver cr, long eventId, + ArrayList<Integer> reminderMinutes, ArrayList<Integer> originalMinutes) { + // If the reminders have not changed, then don't update the database + if (reminderMinutes.equals(originalMinutes)) { + return; + } + + // Delete all the existing reminders for this event + String where = Reminders.EVENT_ID + "=?"; + String[] args = new String[] { Long.toString(eventId) }; + cr.delete(Reminders.CONTENT_URI, where, args); + + // Update the "hasAlarm" field for the event + ContentValues values = new ContentValues(); + int len = reminderMinutes.size(); + values.put(Events.HAS_ALARM, (len > 0) ? 1 : 0); + Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventId); + cr.update(uri, values, null /* where */, null /* selection args */); + + // Insert the new reminders, if any + for (int i = 0; i < len; i++) { + int minutes = reminderMinutes.get(i); + + values.clear(); + values.put(Reminders.MINUTES, minutes); + values.put(Reminders.METHOD, Reminders.METHOD_ALERT); + values.put(Reminders.EVENT_ID, eventId); + cr.insert(Reminders.CONTENT_URI, values); + } + } + + private void addRecurrenceRule(ContentValues values) { + updateRecurrenceRule(); + + if (mRrule == null) { + return; + } + + values.put(Events.RRULE, mRrule); + long end = mEndTime.toMillis(true /* ignore dst */); + long start = mStartTime.toMillis(true /* ignore dst */); + String duration; + + boolean isAllDay = mAllDayCheckBox.isChecked(); + if (isAllDay) { + long days = (end - start + DateUtils.DAY_IN_MILLIS - 1) / DateUtils.DAY_IN_MILLIS; + duration = "P" + days + "D"; + } else { + long seconds = (end - start) / DateUtils.SECOND_IN_MILLIS; + duration = "P" + seconds + "S"; + } + values.put(Events.DURATION, duration); + } + + private void updateRecurrenceRule() { + int position = mRepeatsSpinner.getSelectedItemPosition(); + int selection = mRecurrenceIndexes.get(position); + + if (selection == DOES_NOT_REPEAT) { + mRrule = null; + return; + } else if (selection == REPEATS_CUSTOM) { + // Keep custom recurrence as before. + return; + } else if (selection == REPEATS_DAILY) { + mEventRecurrence.freq = EventRecurrence.DAILY; + } else if (selection == REPEATS_EVERY_WEEKDAY) { + mEventRecurrence.freq = EventRecurrence.WEEKLY; + int dayCount = 5; + int[] byday = new int[dayCount]; + int[] bydayNum = new int[dayCount]; + + byday[0] = EventRecurrence.MO; + byday[1] = EventRecurrence.TU; + byday[2] = EventRecurrence.WE; + byday[3] = EventRecurrence.TH; + byday[4] = EventRecurrence.FR; + for (int day = 0; day < dayCount; day++) { + bydayNum[day] = 0; + } + + mEventRecurrence.byday = byday; + mEventRecurrence.bydayNum = bydayNum; + mEventRecurrence.bydayCount = dayCount; + } else if (selection == REPEATS_WEEKLY_ON_DAY) { + mEventRecurrence.freq = EventRecurrence.WEEKLY; + int[] days = new int[1]; + int dayCount = 1; + int[] dayNum = new int[dayCount]; + + days[0] = EventRecurrence.timeDay2Day(mStartTime.weekDay); + // not sure why this needs to be zero, but set it for now. + dayNum[0] = 0; + + mEventRecurrence.byday = days; + mEventRecurrence.bydayNum = dayNum; + mEventRecurrence.bydayCount = dayCount; + } else if (selection == REPEATS_MONTHLY_ON_DAY) { + mEventRecurrence.freq = EventRecurrence.MONTHLY; + mEventRecurrence.bydayCount = 0; + mEventRecurrence.bymonthdayCount = 1; + int[] bymonthday = new int[1]; + bymonthday[0] = mStartTime.monthDay; + mEventRecurrence.bymonthday = bymonthday; + } else if (selection == REPEATS_MONTHLY_ON_DAY_COUNT) { + mEventRecurrence.freq = EventRecurrence.MONTHLY; + mEventRecurrence.bydayCount = 1; + mEventRecurrence.bymonthdayCount = 0; + + int[] byday = new int[1]; + int[] bydayNum = new int[1]; + // Compute the week number (for example, the "2nd" Monday) + int dayCount = 1 + ((mStartTime.monthDay - 1) / 7); + if (dayCount == 5) { + dayCount = -1; + } + bydayNum[0] = dayCount; + byday[0] = EventRecurrence.timeDay2Day(mStartTime.weekDay); + mEventRecurrence.byday = byday; + mEventRecurrence.bydayNum = bydayNum; + } else if (selection == REPEATS_YEARLY) { + mEventRecurrence.freq = EventRecurrence.YEARLY; + } + + // Set the week start day. + mEventRecurrence.wkst = EventRecurrence.calendarDay2Day(mFirstDayOfWeek); + mRrule = mEventRecurrence.toString(); + } + + private ContentValues getContentValuesFromUi() { + String title = mTitleTextView.getText().toString(); + boolean isAllDay = mAllDayCheckBox.isChecked(); + String location = mLocationTextView.getText().toString(); + String description = mDescriptionTextView.getText().toString(); + long calendarId = mCalendarsSpinner.getSelectedItemId(); + Cursor calendarCursor = (Cursor) mCalendarsSpinner.getSelectedItem(); + + ContentValues values = new ContentValues(); + + String timezone = null; + long startMillis; + long endMillis; + if (isAllDay) { + // Reset start and end time, increment the monthDay by 1, and set + // the timezone to UTC, as required for all-day events. + timezone = Time.TIMEZONE_UTC; + mStartTime.hour = 0; + mStartTime.minute = 0; + mStartTime.second = 0; + mStartTime.timezone = timezone; + startMillis = mStartTime.normalize(true); + + mEndTime.hour = 0; + mEndTime.minute = 0; + mEndTime.second = 0; + mEndTime.monthDay++; + mEndTime.timezone = timezone; + endMillis = mEndTime.normalize(true); + } else { + startMillis = mStartTime.toMillis(true); + endMillis = mEndTime.toMillis(true); + if (mEventCursor != null) { + timezone = mEventCursor.getString(EVENT_INDEX_TIMEZONE); + } else if (calendarCursor != null) { + timezone = calendarCursor.getString(CALENDARS_INDEX_TIMEZONE); + } + } + + values.put(Events.EVENT_TIMEZONE, timezone); + values.put(Events.CALENDAR_ID, calendarId); + values.put(Events.TITLE, title); + values.put(Events.ALL_DAY, isAllDay ? 1 : 0); + values.put(Events.DTSTART, startMillis); + values.put(Events.DTEND, endMillis); + values.put(Events.DESCRIPTION, description); + values.put(Events.EVENT_LOCATION, location); + values.put(Events.TRANSPARENCY, mAvailabilitySpinner.getSelectedItemPosition()); + + int visibility = mVisibilitySpinner.getSelectedItemPosition(); + if (visibility > 0) { + // For now we the array contains the values 0, 2, and 3. We add one to match. + visibility++; + } + values.put(Events.VISIBILITY, visibility); + + return values; + } + + private boolean isEmpty() { + String title = mTitleTextView.getText().toString(); + if (title.length() > 0) { + return false; + } + + String location = mLocationTextView.getText().toString(); + if (location.length() > 0) { + return false; + } + + String description = mDescriptionTextView.getText().toString(); + if (description.length() > 0) { + return false; + } + + return true; + } + + private boolean isCustomRecurrence() { + + if (mEventRecurrence.until != null || mEventRecurrence.interval != 0) { + return true; + } + + if (mEventRecurrence.freq == 0) { + return false; + } + + switch (mEventRecurrence.freq) { + case EventRecurrence.DAILY: + return false; + case EventRecurrence.WEEKLY: + if (mEventRecurrence.repeatsOnEveryWeekDay() && isWeekdayEvent()) { + return false; + } else if (mEventRecurrence.bydayCount == 1) { + return false; + } + break; + case EventRecurrence.MONTHLY: + if (mEventRecurrence.repeatsMonthlyOnDayCount()) { + return false; + } else if (mEventRecurrence.bydayCount == 0 && mEventRecurrence.bymonthdayCount == 1) { + return false; + } + break; + case EventRecurrence.YEARLY: + return false; + } + + return true; + } + + private boolean isWeekdayEvent() { + if (mStartTime.weekDay != Time.SUNDAY && mStartTime.weekDay != Time.SATURDAY) { + return true; + } + return false; + } +} |