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