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