blob: 117cc0919d6ff542f954654f5001c0f556fde5ec [file] [log] [blame]
Erikeca82e92010-06-11 14:05:00 -07001/*
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
Erikfbce65e2010-08-16 12:43:13 -070017package com.android.calendar.event;
Erikeca82e92010-06-11 14:05:00 -070018
Erik79f22812010-06-23 16:55:38 -070019import android.app.Activity;
Erikeca82e92010-06-11 14:05:00 -070020import android.app.AlertDialog;
Michael Chane6aa9372013-04-01 00:31:54 -070021import android.app.DialogFragment;
Michael Chanb21c6382013-03-14 02:53:30 -070022import android.app.FragmentManager;
Erik5f620792010-10-27 12:42:01 -070023import android.app.ProgressDialog;
Andy McFaddenef31a982011-06-09 07:38:56 -070024import android.app.Service;
Erikeca82e92010-06-11 14:05:00 -070025import android.content.Context;
26import android.content.DialogInterface;
Daisuke Miyakawa464f3502010-09-21 22:12:55 -070027import android.content.Intent;
Erikeca82e92010-06-11 14:05:00 -070028import android.content.SharedPreferences;
29import android.content.res.Resources;
30import android.database.Cursor;
Erikc07a57f2010-12-01 10:45:48 -080031import android.graphics.drawable.Drawable;
Michael Chanb21c6382013-03-14 02:53:30 -070032import android.os.Bundle;
James Kung4afba182012-12-18 23:12:08 -080033import android.provider.CalendarContract;
RoboErika7c03902011-06-14 11:06:44 -070034import android.provider.CalendarContract.Attendees;
35import android.provider.CalendarContract.Calendars;
Michael Chan9e196e42012-05-11 09:46:18 -070036import android.provider.CalendarContract.Events;
RoboErika7c03902011-06-14 11:06:44 -070037import android.provider.CalendarContract.Reminders;
Daisuke Miyakawa464f3502010-09-21 22:12:55 -070038import android.provider.Settings;
Erikeca82e92010-06-11 14:05:00 -070039import 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;
Erik7b92da22010-09-23 14:55:22 -070045import android.util.Log;
Sara Ting601ef6e2012-07-10 14:54:19 -070046import android.view.KeyEvent;
Erikeca82e92010-06-11 14:05:00 -070047import android.view.View;
James Kung4afba182012-12-18 23:12:08 -080048import android.view.View.OnClickListener;
Erik259b8f92011-01-12 14:01:12 -080049import android.view.ViewGroup;
50import android.view.accessibility.AccessibilityEvent;
51import android.view.accessibility.AccessibilityManager;
Sara Ting601ef6e2012-07-10 14:54:19 -070052import android.view.inputmethod.EditorInfo;
Erik3400a2b2010-12-06 17:48:44 -080053import android.widget.AdapterView;
54import android.widget.AdapterView.OnItemSelectedListener;
Erikeca82e92010-06-11 14:05:00 -070055import android.widget.ArrayAdapter;
Sara Ting601ef6e2012-07-10 14:54:19 -070056import android.widget.AutoCompleteTextView;
Erikeca82e92010-06-11 14:05:00 -070057import android.widget.Button;
58import android.widget.CheckBox;
59import android.widget.CompoundButton;
Erikeca82e92010-06-11 14:05:00 -070060import android.widget.LinearLayout;
61import android.widget.MultiAutoCompleteTextView;
Erik259b8f92011-01-12 14:01:12 -080062import android.widget.RadioButton;
Michael Chanf92f46b2010-10-27 18:59:05 -070063import android.widget.RadioGroup;
Erikeca82e92010-06-11 14:05:00 -070064import android.widget.ResourceCursorAdapter;
65import android.widget.ScrollView;
66import android.widget.Spinner;
67import android.widget.TextView;
Sara Ting601ef6e2012-07-10 14:54:19 -070068import android.widget.TextView.OnEditorActionListener;
Erikeca82e92010-06-11 14:05:00 -070069
Michael Chan9e196e42012-05-11 09:46:18 -070070import com.android.calendar.CalendarEventModel;
71import com.android.calendar.CalendarEventModel.Attendee;
72import com.android.calendar.CalendarEventModel.ReminderEntry;
73import com.android.calendar.EmailAddressAdapter;
74import com.android.calendar.EventInfoFragment;
Michael Chanb21c6382013-03-14 02:53:30 -070075import com.android.calendar.EventRecurrenceFormatter;
Michael Chan9e196e42012-05-11 09:46:18 -070076import com.android.calendar.GeneralPreferences;
77import com.android.calendar.R;
78import com.android.calendar.RecipientAdapter;
Michael Chan9e196e42012-05-11 09:46:18 -070079import com.android.calendar.Utils;
80import com.android.calendar.event.EditEventHelper.EditDoneRunnable;
Michael Chanb21c6382013-03-14 02:53:30 -070081import com.android.calendar.recurrencepicker.RecurrencePickerDialog;
Michael Chan8a528ad2012-06-26 17:44:34 -070082import com.android.calendarcommon2.EventRecurrence;
Michael Chan9e196e42012-05-11 09:46:18 -070083import com.android.common.Rfc822InputFilter;
84import com.android.common.Rfc822Validator;
James Kung56f42bf2013-03-29 14:59:29 -070085import com.android.datetimepicker.date.DatePickerDialog;
86import com.android.datetimepicker.date.DatePickerDialog.OnDateSetListener;
87import com.android.datetimepicker.time.RadialPickerLayout;
88import com.android.datetimepicker.time.TimePickerDialog;
89import com.android.datetimepicker.time.TimePickerDialog.OnTimeSetListener;
Michael Chan9e196e42012-05-11 09:46:18 -070090import com.android.ex.chips.AccountSpecifier;
91import com.android.ex.chips.BaseRecipientAdapter;
92import com.android.ex.chips.ChipsUtil;
93import com.android.ex.chips.RecipientEditTextView;
Michael Chane6aa9372013-04-01 00:31:54 -070094import com.android.timezonepicker.TimeZoneInfo;
95import com.android.timezonepicker.TimeZonePickerDialog;
James Kung974343a2013-04-03 16:12:38 -070096import com.android.timezonepicker.TimeZonePickerUtils;
Michael Chan9e196e42012-05-11 09:46:18 -070097
Erikeca82e92010-06-11 14:05:00 -070098import java.util.ArrayList;
99import java.util.Arrays;
Erik8a548962011-01-10 18:08:03 -0800100import java.util.Formatter;
Erikd845fbe2010-08-12 11:20:01 -0700101import java.util.HashMap;
Erik8a548962011-01-10 18:08:03 -0800102import java.util.Locale;
Mason Tang4c8871b2010-08-10 10:17:43 -0700103import java.util.TimeZone;
Erikeca82e92010-06-11 14:05:00 -0700104
105public class EditEventView implements View.OnClickListener, DialogInterface.OnCancelListener,
Michael Chanb21c6382013-03-14 02:53:30 -0700106 DialogInterface.OnClickListener, OnItemSelectedListener,
Michael Chane6aa9372013-04-01 00:31:54 -0700107 RecurrencePickerDialog.OnRecurrenceSetListener,
108 TimeZonePickerDialog.OnTimeZoneSetListener {
James Kungbb90a7d2013-04-04 15:29:45 -0700109
Erik7b92da22010-09-23 14:55:22 -0700110 private static final String TAG = "EditEvent";
Michael Chan352e1a22010-09-28 05:15:08 -0700111 private static final String GOOGLE_SECONDARY_CALENDAR = "calendar.google.com";
Erik259b8f92011-01-12 14:01:12 -0800112 private static final String PERIOD_SPACE = ". ";
Michael Chane6aa9372013-04-01 00:31:54 -0700113
James Kung56f42bf2013-03-29 14:59:29 -0700114 private static final String FRAG_TAG_DATE_PICKER = "datePickerDialogFragment";
Sam Blitzstein7674b692013-01-29 11:45:20 -0800115 private static final String FRAG_TAG_TIME_PICKER = "timePickerDialogFragment";
Michael Chane6aa9372013-04-01 00:31:54 -0700116 private static final String FRAG_TAG_TIME_ZONE_PICKER = "timeZonePickerDialogFragment";
117 private static final String FRAG_TAG_RECUR_PICKER = "recurrencePickerDialogFragment";
Erikd845fbe2010-08-12 11:20:01 -0700118
Erikc07a57f2010-12-01 10:45:48 -0800119 ArrayList<View> mEditOnlyList = new ArrayList<View>();
120 ArrayList<View> mEditViewList = new ArrayList<View>();
121 ArrayList<View> mViewOnlyList = new ArrayList<View>();
Erikeca82e92010-06-11 14:05:00 -0700122 TextView mLoadingMessage;
123 ScrollView mScrollView;
124 Button mStartDateButton;
125 Button mEndDateButton;
126 Button mStartTimeButton;
127 Button mEndTimeButton;
Mason Tang4c8871b2010-08-10 10:17:43 -0700128 Button mTimezoneButton;
James Kung4afba182012-12-18 23:12:08 -0800129 View mColorPickerNewEvent;
130 View mColorPickerExistingEvent;
131 OnClickListener mChangeColorOnClickListener;
Michael Chanb20942b2011-09-08 00:52:18 -0700132 View mTimezoneRow;
Erik8a548962011-01-10 18:08:03 -0800133 TextView mStartTimeHome;
134 TextView mStartDateHome;
135 TextView mEndTimeHome;
136 TextView mEndDateHome;
Erikeca82e92010-06-11 14:05:00 -0700137 CheckBox mAllDayCheckBox;
138 Spinner mCalendarsSpinner;
Michael Chanb21c6382013-03-14 02:53:30 -0700139 Button mRruleButton;
Michael Chanced0eb62011-05-31 18:12:52 -0700140 Spinner mAvailabilitySpinner;
141 Spinner mAccessLevelSpinner;
Michael Chanf92f46b2010-10-27 18:59:05 -0700142 RadioGroup mResponseRadioGroup;
Sara Tingd19fa1c2012-12-05 11:02:11 -0800143 TextView mTitleTextView;
Sara Ting4686b402012-11-13 13:52:54 -0800144 AutoCompleteTextView mLocationTextView;
145 EventLocationAdapter mLocationAdapter;
Erikeca82e92010-06-11 14:05:00 -0700146 TextView mDescriptionTextView;
Michael Chan24ee3362010-11-01 19:06:01 -0700147 TextView mWhenView;
Mason Tang4c8871b2010-08-10 10:17:43 -0700148 TextView mTimezoneTextView;
Michael Chan24ee3362010-11-01 19:06:01 -0700149 TextView mTimezoneLabel;
Erikeca82e92010-06-11 14:05:00 -0700150 LinearLayout mRemindersContainer;
Erikeca82e92010-06-11 14:05:00 -0700151 MultiAutoCompleteTextView mAttendeesList;
Erikc07a57f2010-12-01 10:45:48 -0800152 View mCalendarSelectorGroup;
Mindy Pereiraeb51f842011-11-11 14:45:54 -0800153 View mCalendarSelectorWrapper;
Erikc07a57f2010-12-01 10:45:48 -0800154 View mCalendarStaticGroup;
Erika7694ee2010-12-07 15:46:18 -0800155 View mLocationGroup;
156 View mDescriptionGroup;
Erikc07a57f2010-12-01 10:45:48 -0800157 View mRemindersGroup;
158 View mResponseGroup;
159 View mOrganizerGroup;
160 View mAttendeesGroup;
Erik8a548962011-01-10 18:08:03 -0800161 View mStartHomeGroup;
162 View mEndHomeGroup;
Erikc07a57f2010-12-01 10:45:48 -0800163
Erikefa480c2011-01-13 11:57:25 -0800164 private int[] mOriginalPadding = new int[4];
Erikeca82e92010-06-11 14:05:00 -0700165
James Kung4afba182012-12-18 23:12:08 -0800166 public boolean mIsMultipane;
Erikeca82e92010-06-11 14:05:00 -0700167 private ProgressDialog mLoadingCalendarsDialog;
168 private AlertDialog mNoCalendarsDialog;
Michael Chane6aa9372013-04-01 00:31:54 -0700169 private DialogFragment mTimezoneDialog;
Erik79f22812010-06-23 16:55:38 -0700170 private Activity mActivity;
171 private EditDoneRunnable mDone;
Erikeca82e92010-06-11 14:05:00 -0700172 private View mView;
173 private CalendarEventModel mModel;
174 private Cursor mCalendarsCursor;
Mindy Pereira18cabd22011-06-09 18:31:23 -0700175 private AccountSpecifier mAddressAdapter;
Erikeca82e92010-06-11 14:05:00 -0700176 private Rfc822Validator mEmailValidator;
177
Sam Blitzstein7674b692013-01-29 11:45:20 -0800178 public boolean mTimeSelectedWasStartTime;
James Kung56f42bf2013-03-29 14:59:29 -0700179 public boolean mDateSelectedWasStartDate;
Sam Blitzstein3cde8342013-04-01 13:11:15 -0700180 private TimePickerDialog mStartTimePickerDialog;
181 private TimePickerDialog mEndTimePickerDialog;
James Kung56f42bf2013-03-29 14:59:29 -0700182 private DatePickerDialog mDatePickerDialog;
Sam Blitzstein7674b692013-01-29 11:45:20 -0800183
Andy McFaddenfea9af52011-05-13 15:40:10 -0700184 /**
185 * Contents of the "minutes" spinner. This has default values from the XML file, augmented
186 * with any additional values that were already associated with the event.
187 */
188 private ArrayList<Integer> mReminderMinuteValues;
189 private ArrayList<String> mReminderMinuteLabels;
190
Andy McFadden9645d9c2011-05-16 15:28:15 -0700191 /**
192 * Contents of the "methods" spinner. The "values" list specifies the method constant
193 * (e.g. {@link Reminders#METHOD_ALERT}) associated with the labels. Any methods that
194 * aren't allowed by the Calendar will be removed.
195 */
196 private ArrayList<Integer> mReminderMethodValues;
197 private ArrayList<String> mReminderMethodLabels;
198
RoboErikc6680c52011-10-28 13:57:54 -0700199 /**
200 * Contents of the "availability" spinner. The "values" list specifies the
201 * type constant (e.g. {@link Events#AVAILABILITY_BUSY}) associated with the
202 * labels. Any types that aren't allowed by the Calendar will be removed.
203 */
204 private ArrayList<Integer> mAvailabilityValues;
205 private ArrayList<String> mAvailabilityLabels;
Sam Blitzstein7cdfb4d2013-02-22 15:30:27 -0800206 private ArrayList<String> mOriginalAvailabilityLabels;
207 private ArrayAdapter<String> mAvailabilityAdapter;
208 private boolean mAvailabilityExplicitlySet;
209 private boolean mAllDayChangingAvailability;
210 private int mAvailabilityCurrentlySelected;
RoboErikc6680c52011-10-28 13:57:54 -0700211
Erikeca82e92010-06-11 14:05:00 -0700212 private int mDefaultReminderMinutes;
213
214 private boolean mSaveAfterQueryComplete = false;
Erikeca82e92010-06-11 14:05:00 -0700215
Sam Blitzstein00040c62013-04-11 10:13:47 -0700216 private TimeZonePickerUtils mTzPickerUtils;
Erikeca82e92010-06-11 14:05:00 -0700217 private Time mStartTime;
218 private Time mEndTime;
Mason Tang4c8871b2010-08-10 10:17:43 -0700219 private String mTimezone;
Michael Chan8b1119b2012-01-06 16:04:55 -0800220 private boolean mAllDay = false;
Erikeca82e92010-06-11 14:05:00 -0700221 private int mModification = EditEventHelper.MODIFY_UNINITIALIZED;
222
223 private EventRecurrence mEventRecurrence = new EventRecurrence();
224
225 private ArrayList<LinearLayout> mReminderItems = new ArrayList<LinearLayout>(0);
RoboErike35a0322011-09-01 16:21:49 -0700226 private ArrayList<ReminderEntry> mUnsupportedReminders = new ArrayList<ReminderEntry>();
Michael Chanb21c6382013-03-14 02:53:30 -0700227 private String mRrule;
Erikeca82e92010-06-11 14:05:00 -0700228
Erik8a548962011-01-10 18:08:03 -0800229 private static StringBuilder mSB = new StringBuilder(50);
230 private static Formatter mF = new Formatter(mSB, Locale.getDefault());
231
Erikeca82e92010-06-11 14:05:00 -0700232 /* This class is used to update the time buttons. */
233 private class TimeListener implements OnTimeSetListener {
234 private View mView;
235
236 public TimeListener(View view) {
237 mView = view;
238 }
239
Andy McFadden9645d9c2011-05-16 15:28:15 -0700240 @Override
Sam Blitzsteind661d5f2013-03-29 15:34:59 -0700241 public void onTimeSet(RadialPickerLayout view, int hourOfDay, int minute) {
Erikeca82e92010-06-11 14:05:00 -0700242 // Cache the member variables locally to avoid inner class overhead.
243 Time startTime = mStartTime;
244 Time endTime = mEndTime;
245
246 // Cache the start and end millis so that we limit the number
247 // of calls to normalize() and toMillis(), which are fairly
248 // expensive.
249 long startMillis;
250 long endMillis;
251 if (mView == mStartTimeButton) {
252 // The start time was changed.
253 int hourDuration = endTime.hour - startTime.hour;
254 int minuteDuration = endTime.minute - startTime.minute;
255
256 startTime.hour = hourOfDay;
257 startTime.minute = minute;
258 startMillis = startTime.normalize(true);
259
260 // Also update the end time to keep the duration constant.
261 endTime.hour = hourOfDay + hourDuration;
262 endTime.minute = minute + minuteDuration;
Michael Chan559cb862012-05-12 07:42:13 -0700263
264 // Update tz in case the start time switched from/to DLS
265 populateTimezone(startMillis);
Erikeca82e92010-06-11 14:05:00 -0700266 } else {
267 // The end time was changed.
268 startMillis = startTime.toMillis(true);
269 endTime.hour = hourOfDay;
270 endTime.minute = minute;
271
272 // Move to the start time if the end time is before the start
273 // time.
274 if (endTime.before(startTime)) {
275 endTime.monthDay = startTime.monthDay + 1;
276 }
Michael Chan559cb862012-05-12 07:42:13 -0700277 // Call populateTimezone if we support end time zone as well
Erikeca82e92010-06-11 14:05:00 -0700278 }
279
280 endMillis = endTime.normalize(true);
281
282 setDate(mEndDateButton, endMillis);
283 setTime(mStartTimeButton, startMillis);
284 setTime(mEndTimeButton, endMillis);
Erik8a548962011-01-10 18:08:03 -0800285 updateHomeTime();
Erikeca82e92010-06-11 14:05:00 -0700286 }
287 }
288
289 private class TimeClickListener implements View.OnClickListener {
290 private Time mTime;
291
292 public TimeClickListener(Time time) {
293 mTime = time;
294 }
295
Andy McFadden9645d9c2011-05-16 15:28:15 -0700296 @Override
Erikeca82e92010-06-11 14:05:00 -0700297 public void onClick(View v) {
Sam Blitzstein7674b692013-01-29 11:45:20 -0800298
Sam Blitzstein3cde8342013-04-01 13:11:15 -0700299 TimePickerDialog dialog;
Sam Blitzstein7674b692013-01-29 11:45:20 -0800300 if (v == mStartTimeButton) {
301 mTimeSelectedWasStartTime = true;
Sam Blitzstein3cde8342013-04-01 13:11:15 -0700302 if (mStartTimePickerDialog == null) {
303 mStartTimePickerDialog = TimePickerDialog.newInstance(new TimeListener(v),
304 mTime.hour, mTime.minute, DateFormat.is24HourFormat(mActivity));
305 } else {
306 mStartTimePickerDialog.setStartTime(mTime.hour, mTime.minute);
307 }
308 dialog = mStartTimePickerDialog;
Sam Blitzstein7674b692013-01-29 11:45:20 -0800309 } else {
310 mTimeSelectedWasStartTime = false;
Sam Blitzstein3cde8342013-04-01 13:11:15 -0700311 if (mEndTimePickerDialog == null) {
312 mEndTimePickerDialog = TimePickerDialog.newInstance(new TimeListener(v),
313 mTime.hour, mTime.minute, DateFormat.is24HourFormat(mActivity));
314 } else {
315 mEndTimePickerDialog.setStartTime(mTime.hour, mTime.minute);
316 }
317 dialog = mEndTimePickerDialog;
318
Sam Blitzstein7674b692013-01-29 11:45:20 -0800319 }
320
James Kung6a212e32013-04-11 17:03:56 -0700321 final FragmentManager fm = mActivity.getFragmentManager();
322 fm.executePendingTransactions();
323
Sam Blitzstein3cde8342013-04-01 13:11:15 -0700324 if (dialog != null && !dialog.isAdded()) {
Sam Blitzstein3cde8342013-04-01 13:11:15 -0700325 dialog.show(fm, FRAG_TAG_TIME_PICKER);
326 }
Erikeca82e92010-06-11 14:05:00 -0700327 }
328 }
329
330 private class DateListener implements OnDateSetListener {
331 View mView;
332
333 public DateListener(View view) {
334 mView = view;
335 }
336
Andy McFadden9645d9c2011-05-16 15:28:15 -0700337 @Override
James Kung56f42bf2013-03-29 14:59:29 -0700338 public void onDateSet(DatePickerDialog view, int year, int month, int monthDay) {
Michael Chan0a01fd32011-01-23 15:13:28 -0800339 Log.d(TAG, "onDateSet: " + year + " " + month + " " + monthDay);
Erikeca82e92010-06-11 14:05:00 -0700340 // Cache the member variables locally to avoid inner class overhead.
341 Time startTime = mStartTime;
342 Time endTime = mEndTime;
343
344 // Cache the start and end millis so that we limit the number
345 // of calls to normalize() and toMillis(), which are fairly
346 // expensive.
347 long startMillis;
348 long endMillis;
349 if (mView == mStartDateButton) {
350 // The start date was changed.
351 int yearDuration = endTime.year - startTime.year;
352 int monthDuration = endTime.month - startTime.month;
353 int monthDayDuration = endTime.monthDay - startTime.monthDay;
354
355 startTime.year = year;
356 startTime.month = month;
357 startTime.monthDay = monthDay;
358 startMillis = startTime.normalize(true);
359
360 // Also update the end date to keep the duration constant.
361 endTime.year = year + yearDuration;
362 endTime.month = month + monthDuration;
363 endTime.monthDay = monthDay + monthDayDuration;
364 endMillis = endTime.normalize(true);
365
366 // If the start date has changed then update the repeats.
367 populateRepeats();
Michael Chan559cb862012-05-12 07:42:13 -0700368
369 // Update tz in case the start time switched from/to DLS
370 populateTimezone(startMillis);
Erikeca82e92010-06-11 14:05:00 -0700371 } else {
372 // The end date was changed.
373 startMillis = startTime.toMillis(true);
374 endTime.year = year;
375 endTime.month = month;
376 endTime.monthDay = monthDay;
377 endMillis = endTime.normalize(true);
378
379 // Do not allow an event to have an end time before the start
380 // time.
381 if (endTime.before(startTime)) {
382 endTime.set(startTime);
383 endMillis = startMillis;
384 }
Michael Chan559cb862012-05-12 07:42:13 -0700385 // Call populateTimezone if we support end time zone as well
Erikeca82e92010-06-11 14:05:00 -0700386 }
387
388 setDate(mStartDateButton, startMillis);
389 setDate(mEndDateButton, endMillis);
390 setTime(mEndTimeButton, endMillis); // In case end time had to be
391 // reset
Erik8a548962011-01-10 18:08:03 -0800392 updateHomeTime();
Erikeca82e92010-06-11 14:05:00 -0700393 }
394 }
395
396 // Fills in the date and time fields
397 private void populateWhen() {
398 long startMillis = mStartTime.toMillis(false /* use isDst */);
399 long endMillis = mEndTime.toMillis(false /* use isDst */);
400 setDate(mStartDateButton, startMillis);
401 setDate(mEndDateButton, endMillis);
402
403 setTime(mStartTimeButton, startMillis);
404 setTime(mEndTimeButton, endMillis);
405
406 mStartDateButton.setOnClickListener(new DateClickListener(mStartTime));
407 mEndDateButton.setOnClickListener(new DateClickListener(mEndTime));
408
409 mStartTimeButton.setOnClickListener(new TimeClickListener(mStartTime));
410 mEndTimeButton.setOnClickListener(new TimeClickListener(mEndTime));
411 }
412
Michael Chane6aa9372013-04-01 00:31:54 -0700413 // Implements OnTimeZoneSetListener
414 @Override
415 public void onTimeZoneSet(TimeZoneInfo tzi) {
416 setTimezone(tzi.mTzId);
417 updateHomeTime();
418 }
419
420 private void setTimezone(String timeZone) {
421 mTimezone = timeZone;
422 mStartTime.timezone = mTimezone;
423 long timeMillis = mStartTime.normalize(true);
424 mEndTime.timezone = mTimezone;
425 mEndTime.normalize(true);
426
427 populateTimezone(timeMillis);
428 }
429
Michael Chan559cb862012-05-12 07:42:13 -0700430 private void populateTimezone(long eventStartTime) {
Sam Blitzstein00040c62013-04-11 10:13:47 -0700431 if (mTzPickerUtils == null) {
432 mTzPickerUtils = new TimeZonePickerUtils(mActivity);
433 }
434 String displayName = mTzPickerUtils.getGmtDisplayName(mActivity, mTimezone, eventStartTime);
Michael Chane6aa9372013-04-01 00:31:54 -0700435
436 mTimezoneTextView.setText(displayName);
437 mTimezoneButton.setText(displayName);
438 }
439
Mason Tang4c8871b2010-08-10 10:17:43 -0700440 private void showTimezoneDialog() {
Michael Chane6aa9372013-04-01 00:31:54 -0700441 Bundle b = new Bundle();
442 b.putLong(TimeZonePickerDialog.BUNDLE_START_TIME_MILLIS, mStartTime.toMillis(false));
443 b.putString(TimeZonePickerDialog.BUNDLE_TIME_ZONE, mTimezone);
Michael Chanf428b1e2011-01-04 14:11:40 -0800444
Michael Chane6aa9372013-04-01 00:31:54 -0700445 FragmentManager fm = mActivity.getFragmentManager();
446 TimeZonePickerDialog tzpd = (TimeZonePickerDialog) fm
447 .findFragmentByTag(FRAG_TAG_TIME_ZONE_PICKER);
448 if (tzpd != null) {
449 tzpd.dismiss();
450 }
451 tzpd = new TimeZonePickerDialog();
452 tzpd.setArguments(b);
453 tzpd.setOnTimeZoneSetListener(EditEventView.this);
454 tzpd.show(fm, FRAG_TAG_TIME_ZONE_PICKER);
Mason Tang4c8871b2010-08-10 10:17:43 -0700455 }
456
Erikeca82e92010-06-11 14:05:00 -0700457 private void populateRepeats() {
Erikeca82e92010-06-11 14:05:00 -0700458 Resources r = mActivity.getResources();
Michael Chanb21c6382013-03-14 02:53:30 -0700459 String repeatString;
460 boolean enabled;
461 if (!TextUtils.isEmpty(mRrule)) {
462 repeatString = EventRecurrenceFormatter.getRepeatString(mActivity, r,
463 mEventRecurrence, true);
Erikeca82e92010-06-11 14:05:00 -0700464
Michael Chanb21c6382013-03-14 02:53:30 -0700465 if (repeatString == null) {
466 repeatString = r.getString(R.string.custom);
467 Log.e(TAG, "Can't generate display string for " + mRrule);
468 enabled = false;
Erikeca82e92010-06-11 14:05:00 -0700469 } else {
Michael Chanb21c6382013-03-14 02:53:30 -0700470 // TODO Should give option to clear/reset rrule
471 enabled = RecurrencePickerDialog.canHandleRecurrenceRule(mEventRecurrence);
472 if (!enabled) {
473 Log.e(TAG, "UI can't handle " + mRrule);
Erikeca82e92010-06-11 14:05:00 -0700474 }
475 }
Michael Chanb21c6382013-03-14 02:53:30 -0700476 } else {
477 repeatString = r.getString(R.string.does_not_repeat);
478 enabled = true;
Erikeca82e92010-06-11 14:05:00 -0700479 }
Michael Chanb21c6382013-03-14 02:53:30 -0700480
481 mRruleButton.setText(repeatString);
Erikeca82e92010-06-11 14:05:00 -0700482
483 // Don't allow the user to make exceptions recurring events.
RoboErik28dab652011-07-21 15:08:39 -0700484 if (mModel.mOriginalSyncId != null) {
Michael Chanb21c6382013-03-14 02:53:30 -0700485 enabled = false;
Erikeca82e92010-06-11 14:05:00 -0700486 }
Michael Chanb21c6382013-03-14 02:53:30 -0700487 mRruleButton.setOnClickListener(this);
488 mRruleButton.setEnabled(enabled);
Erikeca82e92010-06-11 14:05:00 -0700489 }
490
491 private class DateClickListener implements View.OnClickListener {
492 private Time mTime;
493
494 public DateClickListener(Time time) {
495 mTime = time;
496 }
497
Michael Chanb21c6382013-03-14 02:53:30 -0700498 @Override
Erikeca82e92010-06-11 14:05:00 -0700499 public void onClick(View v) {
James Kung56f42bf2013-03-29 14:59:29 -0700500
501 if (v == mStartDateButton) {
502 mDateSelectedWasStartDate = true;
RoboErikcf7b6912011-02-25 15:37:48 -0800503 } else {
James Kung56f42bf2013-03-29 14:59:29 -0700504 mDateSelectedWasStartDate = false;
RoboErikcf7b6912011-02-25 15:37:48 -0800505 }
James Kung56f42bf2013-03-29 14:59:29 -0700506
507 final DateListener listener = new DateListener(v);
508 if (mDatePickerDialog != null) {
509 mDatePickerDialog.dismiss();
510 }
511 mDatePickerDialog = DatePickerDialog.newInstance(listener,
512 mTime.year, mTime.month, mTime.monthDay);
513 mDatePickerDialog.setFirstDayOfWeek(Utils.getFirstDayOfWeekAsCalendar(mActivity));
514 mDatePickerDialog.setYearRange(Utils.YEAR_MIN, Utils.YEAR_MAX);
515 mDatePickerDialog.show(mActivity.getFragmentManager(), FRAG_TAG_DATE_PICKER);
Erikeca82e92010-06-11 14:05:00 -0700516 }
517 }
518
James Kung09fbd8e2012-11-12 11:19:46 -0800519 public static class CalendarsAdapter extends ResourceCursorAdapter {
520 public CalendarsAdapter(Context context, int resourceId, Cursor c) {
521 super(context, resourceId, c);
Erikeca82e92010-06-11 14:05:00 -0700522 setDropDownViewResource(R.layout.calendars_dropdown_item);
523 }
524
525 @Override
526 public void bindView(View view, Context context, Cursor cursor) {
527 View colorBar = view.findViewById(R.id.color);
RoboErik0e1e6242011-05-05 15:25:02 -0700528 int colorColumn = cursor.getColumnIndexOrThrow(Calendars.CALENDAR_COLOR);
RoboErikef2add92011-06-09 14:49:53 -0700529 int nameColumn = cursor.getColumnIndexOrThrow(Calendars.CALENDAR_DISPLAY_NAME);
Erikeca82e92010-06-11 14:05:00 -0700530 int ownerColumn = cursor.getColumnIndexOrThrow(Calendars.OWNER_ACCOUNT);
531 if (colorBar != null) {
RoboErik4acb2fd2011-07-18 15:39:49 -0700532 colorBar.setBackgroundColor(Utils.getDisplayColorFromColor(cursor
533 .getInt(colorColumn)));
Erikeca82e92010-06-11 14:05:00 -0700534 }
535
536 TextView name = (TextView) view.findViewById(R.id.calendar_name);
537 if (name != null) {
538 String displayName = cursor.getString(nameColumn);
539 name.setText(displayName);
Erikeca82e92010-06-11 14:05:00 -0700540
541 TextView accountName = (TextView) view.findViewById(R.id.account_name);
542 if (accountName != null) {
Erikeca82e92010-06-11 14:05:00 -0700543 accountName.setText(cursor.getString(ownerColumn));
544 accountName.setVisibility(TextView.VISIBLE);
Erikeca82e92010-06-11 14:05:00 -0700545 }
546 }
547 }
548 }
549
Erik7b92da22010-09-23 14:55:22 -0700550 /**
551 * Does prep steps for saving a calendar event.
552 *
553 * This triggers a parse of the attendees list and checks if the event is
Erik5f620792010-10-27 12:42:01 -0700554 * ready to be saved. An event is ready to be saved so long as a model
555 * exists and has a calendar it can be associated with, either because it's
556 * an existing event or we've finished querying.
Erik7b92da22010-09-23 14:55:22 -0700557 *
Erik5f620792010-10-27 12:42:01 -0700558 * @return false if there is no model or no calendar had been loaded yet,
559 * true otherwise.
Erik7b92da22010-09-23 14:55:22 -0700560 */
561 public boolean prepareForSave() {
Erik5f620792010-10-27 12:42:01 -0700562 if (mModel == null || (mCalendarsCursor == null && mModel.mUri == null)) {
Erik7b92da22010-09-23 14:55:22 -0700563 return false;
564 }
Erik5f620792010-10-27 12:42:01 -0700565 return fillModelFromUI();
Erik7b92da22010-09-23 14:55:22 -0700566 }
567
Erika7694ee2010-12-07 15:46:18 -0800568 public boolean fillModelFromReadOnlyUi() {
569 if (mModel == null || (mCalendarsCursor == null && mModel.mUri == null)) {
570 return false;
571 }
Andy McFadden9645d9c2011-05-16 15:28:15 -0700572 mModel.mReminders = EventViewUtils.reminderItemsToReminders(
573 mReminderItems, mReminderMinuteValues, mReminderMethodValues);
RoboErike35a0322011-09-01 16:21:49 -0700574 mModel.mReminders.addAll(mUnsupportedReminders);
575 mModel.normalizeReminders();
Erika7694ee2010-12-07 15:46:18 -0800576 int status = EventInfoFragment.getResponseFromButtonId(
577 mResponseRadioGroup.getCheckedRadioButtonId());
578 if (status != Attendees.ATTENDEE_STATUS_NONE) {
579 mModel.mSelfAttendeeStatus = status;
580 }
581 return true;
582 }
583
Erikeca82e92010-06-11 14:05:00 -0700584 // This is called if the user clicks on one of the buttons: "Save",
585 // "Discard", or "Delete". This is also called if the user clicks
586 // on the "remove reminder" button.
Andy McFadden9645d9c2011-05-16 15:28:15 -0700587 @Override
Daisuke Miyakawabdcb9fc2010-10-18 13:10:56 -0700588 public void onClick(View view) {
Michael Chanb21c6382013-03-14 02:53:30 -0700589 if (view == mRruleButton) {
590 Bundle b = new Bundle();
591 b.putLong(RecurrencePickerDialog.BUNDLE_START_TIME_MILLIS,
592 mStartTime.toMillis(false));
593 b.putString(RecurrencePickerDialog.BUNDLE_TIME_ZONE, mStartTime.timezone);
594
595 // TODO may be more efficient to serialize and pass in EventRecurrence
596 b.putString(RecurrencePickerDialog.BUNDLE_RRULE, mRrule);
597
598 FragmentManager fm = mActivity.getFragmentManager();
599 RecurrencePickerDialog rpd = (RecurrencePickerDialog) fm
600 .findFragmentByTag(FRAG_TAG_RECUR_PICKER);
601 if (rpd != null) {
602 rpd.dismiss();
603 }
604 rpd = new RecurrencePickerDialog();
605 rpd.setArguments(b);
606 rpd.setOnRecurrenceSetListener(EditEventView.this);
607 rpd.show(fm, FRAG_TAG_RECUR_PICKER);
608 return;
609 }
Erikeca82e92010-06-11 14:05:00 -0700610
611 // This must be a click on one of the "remove reminder" buttons
Daisuke Miyakawabdcb9fc2010-10-18 13:10:56 -0700612 LinearLayout reminderItem = (LinearLayout) view.getParent();
Erikeca82e92010-06-11 14:05:00 -0700613 LinearLayout parent = (LinearLayout) reminderItem.getParent();
614 parent.removeView(reminderItem);
615 mReminderItems.remove(reminderItem);
616 updateRemindersVisibility(mReminderItems.size());
Sara Ting06f3d6d2012-03-23 17:19:22 -0700617 EventViewUtils.updateAddReminderButton(mView, mReminderItems, mModel.mCalendarMaxReminders);
Erikeca82e92010-06-11 14:05:00 -0700618 }
619
Michael Chanb21c6382013-03-14 02:53:30 -0700620 @Override
621 public void onRecurrenceSet(String rrule) {
622 Log.d(TAG, "Old rrule:" + mRrule);
623 Log.d(TAG, "New rrule:" + rrule);
624 mRrule = rrule;
625 if (mRrule != null) {
626 mEventRecurrence.parse(mRrule);
627 }
628 populateRepeats();
629 }
630
Erikeca82e92010-06-11 14:05:00 -0700631 // This is called if the user cancels the "No calendars" dialog.
632 // The "No calendars" dialog is shown if there are no syncable calendars.
Michael Chanddbf8082010-10-26 19:09:32 -0700633 @Override
Erikeca82e92010-06-11 14:05:00 -0700634 public void onCancel(DialogInterface dialog) {
635 if (dialog == mLoadingCalendarsDialog) {
636 mLoadingCalendarsDialog = null;
637 mSaveAfterQueryComplete = false;
638 } else if (dialog == mNoCalendarsDialog) {
Erik79f22812010-06-23 16:55:38 -0700639 mDone.setDoneCode(Utils.DONE_REVERT);
640 mDone.run();
Erikeca82e92010-06-11 14:05:00 -0700641 return;
642 }
643 }
644
645 // This is called if the user clicks on a dialog button.
Daisuke Miyakawabdcb9fc2010-10-18 13:10:56 -0700646 @Override
Erikeca82e92010-06-11 14:05:00 -0700647 public void onClick(DialogInterface dialog, int which) {
648 if (dialog == mNoCalendarsDialog) {
Erik79f22812010-06-23 16:55:38 -0700649 mDone.setDoneCode(Utils.DONE_REVERT);
650 mDone.run();
Daisuke Miyakawa464f3502010-09-21 22:12:55 -0700651 if (which == DialogInterface.BUTTON_POSITIVE) {
652 Intent nextIntent = new Intent(Settings.ACTION_ADD_ACCOUNT);
653 final String[] array = {"com.android.calendar"};
654 nextIntent.putExtra(Settings.EXTRA_AUTHORITIES, array);
RoboErik15b4dcc2011-09-19 16:21:27 -0700655 nextIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
Daisuke Miyakawa464f3502010-09-21 22:12:55 -0700656 mActivity.startActivity(nextIntent);
657 }
Erikeca82e92010-06-11 14:05:00 -0700658 }
659 }
660
661 // Goes through the UI elements and updates the model as necessary
Michael Chanddbf8082010-10-26 19:09:32 -0700662 private boolean fillModelFromUI() {
Erikeca82e92010-06-11 14:05:00 -0700663 if (mModel == null) {
664 return false;
665 }
Andy McFadden9645d9c2011-05-16 15:28:15 -0700666 mModel.mReminders = EventViewUtils.reminderItemsToReminders(mReminderItems,
667 mReminderMinuteValues, mReminderMethodValues);
RoboErike35a0322011-09-01 16:21:49 -0700668 mModel.mReminders.addAll(mUnsupportedReminders);
669 mModel.normalizeReminders();
Erikeca82e92010-06-11 14:05:00 -0700670 mModel.mHasAlarm = mReminderItems.size() > 0;
Michael Chan7858f772011-01-26 17:46:19 -0800671 mModel.mTitle = mTitleTextView.getText().toString();
Erikeca82e92010-06-11 14:05:00 -0700672 mModel.mAllDay = mAllDayCheckBox.isChecked();
Michael Chan7858f772011-01-26 17:46:19 -0800673 mModel.mLocation = mLocationTextView.getText().toString();
674 mModel.mDescription = mDescriptionTextView.getText().toString();
Erikcfa4b5b2010-12-10 15:14:10 -0800675 if (TextUtils.isEmpty(mModel.mLocation)) {
676 mModel.mLocation = null;
677 }
678 if (TextUtils.isEmpty(mModel.mDescription)) {
679 mModel.mDescription = null;
680 }
Michael Chanf92f46b2010-10-27 18:59:05 -0700681
682 int status = EventInfoFragment.getResponseFromButtonId(mResponseRadioGroup
683 .getCheckedRadioButtonId());
684 if (status != Attendees.ATTENDEE_STATUS_NONE) {
685 mModel.mSelfAttendeeStatus = status;
Erikd845fbe2010-08-12 11:20:01 -0700686 }
687
RoboErike0e2a4f2011-08-09 11:31:42 -0700688 if (mAttendeesList != null) {
RoboErik6a7003d2011-08-04 16:23:20 -0700689 mEmailValidator.setRemoveInvalid(true);
690 mAttendeesList.performValidation();
RoboErike0e2a4f2011-08-09 11:31:42 -0700691 mModel.mAttendeesList.clear();
RoboErik6a7003d2011-08-04 16:23:20 -0700692 mModel.addAttendees(mAttendeesList.getText().toString(), mEmailValidator);
RoboErike0e2a4f2011-08-09 11:31:42 -0700693 mEmailValidator.setRemoveInvalid(false);
Erikeca82e92010-06-11 14:05:00 -0700694 }
695
696 // If this was a new event we need to fill in the Calendar information
697 if (mModel.mUri == null) {
698 mModel.mCalendarId = mCalendarsSpinner.getSelectedItemId();
699 int calendarCursorPosition = mCalendarsSpinner.getSelectedItemPosition();
700 if (mCalendarsCursor.moveToPosition(calendarCursorPosition)) {
Erik7b92da22010-09-23 14:55:22 -0700701 String defaultCalendar = mCalendarsCursor.getString(
702 EditEventHelper.CALENDARS_INDEX_OWNER_ACCOUNT);
703 Utils.setSharedPreference(
704 mActivity, GeneralPreferences.KEY_DEFAULT_CALENDAR, defaultCalendar);
Erikeca82e92010-06-11 14:05:00 -0700705 mModel.mOwnerAccount = defaultCalendar;
706 mModel.mOrganizer = defaultCalendar;
707 mModel.mCalendarId = mCalendarsCursor.getLong(EditEventHelper.CALENDARS_INDEX_ID);
708 }
709 }
710
711 if (mModel.mAllDay) {
712 // Reset start and end time, increment the monthDay by 1, and set
713 // the timezone to UTC, as required for all-day events.
Mason Tang4c8871b2010-08-10 10:17:43 -0700714 mTimezone = Time.TIMEZONE_UTC;
Erikeca82e92010-06-11 14:05:00 -0700715 mStartTime.hour = 0;
716 mStartTime.minute = 0;
717 mStartTime.second = 0;
Mason Tang4c8871b2010-08-10 10:17:43 -0700718 mStartTime.timezone = mTimezone;
Erikeca82e92010-06-11 14:05:00 -0700719 mModel.mStart = mStartTime.normalize(true);
720
Erikeca82e92010-06-11 14:05:00 -0700721 mEndTime.hour = 0;
722 mEndTime.minute = 0;
723 mEndTime.second = 0;
Mason Tang4c8871b2010-08-10 10:17:43 -0700724 mEndTime.timezone = mTimezone;
Daisuke Miyakawa29190972010-10-27 13:14:38 -0700725 // When a user see the event duration as "X - Y" (e.g. Oct. 28 - Oct. 29), end time
726 // should be Y + 1 (Oct.30).
727 final long normalizedEndTimeMillis =
728 mEndTime.normalize(true) + DateUtils.DAY_IN_MILLIS;
729 if (normalizedEndTimeMillis < mModel.mStart) {
730 // mEnd should be midnight of the next day of mStart.
731 mModel.mEnd = mModel.mStart + DateUtils.DAY_IN_MILLIS;
732 } else {
733 mModel.mEnd = normalizedEndTimeMillis;
734 }
Erikeca82e92010-06-11 14:05:00 -0700735 } else {
Mason Tang4c8871b2010-08-10 10:17:43 -0700736 mStartTime.timezone = mTimezone;
737 mEndTime.timezone = mTimezone;
Erikeca82e92010-06-11 14:05:00 -0700738 mModel.mStart = mStartTime.toMillis(true);
739 mModel.mEnd = mEndTime.toMillis(true);
740 }
Mason Tang4c8871b2010-08-10 10:17:43 -0700741 mModel.mTimezone = mTimezone;
Michael Chanced0eb62011-05-31 18:12:52 -0700742 mModel.mAccessLevel = mAccessLevelSpinner.getSelectedItemPosition();
RoboErikc6680c52011-10-28 13:57:54 -0700743 // TODO set correct availability value
744 mModel.mAvailability = mAvailabilityValues.get(mAvailabilitySpinner
745 .getSelectedItemPosition());
Erikeca82e92010-06-11 14:05:00 -0700746
Michael Chanb21c6382013-03-14 02:53:30 -0700747 // rrrule
Erikeca82e92010-06-11 14:05:00 -0700748 // If we're making an exception we don't want it to be a repeating
749 // event.
750 if (mModification == EditEventHelper.MODIFY_SELECTED) {
Michael Chanb21c6382013-03-14 02:53:30 -0700751 mModel.mRrule = null;
Erikeca82e92010-06-11 14:05:00 -0700752 } else {
Michael Chanb21c6382013-03-14 02:53:30 -0700753 mModel.mRrule = mRrule;
Erikeca82e92010-06-11 14:05:00 -0700754 }
755
Erikeca82e92010-06-11 14:05:00 -0700756 return true;
757 }
758
Sam Blitzstein7674b692013-01-29 11:45:20 -0800759 public EditEventView(Activity activity, View view, EditDoneRunnable done,
James Kung56f42bf2013-03-29 14:59:29 -0700760 boolean timeSelectedWasStartTime, boolean dateSelectedWasStartDate) {
Erikeca82e92010-06-11 14:05:00 -0700761
762 mActivity = activity;
763 mView = view;
Erik79f22812010-06-23 16:55:38 -0700764 mDone = done;
Erikeca82e92010-06-11 14:05:00 -0700765
766 // cache top level view elements
767 mLoadingMessage = (TextView) view.findViewById(R.id.loading_message);
768 mScrollView = (ScrollView) view.findViewById(R.id.scroll_view);
769
770 // cache all the widgets
Michael Chan9d5f3512010-11-05 16:23:57 -0700771 mCalendarsSpinner = (Spinner) view.findViewById(R.id.calendars_spinner);
Sara Tingd19fa1c2012-12-05 11:02:11 -0800772 mTitleTextView = (TextView) view.findViewById(R.id.title);
Sara Ting4686b402012-11-13 13:52:54 -0800773 mLocationTextView = (AutoCompleteTextView) view.findViewById(R.id.location);
Erikeca82e92010-06-11 14:05:00 -0700774 mDescriptionTextView = (TextView) view.findViewById(R.id.description);
Michael Chan24ee3362010-11-01 19:06:01 -0700775 mTimezoneLabel = (TextView) view.findViewById(R.id.timezone_label);
Erikeca82e92010-06-11 14:05:00 -0700776 mStartDateButton = (Button) view.findViewById(R.id.start_date);
777 mEndDateButton = (Button) view.findViewById(R.id.end_date);
Michael Chan24ee3362010-11-01 19:06:01 -0700778 mWhenView = (TextView) mView.findViewById(R.id.when);
779 mTimezoneTextView = (TextView) mView.findViewById(R.id.timezone_textView);
Erikeca82e92010-06-11 14:05:00 -0700780 mStartTimeButton = (Button) view.findViewById(R.id.start_time);
781 mEndTimeButton = (Button) view.findViewById(R.id.end_time);
Michael Chan24ee3362010-11-01 19:06:01 -0700782 mTimezoneButton = (Button) view.findViewById(R.id.timezone_button);
Michael Chane6aa9372013-04-01 00:31:54 -0700783 mTimezoneButton.setOnClickListener(new View.OnClickListener() {
784 @Override
785 public void onClick(View v) {
786 showTimezoneDialog();
787 }
788 });
Michael Chanb20942b2011-09-08 00:52:18 -0700789 mTimezoneRow = view.findViewById(R.id.timezone_button_row);
Isaac Katzenelson476ba392011-04-07 13:33:56 -0700790 mStartTimeHome = (TextView) view.findViewById(R.id.start_time_home_tz);
791 mStartDateHome = (TextView) view.findViewById(R.id.start_date_home_tz);
792 mEndTimeHome = (TextView) view.findViewById(R.id.end_time_home_tz);
793 mEndDateHome = (TextView) view.findViewById(R.id.end_date_home_tz);
Erikeca82e92010-06-11 14:05:00 -0700794 mAllDayCheckBox = (CheckBox) view.findViewById(R.id.is_all_day);
Michael Chanb21c6382013-03-14 02:53:30 -0700795 mRruleButton = (Button) view.findViewById(R.id.rrule);
Michael Chanced0eb62011-05-31 18:12:52 -0700796 mAvailabilitySpinner = (Spinner) view.findViewById(R.id.availability);
797 mAccessLevelSpinner = (Spinner) view.findViewById(R.id.visibility);
Erikc07a57f2010-12-01 10:45:48 -0800798 mCalendarSelectorGroup = view.findViewById(R.id.calendar_selector_group);
Mindy Pereiraeb51f842011-11-11 14:45:54 -0800799 mCalendarSelectorWrapper = view.findViewById(R.id.calendar_selector_wrapper);
Erikc07a57f2010-12-01 10:45:48 -0800800 mCalendarStaticGroup = view.findViewById(R.id.calendar_group);
801 mRemindersGroup = view.findViewById(R.id.reminders_row);
802 mResponseGroup = view.findViewById(R.id.response_row);
803 mOrganizerGroup = view.findViewById(R.id.organizer_row);
Michael Chan1ab41bf2011-08-10 18:20:14 -0700804 mAttendeesGroup = view.findViewById(R.id.add_attendees_row);
Erika7694ee2010-12-07 15:46:18 -0800805 mLocationGroup = view.findViewById(R.id.where_row);
806 mDescriptionGroup = view.findViewById(R.id.description_row);
Isaac Katzenelson476ba392011-04-07 13:33:56 -0700807 mStartHomeGroup = view.findViewById(R.id.from_row_home_tz);
808 mEndHomeGroup = view.findViewById(R.id.to_row_home_tz);
RoboErik6a7003d2011-08-04 16:23:20 -0700809 mAttendeesList = (MultiAutoCompleteTextView) view.findViewById(R.id.attendees);
Erikc07a57f2010-12-01 10:45:48 -0800810
James Kung4afba182012-12-18 23:12:08 -0800811 mColorPickerNewEvent = view.findViewById(R.id.change_color_new_event);
812 mColorPickerExistingEvent = view.findViewById(R.id.change_color_existing_event);
813
Erikefa480c2011-01-13 11:57:25 -0800814 mTitleTextView.setTag(mTitleTextView.getBackground());
815 mLocationTextView.setTag(mLocationTextView.getBackground());
Sara Ting4686b402012-11-13 13:52:54 -0800816 mLocationAdapter = new EventLocationAdapter(activity);
817 mLocationTextView.setAdapter(mLocationAdapter);
818 mLocationTextView.setOnEditorActionListener(new OnEditorActionListener() {
819 @Override
820 public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
821 if (actionId == EditorInfo.IME_ACTION_DONE) {
822 // Dismiss the suggestions dropdown. Return false so the other
823 // side effects still occur (soft keyboard going away, etc.).
824 mLocationTextView.dismissDropDown();
825 }
826 return false;
827 }
828 });
829
Sam Blitzstein7cdfb4d2013-02-22 15:30:27 -0800830 mAvailabilityExplicitlySet = false;
831 mAllDayChangingAvailability = false;
832 mAvailabilityCurrentlySelected = -1;
833 mAvailabilitySpinner.setOnItemSelectedListener(
834 new OnItemSelectedListener() {
835 @Override
836 public void onItemSelected(AdapterView<?> parent,
837 View view, int position, long id) {
838 // The spinner's onItemSelected gets called while it is being
839 // initialized to the first item, and when we explicitly set it
840 // in the allDay checkbox toggling, so we need these checks to
841 // find out when the spinner is actually being clicked.
842
843 // Set the initial selection.
844 if (mAvailabilityCurrentlySelected == -1) {
845 mAvailabilityCurrentlySelected = position;
846 }
847
848 if (mAvailabilityCurrentlySelected != position &&
849 !mAllDayChangingAvailability) {
850 mAvailabilityExplicitlySet = true;
851 } else {
852 mAvailabilityCurrentlySelected = position;
853 mAllDayChangingAvailability = false;
854 }
855 }
856 @Override
857 public void onNothingSelected(AdapterView<?> arg0) { }
858 });
859
Sara Ting4686b402012-11-13 13:52:54 -0800860
Erikefa480c2011-01-13 11:57:25 -0800861 mDescriptionTextView.setTag(mDescriptionTextView.getBackground());
RoboErik6a7003d2011-08-04 16:23:20 -0700862 mAttendeesList.setTag(mAttendeesList.getBackground());
Erikefa480c2011-01-13 11:57:25 -0800863 mOriginalPadding[0] = mLocationTextView.getPaddingLeft();
864 mOriginalPadding[1] = mLocationTextView.getPaddingTop();
865 mOriginalPadding[2] = mLocationTextView.getPaddingRight();
866 mOriginalPadding[3] = mLocationTextView.getPaddingBottom();
Erikc07a57f2010-12-01 10:45:48 -0800867 mEditViewList.add(mTitleTextView);
868 mEditViewList.add(mLocationTextView);
869 mEditViewList.add(mDescriptionTextView);
RoboErik6a7003d2011-08-04 16:23:20 -0700870 mEditViewList.add(mAttendeesList);
Erikc07a57f2010-12-01 10:45:48 -0800871
872 mViewOnlyList.add(view.findViewById(R.id.when_row));
873 mViewOnlyList.add(view.findViewById(R.id.timezone_textview_row));
874
875 mEditOnlyList.add(view.findViewById(R.id.all_day_row));
876 mEditOnlyList.add(view.findViewById(R.id.availability_row));
877 mEditOnlyList.add(view.findViewById(R.id.visibility_row));
878 mEditOnlyList.add(view.findViewById(R.id.from_row));
879 mEditOnlyList.add(view.findViewById(R.id.to_row));
Michael Chanb20942b2011-09-08 00:52:18 -0700880 mEditOnlyList.add(mTimezoneRow);
Erik8a548962011-01-10 18:08:03 -0800881 mEditOnlyList.add(mStartHomeGroup);
882 mEditOnlyList.add(mEndHomeGroup);
Michael Chan352e1a22010-09-28 05:15:08 -0700883
Michael Chanf92f46b2010-10-27 18:59:05 -0700884 mResponseRadioGroup = (RadioGroup) view.findViewById(R.id.response_value);
Erikeca82e92010-06-11 14:05:00 -0700885 mRemindersContainer = (LinearLayout) view.findViewById(R.id.reminder_items_container);
Erikeca82e92010-06-11 14:05:00 -0700886
Erik8a548962011-01-10 18:08:03 -0800887 mTimezone = Utils.getTimeZone(activity, null);
RoboErik3a002712011-08-17 18:18:37 -0700888 mIsMultipane = activity.getResources().getBoolean(R.bool.tablet_config);
Erik8a548962011-01-10 18:08:03 -0800889 mStartTime = new Time(mTimezone);
890 mEndTime = new Time(mTimezone);
RoboErik6a7003d2011-08-04 16:23:20 -0700891 mEmailValidator = new Rfc822Validator(null);
892 initMultiAutoCompleteTextView((RecipientEditTextView) mAttendeesList);
Erikeca82e92010-06-11 14:05:00 -0700893
894 // Display loading screen
895 setModel(null);
Michael Chanb21c6382013-03-14 02:53:30 -0700896
897 FragmentManager fm = activity.getFragmentManager();
898 RecurrencePickerDialog rpd = (RecurrencePickerDialog) fm
899 .findFragmentByTag(FRAG_TAG_RECUR_PICKER);
900 if (rpd != null) {
901 rpd.setOnRecurrenceSetListener(this);
902 }
Michael Chane6aa9372013-04-01 00:31:54 -0700903 TimeZonePickerDialog tzpd = (TimeZonePickerDialog) fm
904 .findFragmentByTag(FRAG_TAG_TIME_ZONE_PICKER);
905 if (tzpd != null) {
906 tzpd.setOnTimeZoneSetListener(this);
907 }
Sam Blitzstein7674b692013-01-29 11:45:20 -0800908 TimePickerDialog tpd = (TimePickerDialog) fm.findFragmentByTag(FRAG_TAG_TIME_PICKER);
909 if (tpd != null) {
910 View v;
911 mTimeSelectedWasStartTime = timeSelectedWasStartTime;
912 if (timeSelectedWasStartTime) {
913 v = mStartTimeButton;
914 } else {
915 v = mEndTimeButton;
916 }
917 tpd.setOnTimeSetListener(new TimeListener(v));
918 }
James Kung56f42bf2013-03-29 14:59:29 -0700919 mDatePickerDialog = (DatePickerDialog) fm.findFragmentByTag(FRAG_TAG_DATE_PICKER);
920 if (mDatePickerDialog != null) {
921 View v;
922 mDateSelectedWasStartDate = dateSelectedWasStartDate;
923 if (dateSelectedWasStartDate) {
924 v = mStartDateButton;
925 } else {
926 v = mEndDateButton;
927 }
928 mDatePickerDialog.setOnDateSetListener(new DateListener(v));
929 }
Erikeca82e92010-06-11 14:05:00 -0700930 }
931
Andy McFadden9645d9c2011-05-16 15:28:15 -0700932
933 /**
934 * Loads an integer array asset into a list.
935 */
936 private static ArrayList<Integer> loadIntegerArray(Resources r, int resNum) {
937 int[] vals = r.getIntArray(resNum);
938 int size = vals.length;
939 ArrayList<Integer> list = new ArrayList<Integer>(size);
940
941 for (int i = 0; i < size; i++) {
942 list.add(vals[i]);
943 }
944
945 return list;
946 }
947
948 /**
949 * Loads a String array asset into a list.
950 */
951 private static ArrayList<String> loadStringArray(Resources r, int resNum) {
952 String[] labels = r.getStringArray(resNum);
953 ArrayList<String> list = new ArrayList<String>(Arrays.asList(labels));
954 return list;
955 }
956
RoboErikc6680c52011-10-28 13:57:54 -0700957 private void prepareAvailability() {
RoboErikc6680c52011-10-28 13:57:54 -0700958 Resources r = mActivity.getResources();
959
960 mAvailabilityValues = loadIntegerArray(r, R.array.availability_values);
961 mAvailabilityLabels = loadStringArray(r, R.array.availability);
Sam Blitzstein7cdfb4d2013-02-22 15:30:27 -0800962 // Copy the unadulterated availability labels for all-day toggling.
963 mOriginalAvailabilityLabels = new ArrayList<String>();
964 mOriginalAvailabilityLabels.addAll(mAvailabilityLabels);
RoboErikc6680c52011-10-28 13:57:54 -0700965
966 if (mModel.mCalendarAllowedAvailability != null) {
967 EventViewUtils.reduceMethodList(mAvailabilityValues, mAvailabilityLabels,
968 mModel.mCalendarAllowedAvailability);
969 }
970
Sam Blitzstein7cdfb4d2013-02-22 15:30:27 -0800971 mAvailabilityAdapter = new ArrayAdapter<String>(mActivity,
RoboErikc6680c52011-10-28 13:57:54 -0700972 android.R.layout.simple_spinner_item, mAvailabilityLabels);
Sam Blitzstein7cdfb4d2013-02-22 15:30:27 -0800973 mAvailabilityAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
974 mAvailabilitySpinner.setAdapter(mAvailabilityAdapter);
RoboErikc6680c52011-10-28 13:57:54 -0700975 }
976
Andy McFadden9645d9c2011-05-16 15:28:15 -0700977 /**
978 * Prepares the reminder UI elements.
979 * <p>
980 * (Re-)loads the minutes / methods lists from the XML assets, adds/removes items as
981 * needed for the current set of reminders and calendar properties, and then creates UI
982 * elements.
983 */
984 private void prepareReminders() {
985 CalendarEventModel model = mModel;
986 Resources r = mActivity.getResources();
987
988 // Load the labels and corresponding numeric values for the minutes and methods lists
989 // from the assets. If we're switching calendars, we need to clear and re-populate the
990 // lists (which may have elements added and removed based on calendar properties). This
991 // is mostly relevant for "methods", since we shouldn't have any "minutes" values in a
992 // new event that aren't in the default set.
993 mReminderMinuteValues = loadIntegerArray(r, R.array.reminder_minutes_values);
994 mReminderMinuteLabels = loadStringArray(r, R.array.reminder_minutes_labels);
995 mReminderMethodValues = loadIntegerArray(r, R.array.reminder_methods_values);
996 mReminderMethodLabels = loadStringArray(r, R.array.reminder_methods_labels);
997
998 // Remove any reminder methods that aren't allowed for this calendar. If this is
999 // a new event, mCalendarAllowedReminders may not be set the first time we're called.
1000 if (mModel.mCalendarAllowedReminders != null) {
1001 EventViewUtils.reduceMethodList(mReminderMethodValues, mReminderMethodLabels,
1002 mModel.mCalendarAllowedReminders);
1003 }
1004
1005 int numReminders = 0;
1006 if (model.mHasAlarm) {
1007 ArrayList<ReminderEntry> reminders = model.mReminders;
1008 numReminders = reminders.size();
1009 // Insert any minute values that aren't represented in the minutes list.
1010 for (ReminderEntry re : reminders) {
RoboErike35a0322011-09-01 16:21:49 -07001011 if (mReminderMethodValues.contains(re.getMethod())) {
1012 EventViewUtils.addMinutesToList(mActivity, mReminderMinuteValues,
1013 mReminderMinuteLabels, re.getMinutes());
1014 }
Andy McFadden9645d9c2011-05-16 15:28:15 -07001015 }
1016
1017 // Create a UI element for each reminder. We display all of the reminders we get
1018 // from the provider, even if the count exceeds the calendar maximum. (Also, for
1019 // a new event, we won't have a maxReminders value available.)
RoboErike35a0322011-09-01 16:21:49 -07001020 mUnsupportedReminders.clear();
Andy McFadden9645d9c2011-05-16 15:28:15 -07001021 for (ReminderEntry re : reminders) {
RoboErikcabc41a2011-09-08 13:34:32 -07001022 if (mReminderMethodValues.contains(re.getMethod())
1023 || re.getMethod() == Reminders.METHOD_DEFAULT) {
RoboErike35a0322011-09-01 16:21:49 -07001024 EventViewUtils.addReminder(mActivity, mScrollView, this, mReminderItems,
1025 mReminderMinuteValues, mReminderMinuteLabels, mReminderMethodValues,
RoboErik8b5743c2011-09-27 16:07:09 -07001026 mReminderMethodLabels, re, Integer.MAX_VALUE, null);
RoboErike35a0322011-09-01 16:21:49 -07001027 } else {
1028 // TODO figure out a way to display unsupported reminders
1029 mUnsupportedReminders.add(re);
1030 }
Andy McFadden9645d9c2011-05-16 15:28:15 -07001031 }
1032 }
1033
1034 updateRemindersVisibility(numReminders);
Sara Ting06f3d6d2012-03-23 17:19:22 -07001035 EventViewUtils.updateAddReminderButton(mView, mReminderItems, mModel.mCalendarMaxReminders);
Andy McFadden9645d9c2011-05-16 15:28:15 -07001036 }
1037
Erikeca82e92010-06-11 14:05:00 -07001038 /**
1039 * Fill in the view with the contents of the given event model. This allows
1040 * an edit view to be initialized before the event has been loaded. Passing
1041 * in null for the model will display a loading screen. A non-null model
1042 * will fill in the view's fields with the data contained in the model.
1043 *
1044 * @param model The event model to pull the data from
1045 */
1046 public void setModel(CalendarEventModel model) {
1047 mModel = model;
Dmitri Plotnikovee212202010-08-27 17:58:19 -07001048
1049 // Need to close the autocomplete adapter to prevent leaking cursors.
Mindy Pereira18cabd22011-06-09 18:31:23 -07001050 if (mAddressAdapter != null && mAddressAdapter instanceof EmailAddressAdapter) {
1051 ((EmailAddressAdapter)mAddressAdapter).close();
Dmitri Plotnikovee212202010-08-27 17:58:19 -07001052 mAddressAdapter = null;
1053 }
1054
Erikeca82e92010-06-11 14:05:00 -07001055 if (model == null) {
1056 // Display loading screen
1057 mLoadingMessage.setVisibility(View.VISIBLE);
1058 mScrollView.setVisibility(View.GONE);
Erikeca82e92010-06-11 14:05:00 -07001059 return;
1060 }
1061
Michael Chan18a3c2e2010-10-13 09:52:28 -07001062 boolean canRespond = EditEventHelper.canRespond(model);
1063
Erikeca82e92010-06-11 14:05:00 -07001064 long begin = model.mStart;
1065 long end = model.mEnd;
Mason Tang86cdba62010-08-17 11:19:32 -07001066 mTimezone = model.mTimezone; // this will be UTC for all day events
Erikeca82e92010-06-11 14:05:00 -07001067
1068 // Set up the starting times
1069 if (begin > 0) {
Mason Tang4c8871b2010-08-10 10:17:43 -07001070 mStartTime.timezone = mTimezone;
Erikeca82e92010-06-11 14:05:00 -07001071 mStartTime.set(begin);
Erikeca82e92010-06-11 14:05:00 -07001072 mStartTime.normalize(true);
1073 }
1074 if (end > 0) {
Mason Tang4c8871b2010-08-10 10:17:43 -07001075 mEndTime.timezone = mTimezone;
Erikeca82e92010-06-11 14:05:00 -07001076 mEndTime.set(end);
Erikeca82e92010-06-11 14:05:00 -07001077 mEndTime.normalize(true);
1078 }
Michael Chanb21c6382013-03-14 02:53:30 -07001079
1080 mRrule = model.mRrule;
1081 if (!TextUtils.isEmpty(mRrule)) {
1082 mEventRecurrence.parse(mRrule);
1083 }
1084
1085 if (mEventRecurrence.startDate == null) {
1086 mEventRecurrence.startDate = mStartTime;
Erikeca82e92010-06-11 14:05:00 -07001087 }
1088
1089 // If the user is allowed to change the attendees set up the view and
1090 // validator
Michael Chan352e1a22010-09-28 05:15:08 -07001091 if (!model.mHasAttendeeData) {
RoboErik6a7003d2011-08-04 16:23:20 -07001092 mAttendeesGroup.setVisibility(View.GONE);
Erikeca82e92010-06-11 14:05:00 -07001093 }
1094
Michael Chan24ee3362010-11-01 19:06:01 -07001095 mAllDayCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
Andy McFadden9645d9c2011-05-16 15:28:15 -07001096 @Override
Michael Chan24ee3362010-11-01 19:06:01 -07001097 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Erikc07a57f2010-12-01 10:45:48 -08001098 setAllDayViewsVisibility(isChecked);
Michael Chan24ee3362010-11-01 19:06:01 -07001099 }
1100 });
Erikeca82e92010-06-11 14:05:00 -07001101
RoboErike8e0fc62011-09-13 09:48:18 -07001102 boolean prevAllDay = mAllDayCheckBox.isChecked();
Michael Chan8b1119b2012-01-06 16:04:55 -08001103 mAllDay = false; // default to false. Let setAllDayViewsVisibility update it as needed
Erikeca82e92010-06-11 14:05:00 -07001104 if (model.mAllDay) {
1105 mAllDayCheckBox.setChecked(true);
Mason Tang86cdba62010-08-17 11:19:32 -07001106 // put things back in local time for all day events
Michael Chan559cb862012-05-12 07:42:13 -07001107 mTimezone = Utils.getTimeZone(mActivity, null);
Mason Tang86cdba62010-08-17 11:19:32 -07001108 mStartTime.timezone = mTimezone;
Mason Tang86cdba62010-08-17 11:19:32 -07001109 mEndTime.timezone = mTimezone;
1110 mEndTime.normalize(true);
Erikeca82e92010-06-11 14:05:00 -07001111 } else {
1112 mAllDayCheckBox.setChecked(false);
1113 }
RoboErike8e0fc62011-09-13 09:48:18 -07001114 // On a rotation we need to update the views but onCheckedChanged
1115 // doesn't get called
1116 if (prevAllDay == mAllDayCheckBox.isChecked()) {
1117 setAllDayViewsVisibility(prevAllDay);
1118 }
Erikeca82e92010-06-11 14:05:00 -07001119
Michael Chan559cb862012-05-12 07:42:13 -07001120 populateTimezone(mStartTime.normalize(true));
Mason Tang86cdba62010-08-17 11:19:32 -07001121
Daisuke Miyakawa4b441bd2010-09-16 14:55:36 -07001122 SharedPreferences prefs = GeneralPreferences.getSharedPreferences(mActivity);
Michael Chan9d8a3762011-03-29 15:40:24 -07001123 String defaultReminderString = prefs.getString(
1124 GeneralPreferences.KEY_DEFAULT_REMINDER, GeneralPreferences.NO_REMINDER_STRING);
1125 mDefaultReminderMinutes = Integer.parseInt(defaultReminderString);
Erikeca82e92010-06-11 14:05:00 -07001126
Andy McFadden9645d9c2011-05-16 15:28:15 -07001127 prepareReminders();
RoboErikc6680c52011-10-28 13:57:54 -07001128 prepareAvailability();
Erikeca82e92010-06-11 14:05:00 -07001129
Michael Chanb20942b2011-09-08 00:52:18 -07001130 View reminderAddButton = mView.findViewById(R.id.reminder_add);
Erikeca82e92010-06-11 14:05:00 -07001131 View.OnClickListener addReminderOnClickListener = new View.OnClickListener() {
Andy McFadden9645d9c2011-05-16 15:28:15 -07001132 @Override
Erikeca82e92010-06-11 14:05:00 -07001133 public void onClick(View v) {
1134 addReminder();
1135 }
1136 };
Michael Chan352e1a22010-09-28 05:15:08 -07001137 reminderAddButton.setOnClickListener(addReminderOnClickListener);
Erikeca82e92010-06-11 14:05:00 -07001138
Michael Chanb20942b2011-09-08 00:52:18 -07001139 if (!mIsMultipane) {
1140 mView.findViewById(R.id.is_all_day_label).setOnClickListener(
1141 new View.OnClickListener() {
1142 @Override
1143 public void onClick(View v) {
1144 mAllDayCheckBox.setChecked(!mAllDayCheckBox.isChecked());
1145 }
1146 });
1147 }
1148
Michael Chanb7c1fcc2011-10-03 15:37:18 -07001149 if (model.mTitle != null) {
1150 mTitleTextView.setTextKeepState(model.mTitle);
1151 }
1152
Michael Chan352e1a22010-09-28 05:15:08 -07001153 if (model.mIsOrganizer || TextUtils.isEmpty(model.mOrganizer)
1154 || model.mOrganizer.endsWith(GOOGLE_SECONDARY_CALENDAR)) {
1155 mView.findViewById(R.id.organizer_label).setVisibility(View.GONE);
1156 mView.findViewById(R.id.organizer).setVisibility(View.GONE);
Isaac Katzenelson2e46cff2011-06-09 18:02:44 -07001157 mOrganizerGroup.setVisibility(View.GONE);
Michael Chan352e1a22010-09-28 05:15:08 -07001158 } else {
1159 ((TextView) mView.findViewById(R.id.organizer)).setText(model.mOrganizerDisplayName);
1160 }
Michael Chanb7c1fcc2011-10-03 15:37:18 -07001161
1162 if (model.mLocation != null) {
1163 mLocationTextView.setTextKeepState(model.mLocation);
1164 }
1165
1166 if (model.mDescription != null) {
1167 mDescriptionTextView.setTextKeepState(model.mDescription);
1168 }
1169
RoboErikc6680c52011-10-28 13:57:54 -07001170 int availIndex = mAvailabilityValues.indexOf(model.mAvailability);
1171 if (availIndex != -1) {
1172 mAvailabilitySpinner.setSelection(availIndex);
1173 }
Michael Chanced0eb62011-05-31 18:12:52 -07001174 mAccessLevelSpinner.setSelection(model.mAccessLevel);
Michael Chanf92f46b2010-10-27 18:59:05 -07001175
Michael Chan352e1a22010-09-28 05:15:08 -07001176 View responseLabel = mView.findViewById(R.id.response_label);
Michael Chan18a3c2e2010-10-13 09:52:28 -07001177 if (canRespond) {
Michael Chanf92f46b2010-10-27 18:59:05 -07001178 int buttonToCheck = EventInfoFragment
1179 .findButtonIdForResponse(model.mSelfAttendeeStatus);
1180 mResponseRadioGroup.check(buttonToCheck); // -1 clear all radio buttons
1181 mResponseRadioGroup.setVisibility(View.VISIBLE);
Michael Chan352e1a22010-09-28 05:15:08 -07001182 responseLabel.setVisibility(View.VISIBLE);
Michael Chan352e1a22010-09-28 05:15:08 -07001183 } else {
1184 responseLabel.setVisibility(View.GONE);
Michael Chanf92f46b2010-10-27 18:59:05 -07001185 mResponseRadioGroup.setVisibility(View.GONE);
RoboErik13ea4e32011-08-08 10:40:38 -07001186 mResponseGroup.setVisibility(View.GONE);
Michael Chan352e1a22010-09-28 05:15:08 -07001187 }
Erikeca82e92010-06-11 14:05:00 -07001188
1189 if (model.mUri != null) {
1190 // This is an existing event so hide the calendar spinner
1191 // since we can't change the calendar.
Michael Chan9d5f3512010-11-05 16:23:57 -07001192 View calendarGroup = mView.findViewById(R.id.calendar_selector_group);
1193 calendarGroup.setVisibility(View.GONE);
1194 TextView tv = (TextView) mView.findViewById(R.id.calendar_textview);
1195 tv.setText(model.mCalendarDisplayName);
RoboErik0d865f32011-08-09 10:17:25 -07001196 tv = (TextView) mView.findViewById(R.id.calendar_textview_secondary);
1197 if (tv != null) {
1198 tv.setText(model.mOwnerAccount);
1199 }
Michael Chan9d5f3512010-11-05 16:23:57 -07001200 } else {
Erikeca82e92010-06-11 14:05:00 -07001201 View calendarGroup = mView.findViewById(R.id.calendar_group);
1202 calendarGroup.setVisibility(View.GONE);
Erikeca82e92010-06-11 14:05:00 -07001203 }
James Kungf56b1492013-03-07 15:10:13 -08001204 if (model.isEventColorInitialized()) {
1205 updateHeadlineColor(model, model.getEventColor());
1206 }
Erikeca82e92010-06-11 14:05:00 -07001207
Erik8a548962011-01-10 18:08:03 -08001208 populateWhen();
Erikeca82e92010-06-11 14:05:00 -07001209 populateRepeats();
Erikd845fbe2010-08-12 11:20:01 -07001210 updateAttendees(model.mAttendeesList);
Michael Chanddbf8082010-10-26 19:09:32 -07001211
Erikc07a57f2010-12-01 10:45:48 -08001212 updateView();
Erikeca82e92010-06-11 14:05:00 -07001213 mScrollView.setVisibility(View.VISIBLE);
1214 mLoadingMessage.setVisibility(View.GONE);
Erik259b8f92011-01-12 14:01:12 -08001215 sendAccessibilityEvent();
1216 }
1217
James Kung4afba182012-12-18 23:12:08 -08001218 public void updateHeadlineColor(CalendarEventModel model, int displayColor) {
1219 if (model.mUri != null) {
1220 if (mIsMultipane) {
1221 mView.findViewById(R.id.calendar_textview_with_colorpicker)
1222 .setBackgroundColor(displayColor);
1223 } else {
1224 mView.findViewById(R.id.calendar_group).setBackgroundColor(displayColor);
1225 }
1226 } else {
James Kung317f6612013-03-05 16:29:14 -08001227 setSpinnerBackgroundColor(displayColor);
1228 }
1229 }
1230
1231 private void setSpinnerBackgroundColor(int displayColor) {
1232 if (mIsMultipane) {
1233 mCalendarSelectorWrapper.setBackgroundColor(displayColor);
1234 } else {
1235 mCalendarSelectorGroup.setBackgroundColor(displayColor);
James Kung4afba182012-12-18 23:12:08 -08001236 }
1237 }
1238
Erik259b8f92011-01-12 14:01:12 -08001239 private void sendAccessibilityEvent() {
Andy McFaddenef31a982011-06-09 07:38:56 -07001240 AccessibilityManager am =
1241 (AccessibilityManager) mActivity.getSystemService(Service.ACCESSIBILITY_SERVICE);
Erik259b8f92011-01-12 14:01:12 -08001242 if (!am.isEnabled() || mModel == null) {
1243 return;
1244 }
1245 StringBuilder b = new StringBuilder();
1246 addFieldsRecursive(b, mView);
1247 CharSequence msg = b.toString();
1248
1249 AccessibilityEvent event = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_FOCUSED);
1250 event.setClassName(getClass().getName());
1251 event.setPackageName(mActivity.getPackageName());
1252 event.getText().add(msg);
1253 event.setAddedCount(msg.length());
1254
1255 am.sendAccessibilityEvent(event);
1256 }
1257
1258 private void addFieldsRecursive(StringBuilder b, View v) {
1259 if (v == null || v.getVisibility() != View.VISIBLE) {
1260 return;
1261 }
1262 if (v instanceof TextView) {
1263 CharSequence tv = ((TextView) v).getText();
1264 if (!TextUtils.isEmpty(tv.toString().trim())) {
1265 b.append(tv + PERIOD_SPACE);
1266 }
1267 } else if (v instanceof RadioGroup) {
1268 RadioGroup rg = (RadioGroup) v;
1269 int id = rg.getCheckedRadioButtonId();
1270 if (id != View.NO_ID) {
1271 b.append(((RadioButton) (v.findViewById(id))).getText() + PERIOD_SPACE);
1272 }
1273 } else if (v instanceof Spinner) {
1274 Spinner s = (Spinner) v;
1275 if (s.getSelectedItem() instanceof String) {
1276 String str = ((String) (s.getSelectedItem())).trim();
1277 if (!TextUtils.isEmpty(str)) {
1278 b.append(str + PERIOD_SPACE);
1279 }
1280 }
1281 } else if (v instanceof ViewGroup) {
1282 ViewGroup vg = (ViewGroup) v;
1283 int children = vg.getChildCount();
1284 for (int i = 0; i < children; i++) {
1285 addFieldsRecursive(b, vg.getChildAt(i));
1286 }
1287 }
Erikeca82e92010-06-11 14:05:00 -07001288 }
1289
Michael Chan24ee3362010-11-01 19:06:01 -07001290 /**
Erikc07a57f2010-12-01 10:45:48 -08001291 * Creates a single line string for the time/duration
Michael Chan24ee3362010-11-01 19:06:01 -07001292 */
Erikc07a57f2010-12-01 10:45:48 -08001293 protected void setWhenString() {
1294 String when;
1295 int flags = DateUtils.FORMAT_SHOW_DATE;
RoboErika99feb92011-02-09 18:03:24 -08001296 String tz = mTimezone;
Erikc07a57f2010-12-01 10:45:48 -08001297 if (mModel.mAllDay) {
RoboErika99feb92011-02-09 18:03:24 -08001298 flags |= DateUtils.FORMAT_SHOW_WEEKDAY;
1299 tz = Time.TIMEZONE_UTC;
Erikc07a57f2010-12-01 10:45:48 -08001300 } else {
1301 flags |= DateUtils.FORMAT_SHOW_TIME;
1302 if (DateFormat.is24HourFormat(mActivity)) {
1303 flags |= DateUtils.FORMAT_24HOUR;
1304 }
1305 }
Michael Chan24ee3362010-11-01 19:06:01 -07001306 long startMillis = mStartTime.normalize(true);
1307 long endMillis = mEndTime.normalize(true);
Erik8a548962011-01-10 18:08:03 -08001308 mSB.setLength(0);
RoboErika99feb92011-02-09 18:03:24 -08001309 when = DateUtils
1310 .formatDateRange(mActivity, mF, startMillis, endMillis, flags, tz).toString();
Erikc07a57f2010-12-01 10:45:48 -08001311 mWhenView.setText(when);
Michael Chan24ee3362010-11-01 19:06:01 -07001312 }
1313
Andy McFadden9645d9c2011-05-16 15:28:15 -07001314 /**
1315 * Configures the Calendars spinner. This is only done for new events, because only new
1316 * events allow you to select a calendar while editing an event.
1317 * <p>
1318 * We tuck a reference to a Cursor with calendar database data into the spinner, so that
1319 * we can easily extract calendar-specific values when the value changes (the spinner's
1320 * onItemSelected callback is configured).
1321 */
James Kung09fbd8e2012-11-12 11:19:46 -08001322 public void setCalendarsCursor(Cursor cursor, boolean userVisible, long selectedCalendarId) {
Erikeca82e92010-06-11 14:05:00 -07001323 // If there are no syncable calendars, then we cannot allow
1324 // creating a new event.
1325 mCalendarsCursor = cursor;
1326 if (cursor == null || cursor.getCount() == 0) {
1327 // Cancel the "loading calendars" dialog if it exists
1328 if (mSaveAfterQueryComplete) {
1329 mLoadingCalendarsDialog.cancel();
1330 }
Erik7b92da22010-09-23 14:55:22 -07001331 if (!userVisible) {
1332 return;
1333 }
Erikeca82e92010-06-11 14:05:00 -07001334 // Create an error message for the user that, when clicked,
1335 // will exit this activity without saving the event.
1336 AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
Erikf6b6fb32011-01-18 11:54:50 -08001337 builder.setTitle(R.string.no_syncable_calendars).setIconAttribute(
1338 android.R.attr.alertDialogIcon).setMessage(R.string.no_calendars_found)
Daisuke Miyakawa464f3502010-09-21 22:12:55 -07001339 .setPositiveButton(R.string.add_account, this)
Erikf6b6fb32011-01-18 11:54:50 -08001340 .setNegativeButton(android.R.string.no, this).setOnCancelListener(this);
Erikeca82e92010-06-11 14:05:00 -07001341 mNoCalendarsDialog = builder.show();
1342 return;
1343 }
1344
James Kung09fbd8e2012-11-12 11:19:46 -08001345 int selection;
1346 if (selectedCalendarId != -1) {
1347 selection = findSelectedCalendarPosition(cursor, selectedCalendarId);
1348 } else {
1349 selection = findDefaultCalendarPosition(cursor);
1350 }
Erikeca82e92010-06-11 14:05:00 -07001351
1352 // populate the calendars spinner
James Kung09fbd8e2012-11-12 11:19:46 -08001353 CalendarsAdapter adapter = new CalendarsAdapter(mActivity,
1354 R.layout.calendars_spinner_item, cursor);
Erikeca82e92010-06-11 14:05:00 -07001355 mCalendarsSpinner.setAdapter(adapter);
James Kung09fbd8e2012-11-12 11:19:46 -08001356 mCalendarsSpinner.setSelection(selection);
Erik3400a2b2010-12-06 17:48:44 -08001357 mCalendarsSpinner.setOnItemSelectedListener(this);
1358
Erikeca82e92010-06-11 14:05:00 -07001359 if (mSaveAfterQueryComplete) {
1360 mLoadingCalendarsDialog.cancel();
Erik7b92da22010-09-23 14:55:22 -07001361 if (prepareForSave() && fillModelFromUI()) {
1362 int exit = userVisible ? Utils.DONE_EXIT : 0;
1363 mDone.setDoneCode(Utils.DONE_SAVE | exit);
Erik79f22812010-06-23 16:55:38 -07001364 mDone.run();
Erik7b92da22010-09-23 14:55:22 -07001365 } else if (userVisible) {
1366 mDone.setDoneCode(Utils.DONE_EXIT);
Erik79f22812010-06-23 16:55:38 -07001367 mDone.run();
Erik7b92da22010-09-23 14:55:22 -07001368 } else if (Log.isLoggable(TAG, Log.DEBUG)) {
1369 Log.d(TAG, "SetCalendarsCursor:Save failed and unable to exit view");
Erikeca82e92010-06-11 14:05:00 -07001370 }
1371 return;
1372 }
1373 }
1374
Erikc07a57f2010-12-01 10:45:48 -08001375 /**
1376 * Updates the view based on {@link #mModification} and {@link #mModel}
1377 */
1378 public void updateView() {
1379 if (mModel == null) {
1380 return;
1381 }
1382 if (EditEventHelper.canModifyEvent(mModel)) {
1383 setViewStates(mModification);
1384 } else {
1385 setViewStates(Utils.MODIFY_UNINITIALIZED);
1386 }
1387 }
1388
1389 private void setViewStates(int mode) {
1390 // Extra canModify check just in case
1391 if (mode == Utils.MODIFY_UNINITIALIZED || !EditEventHelper.canModifyEvent(mModel)) {
1392 setWhenString();
1393
1394 for (View v : mViewOnlyList) {
1395 v.setVisibility(View.VISIBLE);
1396 }
1397 for (View v : mEditOnlyList) {
1398 v.setVisibility(View.GONE);
1399 }
1400 for (View v : mEditViewList) {
1401 v.setEnabled(false);
Erikefa480c2011-01-13 11:57:25 -08001402 v.setBackgroundDrawable(null);
Erikc07a57f2010-12-01 10:45:48 -08001403 }
1404 mCalendarSelectorGroup.setVisibility(View.GONE);
1405 mCalendarStaticGroup.setVisibility(View.VISIBLE);
Michael Chanb21c6382013-03-14 02:53:30 -07001406 mRruleButton.setEnabled(false);
1407 mRruleButton.setBackgroundDrawable(null);
Erikc07a57f2010-12-01 10:45:48 -08001408 if (EditEventHelper.canAddReminders(mModel)) {
1409 mRemindersGroup.setVisibility(View.VISIBLE);
1410 } else {
1411 mRemindersGroup.setVisibility(View.GONE);
1412 }
Erika7694ee2010-12-07 15:46:18 -08001413 if (TextUtils.isEmpty(mLocationTextView.getText())) {
1414 mLocationGroup.setVisibility(View.GONE);
1415 }
1416 if (TextUtils.isEmpty(mDescriptionTextView.getText())) {
1417 mDescriptionGroup.setVisibility(View.GONE);
1418 }
Erikc07a57f2010-12-01 10:45:48 -08001419 } else {
1420 for (View v : mViewOnlyList) {
1421 v.setVisibility(View.GONE);
1422 }
1423 for (View v : mEditOnlyList) {
1424 v.setVisibility(View.VISIBLE);
1425 }
1426 for (View v : mEditViewList) {
1427 v.setEnabled(true);
Erikefa480c2011-01-13 11:57:25 -08001428 if (v.getTag() != null) {
1429 v.setBackgroundDrawable((Drawable) v.getTag());
RoboErik0d865f32011-08-09 10:17:25 -07001430 v.setPadding(mOriginalPadding[0], mOriginalPadding[1], mOriginalPadding[2],
1431 mOriginalPadding[3]);
Erikefa480c2011-01-13 11:57:25 -08001432 }
Erikc07a57f2010-12-01 10:45:48 -08001433 }
1434 if (mModel.mUri == null) {
1435 mCalendarSelectorGroup.setVisibility(View.VISIBLE);
1436 mCalendarStaticGroup.setVisibility(View.GONE);
1437 } else {
1438 mCalendarSelectorGroup.setVisibility(View.GONE);
1439 mCalendarStaticGroup.setVisibility(View.VISIBLE);
1440 }
RoboErik28dab652011-07-21 15:08:39 -07001441 if (mModel.mOriginalSyncId == null) {
Michael Chanb21c6382013-03-14 02:53:30 -07001442 mRruleButton.setEnabled(true);
Erikc07a57f2010-12-01 10:45:48 -08001443 } else {
Michael Chanb21c6382013-03-14 02:53:30 -07001444 mRruleButton.setEnabled(false);
Erikc07a57f2010-12-01 10:45:48 -08001445 }
1446 mRemindersGroup.setVisibility(View.VISIBLE);
Erikc07a57f2010-12-01 10:45:48 -08001447
Erika7694ee2010-12-07 15:46:18 -08001448 mLocationGroup.setVisibility(View.VISIBLE);
1449 mDescriptionGroup.setVisibility(View.VISIBLE);
Erikc07a57f2010-12-01 10:45:48 -08001450 }
Mindy Pereira521e7b82012-05-09 13:04:23 -07001451 setAllDayViewsVisibility(mAllDayCheckBox.isChecked());
Erikc07a57f2010-12-01 10:45:48 -08001452 }
1453
Erikeca82e92010-06-11 14:05:00 -07001454 public void setModification(int modifyWhich) {
1455 mModification = modifyWhich;
Erikc07a57f2010-12-01 10:45:48 -08001456 updateView();
Erik8a548962011-01-10 18:08:03 -08001457 updateHomeTime();
Erikeca82e92010-06-11 14:05:00 -07001458 }
1459
James Kung09fbd8e2012-11-12 11:19:46 -08001460 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
Erikeca82e92010-06-11 14:05:00 -07001476 // 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
Erik7b92da22010-09-23 14:55:22 -07001483 String defaultCalendar = Utils.getSharedPreference(
Michael Chane98dca72012-06-16 08:22:47 -07001484 mActivity, GeneralPreferences.KEY_DEFAULT_CALENDAR, (String) null);
Erikeca82e92010-06-11 14:05:00 -07001485
James Kung09fbd8e2012-11-12 11:19:46 -08001486 int calendarsOwnerIndex = calendarsCursor.getColumnIndexOrThrow(Calendars.OWNER_ACCOUNT);
Sara Ting8286d2f2012-09-13 12:17:01 -07001487 int accountNameIndex = calendarsCursor.getColumnIndexOrThrow(Calendars.ACCOUNT_NAME);
Sara Ting844cac02012-09-18 11:02:13 -07001488 int accountTypeIndex = calendarsCursor.getColumnIndexOrThrow(Calendars.ACCOUNT_TYPE);
Erikeca82e92010-06-11 14:05:00 -07001489 int position = 0;
1490 calendarsCursor.moveToPosition(-1);
1491 while (calendarsCursor.moveToNext()) {
James Kung09fbd8e2012-11-12 11:19:46 -08001492 String calendarOwner = calendarsCursor.getString(calendarsOwnerIndex);
Sara Ting8286d2f2012-09-13 12:17:01 -07001493 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 &&
Sara Ting844cac02012-09-18 11:02:13 -07001497 calendarOwner.equals(calendarsCursor.getString(accountNameIndex)) &&
1498 !CalendarContract.ACCOUNT_TYPE_LOCAL.equals(
1499 calendarsCursor.getString(accountTypeIndex))) {
Sara Ting8286d2f2012-09-13 12:17:01 -07001500 return position;
1501 }
1502 } else if (defaultCalendar.equals(calendarOwner)) {
1503 // Found the default calendar.
Erikeca82e92010-06-11 14:05:00 -07001504 return position;
1505 }
1506 position++;
1507 }
1508 return 0;
1509 }
1510
Michael Chanddbf8082010-10-26 19:09:32 -07001511 private void updateAttendees(HashMap<String, Attendee> attendeesList) {
RoboErik6a7003d2011-08-04 16:23:20 -07001512 if (attendeesList == null || attendeesList.isEmpty()) {
1513 return;
1514 }
1515 mAttendeesList.setText(null);
1516 for (Attendee attendee : attendeesList.values()) {
James Kungbb90a7d2013-04-04 15:29:45 -07001517
1518 // TODO: Please remove separator when Calendar uses the chips MR2 project
1519
1520 // Adding a comma separator between email addresses to prevent a chips MR1.1 bug
1521 // in which email addresses are concatenated together with no separator.
1522 mAttendeesList.append(attendee.mEmail + ", ");
RoboErik6a7003d2011-08-04 16:23:20 -07001523 }
Erikd845fbe2010-08-12 11:20:01 -07001524 }
Mason Tang4c8871b2010-08-10 10:17:43 -07001525
Erikeca82e92010-06-11 14:05:00 -07001526 private void updateRemindersVisibility(int numReminders) {
1527 if (numReminders == 0) {
Erikeca82e92010-06-11 14:05:00 -07001528 mRemindersContainer.setVisibility(View.GONE);
1529 } else {
Erikeca82e92010-06-11 14:05:00 -07001530 mRemindersContainer.setVisibility(View.VISIBLE);
1531 }
1532 }
1533
Andy McFadden9645d9c2011-05-16 15:28:15 -07001534 /**
1535 * Add a new reminder when the user hits the "add reminder" button. We use the default
1536 * reminder time and method.
1537 */
Michael Chanddbf8082010-10-26 19:09:32 -07001538 private void addReminder() {
Erikeca82e92010-06-11 14:05:00 -07001539 // TODO: when adding a new reminder, make it different from the
1540 // last one in the list (if any).
Michael Chan9d8a3762011-03-29 15:40:24 -07001541 if (mDefaultReminderMinutes == GeneralPreferences.NO_REMINDER) {
Erik79f22812010-06-23 16:55:38 -07001542 EventViewUtils.addReminder(mActivity, mScrollView, this, mReminderItems,
Andy McFaddenfea9af52011-05-13 15:40:10 -07001543 mReminderMinuteValues, mReminderMinuteLabels,
Andy McFadden9645d9c2011-05-16 15:28:15 -07001544 mReminderMethodValues, mReminderMethodLabels,
1545 ReminderEntry.valueOf(GeneralPreferences.REMINDER_DEFAULT_TIME),
RoboErik8b5743c2011-09-27 16:07:09 -07001546 mModel.mCalendarMaxReminders, null);
Erikeca82e92010-06-11 14:05:00 -07001547 } else {
Erik79f22812010-06-23 16:55:38 -07001548 EventViewUtils.addReminder(mActivity, mScrollView, this, mReminderItems,
Andy McFaddenfea9af52011-05-13 15:40:10 -07001549 mReminderMinuteValues, mReminderMinuteLabels,
Andy McFadden9645d9c2011-05-16 15:28:15 -07001550 mReminderMethodValues, mReminderMethodLabels,
1551 ReminderEntry.valueOf(mDefaultReminderMinutes),
RoboErik8b5743c2011-09-27 16:07:09 -07001552 mModel.mCalendarMaxReminders, null);
Erikeca82e92010-06-11 14:05:00 -07001553 }
1554 updateRemindersVisibility(mReminderItems.size());
Sara Ting06f3d6d2012-03-23 17:19:22 -07001555 EventViewUtils.updateAddReminderButton(mView, mReminderItems, mModel.mCalendarMaxReminders);
Erikeca82e92010-06-11 14:05:00 -07001556 }
1557
Erikeca82e92010-06-11 14:05:00 -07001558 // From com.google.android.gm.ComposeActivity
RoboErik6a7003d2011-08-04 16:23:20 -07001559 private MultiAutoCompleteTextView initMultiAutoCompleteTextView(RecipientEditTextView list) {
Mindy Pereira18cabd22011-06-09 18:31:23 -07001560 if (ChipsUtil.supportsChipsUi()) {
1561 mAddressAdapter = new RecipientAdapter(mActivity);
1562 list.setAdapter((BaseRecipientAdapter) mAddressAdapter);
RoboErikce8a8332011-08-09 17:13:01 -07001563 list.setOnFocusListShrinkRecipients(false);
Mindy Pereira18cabd22011-06-09 18:31:23 -07001564 } else {
1565 mAddressAdapter = new EmailAddressAdapter(mActivity);
1566 list.setAdapter((EmailAddressAdapter)mAddressAdapter);
1567 }
Erikeca82e92010-06-11 14:05:00 -07001568 list.setTokenizer(new Rfc822Tokenizer());
1569 list.setValidator(mEmailValidator);
1570
1571 // NOTE: assumes no other filters are set
1572 list.setFilters(sRecipientFilters);
1573
1574 return list;
1575 }
1576
1577 /**
1578 * From com.google.android.gm.ComposeActivity Implements special address
1579 * cleanup rules: The first space key entry following an "@" symbol that is
1580 * followed by any combination of letters and symbols, including one+ dots
1581 * and zero commas, should insert an extra comma (followed by the space).
1582 */
Erik7b92da22010-09-23 14:55:22 -07001583 private static InputFilter[] sRecipientFilters = new InputFilter[] { new Rfc822InputFilter() };
Erikeca82e92010-06-11 14:05:00 -07001584
1585 private void setDate(TextView view, long millis) {
1586 int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR
1587 | DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_ABBREV_MONTH
1588 | DateUtils.FORMAT_ABBREV_WEEKDAY;
Mason Tang4c8871b2010-08-10 10:17:43 -07001589
1590 // Unfortunately, DateUtils doesn't support a timezone other than the
1591 // default timezone provided by the system, so we have this ugly hack
1592 // here to trick it into formatting our time correctly. In order to
1593 // prevent all sorts of craziness, we synchronize on the TimeZone class
1594 // to prevent other threads from reading an incorrect timezone from
1595 // calls to TimeZone#getDefault()
1596 // TODO fix this if/when DateUtils allows for passing in a timezone
1597 String dateString;
1598 synchronized (TimeZone.class) {
1599 TimeZone.setDefault(TimeZone.getTimeZone(mTimezone));
1600 dateString = DateUtils.formatDateTime(mActivity, millis, flags);
1601 // setting the default back to null restores the correct behavior
1602 TimeZone.setDefault(null);
1603 }
1604 view.setText(dateString);
Erikeca82e92010-06-11 14:05:00 -07001605 }
1606
1607 private void setTime(TextView view, long millis) {
1608 int flags = DateUtils.FORMAT_SHOW_TIME;
1609 if (DateFormat.is24HourFormat(mActivity)) {
1610 flags |= DateUtils.FORMAT_24HOUR;
1611 }
Mason Tang4c8871b2010-08-10 10:17:43 -07001612
1613 // Unfortunately, DateUtils doesn't support a timezone other than the
1614 // default timezone provided by the system, so we have this ugly hack
1615 // here to trick it into formatting our time correctly. In order to
1616 // prevent all sorts of craziness, we synchronize on the TimeZone class
1617 // to prevent other threads from reading an incorrect timezone from
1618 // calls to TimeZone#getDefault()
1619 // TODO fix this if/when DateUtils allows for passing in a timezone
1620 String timeString;
1621 synchronized (TimeZone.class) {
1622 TimeZone.setDefault(TimeZone.getTimeZone(mTimezone));
1623 timeString = DateUtils.formatDateTime(mActivity, millis, flags);
1624 TimeZone.setDefault(null);
1625 }
1626 view.setText(timeString);
1627 }
1628
Erikc07a57f2010-12-01 10:45:48 -08001629 /**
1630 * @param isChecked
1631 */
1632 protected void setAllDayViewsVisibility(boolean isChecked) {
1633 if (isChecked) {
1634 if (mEndTime.hour == 0 && mEndTime.minute == 0) {
Michael Chan8b1119b2012-01-06 16:04:55 -08001635 if (mAllDay != isChecked) {
1636 mEndTime.monthDay--;
1637 }
1638
Erikc07a57f2010-12-01 10:45:48 -08001639 long endMillis = mEndTime.normalize(true);
1640
1641 // Do not allow an event to have an end time
1642 // before the
1643 // start time.
1644 if (mEndTime.before(mStartTime)) {
1645 mEndTime.set(mStartTime);
1646 endMillis = mEndTime.normalize(true);
1647 }
1648 setDate(mEndDateButton, endMillis);
1649 setTime(mEndTimeButton, endMillis);
1650 }
1651
1652 mStartTimeButton.setVisibility(View.GONE);
1653 mEndTimeButton.setVisibility(View.GONE);
Michael Chanb20942b2011-09-08 00:52:18 -07001654 mTimezoneRow.setVisibility(View.GONE);
Erikc07a57f2010-12-01 10:45:48 -08001655 } else {
1656 if (mEndTime.hour == 0 && mEndTime.minute == 0) {
Michael Chan8b1119b2012-01-06 16:04:55 -08001657 if (mAllDay != isChecked) {
1658 mEndTime.monthDay++;
1659 }
1660
Erikc07a57f2010-12-01 10:45:48 -08001661 long endMillis = mEndTime.normalize(true);
1662 setDate(mEndDateButton, endMillis);
1663 setTime(mEndTimeButton, endMillis);
1664 }
1665 mStartTimeButton.setVisibility(View.VISIBLE);
1666 mEndTimeButton.setVisibility(View.VISIBLE);
Michael Chanb20942b2011-09-08 00:52:18 -07001667 mTimezoneRow.setVisibility(View.VISIBLE);
Erikc07a57f2010-12-01 10:45:48 -08001668 }
Sam Blitzstein7cdfb4d2013-02-22 15:30:27 -08001669
1670 // If this is a new event, and if availability has not yet been
1671 // explicitly set, toggle busy/available as the inverse of all day.
1672 if (mModel.mUri == null && !mAvailabilityExplicitlySet) {
1673 // Values are from R.arrays.availability_values.
1674 // 0 = busy
1675 // 1 = available
1676 int newAvailabilityValue = isChecked? 1 : 0;
1677 if (mAvailabilityAdapter != null && mAvailabilityValues != null
1678 && mAvailabilityValues.contains(newAvailabilityValue)) {
1679 // We'll need to let the spinner's listener know that we're
1680 // explicitly toggling it.
1681 mAllDayChangingAvailability = true;
1682
1683 String newAvailabilityLabel = mOriginalAvailabilityLabels.get(newAvailabilityValue);
1684 int newAvailabilityPos = mAvailabilityAdapter.getPosition(newAvailabilityLabel);
1685 mAvailabilitySpinner.setSelection(newAvailabilityPos);
1686 }
1687 }
1688
Michael Chan8b1119b2012-01-06 16:04:55 -08001689 mAllDay = isChecked;
Erik8a548962011-01-10 18:08:03 -08001690 updateHomeTime();
Erikc07a57f2010-12-01 10:45:48 -08001691 }
Erik3400a2b2010-12-06 17:48:44 -08001692
James Kung4afba182012-12-18 23:12:08 -08001693 public void setColorPickerButtonStates(int[] eventColors) {
1694 if (eventColors == null || eventColors.length == 0) {
James Kunge134cd52013-02-13 11:33:49 -08001695 mColorPickerNewEvent.setVisibility(View.INVISIBLE);
James Kungc2ea1502013-02-14 17:57:44 -08001696 mColorPickerExistingEvent.setVisibility(View.GONE);
James Kung4afba182012-12-18 23:12:08 -08001697 } else {
James Kunge134cd52013-02-13 11:33:49 -08001698 mColorPickerNewEvent.setVisibility(View.VISIBLE);
1699 mColorPickerExistingEvent.setVisibility(View.VISIBLE);
James Kung4afba182012-12-18 23:12:08 -08001700 }
1701 }
1702
Erik3400a2b2010-12-06 17:48:44 -08001703 @Override
1704 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
Andy McFadden9645d9c2011-05-16 15:28:15 -07001705 // This is only used for the Calendar spinner in new events, and only fires when the
Isaac Katzenelson6a63b772011-09-28 15:56:33 -07001706 // calendar selection changes or on screen rotation
Erik3400a2b2010-12-06 17:48:44 -08001707 Cursor c = (Cursor) parent.getItemAtPosition(position);
Andy McFadden9645d9c2011-05-16 15:28:15 -07001708 if (c == null) {
1709 // TODO: can this happen? should we drop this check?
1710 Log.w(TAG, "Cursor not set on calendar item");
1711 return;
Erik3400a2b2010-12-06 17:48:44 -08001712 }
Andy McFadden9645d9c2011-05-16 15:28:15 -07001713
James Kung4afba182012-12-18 23:12:08 -08001714 // Do nothing if the selection didn't change so that reminders will not get lost
1715 int idColumn = c.getColumnIndexOrThrow(Calendars._ID);
1716 long calendarId = c.getLong(idColumn);
Andy McFadden9645d9c2011-05-16 15:28:15 -07001717 int colorColumn = c.getColumnIndexOrThrow(Calendars.CALENDAR_COLOR);
RoboErik4acb2fd2011-07-18 15:39:49 -07001718 int color = c.getInt(colorColumn);
1719 int displayColor = Utils.getDisplayColorFromColor(color);
Mindy Pereiraeb51f842011-11-11 14:45:54 -08001720
James Kung317f6612013-03-05 16:29:14 -08001721 // Prevents resetting of data (reminders, etc.) on orientation change.
James Kungf56b1492013-03-07 15:10:13 -08001722 if (calendarId == mModel.mCalendarId && mModel.isCalendarColorInitialized() &&
1723 displayColor == mModel.getCalendarColor()) {
James Kung317f6612013-03-05 16:29:14 -08001724 return;
RoboErik3a002712011-08-17 18:18:37 -07001725 }
Andy McFadden9645d9c2011-05-16 15:28:15 -07001726
James Kung317f6612013-03-05 16:29:14 -08001727 setSpinnerBackgroundColor(displayColor);
1728
Isaac Katzenelson6a63b772011-09-28 15:56:33 -07001729 mModel.mCalendarId = calendarId;
James Kungf56b1492013-03-07 15:10:13 -08001730 mModel.setCalendarColor(displayColor);
James Kung4afba182012-12-18 23:12:08 -08001731 mModel.mCalendarAccountName = c.getString(EditEventHelper.CALENDARS_INDEX_ACCOUNT_NAME);
1732 mModel.mCalendarAccountType = c.getString(EditEventHelper.CALENDARS_INDEX_ACCOUNT_TYPE);
James Kungf56b1492013-03-07 15:10:13 -08001733 mModel.setEventColor(mModel.getCalendarColor());
James Kung4afba182012-12-18 23:12:08 -08001734
1735 setColorPickerButtonStates(mModel.getCalendarEventColors());
1736
Andy McFadden9645d9c2011-05-16 15:28:15 -07001737 // Update the max/allowed reminders with the new calendar properties.
1738 int maxRemindersColumn = c.getColumnIndexOrThrow(Calendars.MAX_REMINDERS);
1739 mModel.mCalendarMaxReminders = c.getInt(maxRemindersColumn);
1740 int allowedRemindersColumn = c.getColumnIndexOrThrow(Calendars.ALLOWED_REMINDERS);
1741 mModel.mCalendarAllowedReminders = c.getString(allowedRemindersColumn);
RoboErikc6680c52011-10-28 13:57:54 -07001742 int allowedAttendeeTypesColumn = c.getColumnIndexOrThrow(Calendars.ALLOWED_ATTENDEE_TYPES);
1743 mModel.mCalendarAllowedAttendeeTypes = c.getString(allowedAttendeeTypesColumn);
1744 int allowedAvailabilityColumn = c.getColumnIndexOrThrow(Calendars.ALLOWED_AVAILABILITY);
1745 mModel.mCalendarAllowedAvailability = c.getString(allowedAvailabilityColumn);
Andy McFadden9645d9c2011-05-16 15:28:15 -07001746
1747 // Discard the current reminders and replace them with the model's default reminder set.
1748 // We could attempt to save & restore the reminders that have been added, but that's
1749 // probably more trouble than it's worth.
1750 mModel.mReminders.clear();
1751 mModel.mReminders.addAll(mModel.mDefaultReminders);
1752 mModel.mHasAlarm = mModel.mReminders.size() != 0;
1753
1754 // Update the UI elements.
1755 mReminderItems.clear();
1756 LinearLayout reminderLayout =
1757 (LinearLayout) mScrollView.findViewById(R.id.reminder_items_container);
1758 reminderLayout.removeAllViews();
1759 prepareReminders();
RoboErikc6680c52011-10-28 13:57:54 -07001760 prepareAvailability();
Erik3400a2b2010-12-06 17:48:44 -08001761 }
1762
Erik8a548962011-01-10 18:08:03 -08001763 /**
1764 * Checks if the start and end times for this event should be displayed in
1765 * the Calendar app's time zone as well and formats and displays them.
1766 */
1767 private void updateHomeTime() {
1768 String tz = Utils.getTimeZone(mActivity, null);
1769 if (!mAllDayCheckBox.isChecked() && !TextUtils.equals(tz, mTimezone)
1770 && mModification != EditEventHelper.MODIFY_UNINITIALIZED) {
1771 int flags = DateUtils.FORMAT_SHOW_TIME;
1772 boolean is24Format = DateFormat.is24HourFormat(mActivity);
1773 if (is24Format) {
1774 flags |= DateUtils.FORMAT_24HOUR;
1775 }
1776 long millisStart = mStartTime.toMillis(false);
1777 long millisEnd = mEndTime.toMillis(false);
1778
1779 boolean isDSTStart = mStartTime.isDst != 0;
1780 boolean isDSTEnd = mEndTime.isDst != 0;
1781
1782 // First update the start date and times
1783 String tzDisplay = TimeZone.getTimeZone(tz).getDisplayName(
1784 isDSTStart, TimeZone.SHORT, Locale.getDefault());
1785 StringBuilder time = new StringBuilder();
1786
1787 mSB.setLength(0);
1788 time.append(DateUtils
1789 .formatDateRange(mActivity, mF, millisStart, millisStart, flags, tz))
1790 .append(" ").append(tzDisplay);
1791 mStartTimeHome.setText(time.toString());
1792
1793 flags = DateUtils.FORMAT_ABBREV_ALL | DateUtils.FORMAT_SHOW_DATE
1794 | DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_SHOW_WEEKDAY;
1795 mSB.setLength(0);
1796 mStartDateHome
1797 .setText(DateUtils.formatDateRange(
1798 mActivity, mF, millisStart, millisStart, flags, tz).toString());
1799
1800 // Make any adjustments needed for the end times
1801 if (isDSTEnd != isDSTStart) {
1802 tzDisplay = TimeZone.getTimeZone(tz).getDisplayName(
1803 isDSTEnd, TimeZone.SHORT, Locale.getDefault());
1804 }
1805 flags = DateUtils.FORMAT_SHOW_TIME;
1806 if (is24Format) {
1807 flags |= DateUtils.FORMAT_24HOUR;
1808 }
1809
1810 // Then update the end times
1811 time.setLength(0);
1812 mSB.setLength(0);
1813 time.append(DateUtils.formatDateRange(
1814 mActivity, mF, millisEnd, millisEnd, flags, tz)).append(" ").append(tzDisplay);
1815 mEndTimeHome.setText(time.toString());
1816
1817 flags = DateUtils.FORMAT_ABBREV_ALL | DateUtils.FORMAT_SHOW_DATE
1818 | DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_SHOW_WEEKDAY;
1819 mSB.setLength(0);
1820 mEndDateHome.setText(DateUtils.formatDateRange(
1821 mActivity, mF, millisEnd, millisEnd, flags, tz).toString());
1822
1823 mStartHomeGroup.setVisibility(View.VISIBLE);
1824 mEndHomeGroup.setVisibility(View.VISIBLE);
1825 } else {
1826 mStartHomeGroup.setVisibility(View.GONE);
1827 mEndHomeGroup.setVisibility(View.GONE);
1828 }
1829 }
1830
Erik3400a2b2010-12-06 17:48:44 -08001831 @Override
1832 public void onNothingSelected(AdapterView<?> parent) {
Erik3400a2b2010-12-06 17:48:44 -08001833 }
Erikeca82e92010-06-11 14:05:00 -07001834}