Automated import from //branches/donutburger/...@141076,141076
[android/platform/packages/apps/Calendar.git] / src / com / android / calendar / EventInfoActivity.java
1 /*
2  * Copyright (C) 2007 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
17 package com.android.calendar;
18
19 import static android.provider.Calendar.EVENT_BEGIN_TIME;
20 import static android.provider.Calendar.EVENT_END_TIME;
21 import android.app.Activity;
22 import android.content.ContentResolver;
23 import android.content.ContentUris;
24 import android.content.ContentValues;
25 import android.content.Intent;
26 import android.content.SharedPreferences;
27 import android.content.res.Resources;
28 import android.database.Cursor;
29 import android.graphics.PorterDuff;
30 import android.net.Uri;
31 import android.os.Bundle;
32 import android.pim.EventRecurrence;
33 import android.preference.PreferenceManager;
34 import android.provider.Calendar;
35 import android.provider.Calendar.Attendees;
36 import android.provider.Calendar.Calendars;
37 import android.provider.Calendar.Events;
38 import android.provider.Calendar.Reminders;
39 import android.text.format.DateFormat;
40 import android.text.format.DateUtils;
41 import android.text.format.Time;
42 import android.util.Log;
43 import android.view.KeyEvent;
44 import android.view.Menu;
45 import android.view.MenuItem;
46 import android.view.View;
47 import android.widget.AdapterView;
48 import android.widget.ArrayAdapter;
49 import android.widget.ImageButton;
50 import android.widget.LinearLayout;
51 import android.widget.Spinner;
52 import android.widget.TextView;
53 import android.widget.Toast;
54
55 import java.util.ArrayList;
56 import java.util.Arrays;
57
58 public class EventInfoActivity extends Activity implements View.OnClickListener,
59         AdapterView.OnItemSelectedListener {
60     private static final int MAX_REMINDERS = 5;
61
62     /**
63      * These are the corresponding indices into the array of strings
64      * "R.array.change_response_labels" in the resource file.
65      */
66     static final int UPDATE_SINGLE = 0;
67     static final int UPDATE_ALL = 1;
68
69     private static final String[] EVENT_PROJECTION = new String[] {
70         Events._ID,                  // 0  do not remove; used in DeleteEventHelper
71         Events.TITLE,                // 1  do not remove; used in DeleteEventHelper
72         Events.RRULE,                // 2  do not remove; used in DeleteEventHelper
73         Events.ALL_DAY,              // 3  do not remove; used in DeleteEventHelper
74         Events.CALENDAR_ID,          // 4  do not remove; used in DeleteEventHelper
75         Events.DTSTART,              // 5  do not remove; used in DeleteEventHelper
76         Events._SYNC_ID,             // 6  do not remove; used in DeleteEventHelper
77         Events.EVENT_TIMEZONE,       // 7  do not remove; used in DeleteEventHelper
78         Events.DESCRIPTION,          // 8
79         Events.EVENT_LOCATION,       // 9
80         Events.HAS_ALARM,            // 10
81         Events.ACCESS_LEVEL,         // 11
82         Events.COLOR,                // 12
83     };
84     private static final int EVENT_INDEX_ID = 0;
85     private static final int EVENT_INDEX_TITLE = 1;
86     private static final int EVENT_INDEX_RRULE = 2;
87     private static final int EVENT_INDEX_ALL_DAY = 3;
88     private static final int EVENT_INDEX_CALENDAR_ID = 4;
89     private static final int EVENT_INDEX_SYNC_ID = 6;
90     private static final int EVENT_INDEX_EVENT_TIMEZONE = 7;
91     private static final int EVENT_INDEX_DESCRIPTION = 8;
92     private static final int EVENT_INDEX_EVENT_LOCATION = 9;
93     private static final int EVENT_INDEX_HAS_ALARM = 10;
94     private static final int EVENT_INDEX_ACCESS_LEVEL = 11;
95     private static final int EVENT_INDEX_COLOR = 12;
96
97     private static final String[] ATTENDEES_PROJECTION = new String[] {
98         Attendees._ID,                      // 0
99         Attendees.ATTENDEE_RELATIONSHIP,    // 1
100         Attendees.ATTENDEE_STATUS,          // 2
101     };
102     private static final int ATTENDEES_INDEX_ID = 0;
103     private static final int ATTENDEES_INDEX_RELATIONSHIP = 1;
104     private static final int ATTENDEES_INDEX_STATUS = 2;
105     private static final String ATTENDEES_WHERE = Attendees.EVENT_ID + "=%d";
106
107     static final String[] CALENDARS_PROJECTION = new String[] {
108         Calendars._ID,          // 0
109         Calendars.DISPLAY_NAME, // 1
110     };
111     static final int CALENDARS_INDEX_DISPLAY_NAME = 1;
112     static final String CALENDARS_WHERE = Calendars._ID + "=%d";
113
114     private static final String[] REMINDERS_PROJECTION = new String[] {
115         Reminders._ID,      // 0
116         Reminders.MINUTES,  // 1
117     };
118     private static final int REMINDERS_INDEX_MINUTES = 1;
119     private static final String REMINDERS_WHERE = Reminders.EVENT_ID + "=%d AND (" +
120             Reminders.METHOD + "=" + Reminders.METHOD_ALERT + " OR " + Reminders.METHOD + "=" +
121             Reminders.METHOD_DEFAULT + ")";
122
123     private static final int MENU_GROUP_REMINDER = 1;
124     private static final int MENU_GROUP_EDIT = 2;
125     private static final int MENU_GROUP_DELETE = 3;
126
127     private static final int MENU_ADD_REMINDER = 1;
128     private static final int MENU_EDIT = 2;
129     private static final int MENU_DELETE = 3;
130
131     private static final int ATTENDEE_NO_RESPONSE = -1;
132     private static final int[] ATTENDEE_VALUES = {
133             ATTENDEE_NO_RESPONSE,
134             Attendees.ATTENDEE_STATUS_ACCEPTED,
135             Attendees.ATTENDEE_STATUS_TENTATIVE,
136             Attendees.ATTENDEE_STATUS_DECLINED,
137     };
138
139     private LinearLayout mRemindersContainer;
140
141     private Uri mUri;
142     private long mEventId;
143     private Cursor mEventCursor;
144     private Cursor mAttendeesCursor;
145     private Cursor mCalendarsCursor;
146
147     private long mStartMillis;
148     private long mEndMillis;
149     private int mVisibility = Calendars.NO_ACCESS;
150     private int mRelationship = Attendees.RELATIONSHIP_ORGANIZER;
151
152     private ArrayList<Integer> mOriginalMinutes = new ArrayList<Integer>();
153     private ArrayList<LinearLayout> mReminderItems = new ArrayList<LinearLayout>(0);
154     private ArrayList<Integer> mReminderValues;
155     private ArrayList<String> mReminderLabels;
156     private int mDefaultReminderMinutes;
157
158     private DeleteEventHelper mDeleteEventHelper;
159     private EditResponseHelper mEditResponseHelper;
160
161     private int mResponseOffset;
162     private int mOriginalAttendeeResponse;
163     private boolean mIsRepeating;
164
165     // This is called when one of the "remove reminder" buttons is selected.
166     public void onClick(View v) {
167         LinearLayout reminderItem = (LinearLayout) v.getParent();
168         LinearLayout parent = (LinearLayout) reminderItem.getParent();
169         parent.removeView(reminderItem);
170         mReminderItems.remove(reminderItem);
171         updateRemindersVisibility();
172     }
173     
174     public void onItemSelected(AdapterView parent, View v, int position, long id) {
175         // If they selected the "No response" option, then don't display the
176         // dialog asking which events to change.
177         if (id == 0 && mResponseOffset == 0) {
178             return;
179         }
180         
181         // If this is not a repeating event, then don't display the dialog
182         // asking which events to change.
183         if (!mIsRepeating) {
184             return;
185         }
186         
187         // If the selection is the same as the original, then don't display the
188         // dialog asking which events to change.
189         int index = findResponseIndexFor(mOriginalAttendeeResponse);
190         if (position == index + mResponseOffset) {
191             return;
192         }
193         
194         // This is a repeating event. We need to ask the user if they mean to
195         // change just this one instance or all instances.
196         mEditResponseHelper.showDialog(mEditResponseHelper.getWhichEvents());
197     }
198
199     public void onNothingSelected(AdapterView parent) {
200     }
201
202     @Override
203     protected void onCreate(Bundle icicle) {
204         super.onCreate(icicle);
205
206         // Event cursor
207         Intent intent = getIntent();
208         mUri = intent.getData();
209         ContentResolver cr = getContentResolver();
210         mStartMillis = intent.getLongExtra(EVENT_BEGIN_TIME, 0);
211         mEndMillis = intent.getLongExtra(EVENT_END_TIME, 0);
212         mEventCursor = managedQuery(mUri, EVENT_PROJECTION, null, null);
213         if (initEventCursor()) {
214             // The cursor is empty. This can happen if the event was deleted.
215             finish();
216             return;
217         }
218
219         setContentView(R.layout.event_info_activity);
220
221         // Attendees cursor
222         Uri uri = Attendees.CONTENT_URI;
223         String where = String.format(ATTENDEES_WHERE, mEventId);
224         mAttendeesCursor = managedQuery(uri, ATTENDEES_PROJECTION, where, null);
225         initAttendeesCursor();
226
227         // Calendars cursor
228         uri = Calendars.CONTENT_URI;
229         where = String.format(CALENDARS_WHERE, mEventCursor.getLong(EVENT_INDEX_CALENDAR_ID));
230         mCalendarsCursor = managedQuery(uri, CALENDARS_PROJECTION, where, null);
231         initCalendarsCursor();
232
233         Resources res = getResources();
234
235         if (mVisibility >= Calendars.CONTRIBUTOR_ACCESS &&
236                 mRelationship == Attendees.RELATIONSHIP_ATTENDEE) {
237             setTitle(res.getString(R.string.event_info_title_invite));
238         } else {
239             setTitle(res.getString(R.string.event_info_title));
240         }
241
242         // Initialize the reminder values array.
243         Resources r = getResources();
244         String[] strings = r.getStringArray(R.array.reminder_minutes_values);
245         int size = strings.length;
246         ArrayList<Integer> list = new ArrayList<Integer>(size);
247         for (int i = 0 ; i < size ; i++) {
248             list.add(Integer.parseInt(strings[i]));
249         }
250         mReminderValues = list;
251         String[] labels = r.getStringArray(R.array.reminder_minutes_labels);
252         mReminderLabels = new ArrayList<String>(Arrays.asList(labels));
253
254         SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
255         String durationString =
256                 prefs.getString(CalendarPreferenceActivity.KEY_DEFAULT_REMINDER, "0");
257         mDefaultReminderMinutes = Integer.parseInt(durationString);
258
259         mRemindersContainer = (LinearLayout) findViewById(R.id.reminder_items_container);
260
261         // Reminders cursor
262         boolean hasAlarm = mEventCursor.getInt(EVENT_INDEX_HAS_ALARM) != 0;
263         if (hasAlarm) {
264             uri = Reminders.CONTENT_URI;
265             where = String.format(REMINDERS_WHERE, mEventId);
266             Cursor reminderCursor = cr.query(uri, REMINDERS_PROJECTION, where, null, null);
267             try {
268                 // First pass: collect all the custom reminder minutes (e.g.,
269                 // a reminder of 8 minutes) into a global list.
270                 while (reminderCursor.moveToNext()) {
271                     int minutes = reminderCursor.getInt(REMINDERS_INDEX_MINUTES);
272                     EditEvent.addMinutesToList(this, mReminderValues, mReminderLabels, minutes);
273                 }
274                 
275                 // Second pass: create the reminder spinners
276                 reminderCursor.moveToPosition(-1);
277                 while (reminderCursor.moveToNext()) {
278                     int minutes = reminderCursor.getInt(REMINDERS_INDEX_MINUTES);
279                     mOriginalMinutes.add(minutes);
280                     EditEvent.addReminder(this, this, mReminderItems, mReminderValues,
281                             mReminderLabels, minutes);
282                 }
283             } finally {
284                 reminderCursor.close();
285             }
286         }
287
288         updateView();
289         updateRemindersVisibility();
290
291         // Setup the + Add Reminder Button
292         View.OnClickListener addReminderOnClickListener = new View.OnClickListener() {
293             public void onClick(View v) {
294                 addReminder();
295             }
296         };        
297         ImageButton reminderRemoveButton = (ImageButton) findViewById(R.id.reminder_add);
298         reminderRemoveButton.setOnClickListener(addReminderOnClickListener);
299
300         mDeleteEventHelper = new DeleteEventHelper(this, true /* exit when done */);
301         mEditResponseHelper = new EditResponseHelper(this);
302     }
303
304     @Override
305     protected void onResume() {
306         super.onResume();
307         if (initEventCursor()) {
308             // The cursor is empty. This can happen if the event was deleted.
309             finish();
310             return;
311         }
312         initAttendeesCursor();
313         initCalendarsCursor();
314     }
315
316     /**
317      * Initializes the event cursor, which is expected to point to the first
318      * (and only) result from a query.
319      * @return true if the cursor is empty.
320      */
321     private boolean initEventCursor() {
322         if ((mEventCursor == null) || (mEventCursor.getCount() == 0)) {
323             return true;
324         }
325         mEventCursor.moveToFirst();
326         mVisibility = mEventCursor.getInt(EVENT_INDEX_ACCESS_LEVEL);
327         mEventId = mEventCursor.getInt(EVENT_INDEX_ID);
328         String rRule = mEventCursor.getString(EVENT_INDEX_RRULE);
329         mIsRepeating = (rRule != null);
330         return false;
331     }
332
333     private void initAttendeesCursor() {
334         if (mAttendeesCursor != null) {
335             if (mAttendeesCursor.moveToFirst()) {
336                 mRelationship = mAttendeesCursor.getInt(ATTENDEES_INDEX_RELATIONSHIP);
337             }
338         }
339     }
340
341     private void initCalendarsCursor() {
342         if (mCalendarsCursor != null) {
343             mCalendarsCursor.moveToFirst();
344         }
345     }
346
347     @Override
348     public void onPause() {
349         super.onPause();
350         if (!isFinishing()) {
351             return;
352         }
353         ContentResolver cr = getContentResolver();
354         ArrayList<Integer> reminderMinutes = EditEvent.reminderItemsToMinutes(mReminderItems,
355                 mReminderValues);
356         boolean changed = EditEvent.saveReminders(cr, mEventId, reminderMinutes, mOriginalMinutes);
357         changed |= saveResponse(cr);
358         if (changed) {
359             Toast.makeText(this, R.string.saving_event, Toast.LENGTH_SHORT).show();
360         }
361     }
362
363     @Override
364     public boolean onCreateOptionsMenu(Menu menu) {
365         MenuItem item;
366         item = menu.add(MENU_GROUP_REMINDER, MENU_ADD_REMINDER, 0,
367                 R.string.add_new_reminder);
368         item.setIcon(R.drawable.ic_menu_reminder);
369         item.setAlphabeticShortcut('r');
370
371         item = menu.add(MENU_GROUP_EDIT, MENU_EDIT, 0, R.string.edit_event_label);
372         item.setIcon(android.R.drawable.ic_menu_edit);
373         item.setAlphabeticShortcut('e');
374
375         item = menu.add(MENU_GROUP_DELETE, MENU_DELETE, 0, R.string.delete_event_label);
376         item.setIcon(android.R.drawable.ic_menu_delete);
377
378         return super.onCreateOptionsMenu(menu);
379     }
380
381     @Override
382     public boolean onPrepareOptionsMenu(Menu menu) {
383         // Cannot add reminders to a shared calendar with only free/busy
384         // permissions
385         if (mVisibility >= Calendars.READ_ACCESS && mReminderItems.size() < MAX_REMINDERS) {
386             menu.setGroupVisible(MENU_GROUP_REMINDER, true);
387             menu.setGroupEnabled(MENU_GROUP_REMINDER, true);
388         } else {
389             menu.setGroupVisible(MENU_GROUP_REMINDER, false);
390             menu.setGroupEnabled(MENU_GROUP_REMINDER, false);
391         }
392
393         if (mVisibility >= Calendars.CONTRIBUTOR_ACCESS &&
394                 mRelationship >= Attendees.RELATIONSHIP_ORGANIZER) {
395             menu.setGroupVisible(MENU_GROUP_EDIT, true);
396             menu.setGroupEnabled(MENU_GROUP_EDIT, true);
397             menu.setGroupVisible(MENU_GROUP_DELETE, true);
398             menu.setGroupEnabled(MENU_GROUP_DELETE, true);
399         } else {
400             menu.setGroupVisible(MENU_GROUP_EDIT, false);
401             menu.setGroupEnabled(MENU_GROUP_EDIT, false);
402             menu.setGroupVisible(MENU_GROUP_DELETE, false);
403             menu.setGroupEnabled(MENU_GROUP_DELETE, false);
404         }
405
406         return super.onPrepareOptionsMenu(menu);
407     }
408     
409     private void addReminder() {
410         // TODO: when adding a new reminder, make it different from the
411         // last one in the list (if any).
412         if (mDefaultReminderMinutes == 0) {
413             EditEvent.addReminder(this, this, mReminderItems,
414                     mReminderValues, mReminderLabels, 10 /* minutes */);
415         } else {
416             EditEvent.addReminder(this, this, mReminderItems,
417                     mReminderValues, mReminderLabels, mDefaultReminderMinutes);
418         }
419         updateRemindersVisibility();
420     }
421
422     @Override
423     public boolean onOptionsItemSelected(MenuItem item) {
424         super.onOptionsItemSelected(item);
425         switch (item.getItemId()) {
426         case MENU_ADD_REMINDER:
427             addReminder();
428             break;
429         case MENU_EDIT:
430             doEdit();
431             break;
432         case MENU_DELETE:
433             doDelete();
434             break;
435         }
436         return true;
437     }
438
439     @Override
440     public boolean onKeyDown(int keyCode, KeyEvent event) {
441         if (keyCode == KeyEvent.KEYCODE_DEL) {
442             doDelete();
443             return true;
444         }
445         return super.onKeyDown(keyCode, event);
446     }
447
448     private void updateRemindersVisibility() {
449         if (mReminderItems.size() == 0) {
450             mRemindersContainer.setVisibility(View.GONE);
451         } else {
452             mRemindersContainer.setVisibility(View.VISIBLE);
453         }
454     }
455
456     /**
457      * Saves the response to an invitation if the user changed the response.
458      * Returns true if the database was updated.
459      * 
460      * @param cr the ContentResolver
461      * @return true if the database was changed
462      */
463     private boolean saveResponse(ContentResolver cr) {
464         if (mAttendeesCursor == null || mEventCursor == null) {
465             return false;
466         }
467         Spinner spinner = (Spinner) findViewById(R.id.response_value);
468         int position = spinner.getSelectedItemPosition() - mResponseOffset;
469         if (position <= 0) {
470             return false;
471         }
472
473         int status = ATTENDEE_VALUES[position];
474
475         // If the status has not changed, then don't update the database
476         if (status == mOriginalAttendeeResponse) {
477             return false;
478         }
479
480         long attendeeId = mAttendeesCursor.getInt(ATTENDEES_INDEX_ID);
481         if (!mIsRepeating) {
482             // This is a non-repeating event
483             updateResponse(cr, mEventId, attendeeId, status);
484             return true;
485         }
486
487         // This is a repeating event
488         int whichEvents = mEditResponseHelper.getWhichEvents();
489         switch (whichEvents) {
490             case -1:
491                 return false;
492             case UPDATE_SINGLE:
493                 createExceptionResponse(cr, mEventId, attendeeId, status);
494                 return true;
495             case UPDATE_ALL:
496                 updateResponse(cr, mEventId, attendeeId, status);
497                 return true;
498             default:
499                 Log.e("Calendar", "Unexpected choice for updating invitation response");
500                 break;
501         }
502         return false;
503     }
504     
505     private void updateResponse(ContentResolver cr, long eventId, long attendeeId, int status) {
506         // Update the "selfAttendeeStatus" field for the event
507         ContentValues values = new ContentValues();
508
509         // Will need to add email when MULTIPLE_ATTENDEES_PER_EVENT supported.
510         values.put(Attendees.ATTENDEE_STATUS, status);
511         values.put(Attendees.EVENT_ID, eventId);
512
513         Uri uri = ContentUris.withAppendedId(Attendees.CONTENT_URI, attendeeId);
514         cr.update(uri, values, null /* where */, null /* selection args */);
515     }
516     
517     private void createExceptionResponse(ContentResolver cr, long eventId,
518             long attendeeId, int status) {
519         // Fetch information about the repeating event.
520         Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventId);
521         Cursor cursor = cr.query(uri, EVENT_PROJECTION, null, null, null);
522         if (cursor == null) {
523             return;
524         }
525
526         try {
527             cursor.moveToFirst();
528             ContentValues values = new ContentValues();
529             
530             String title = cursor.getString(EVENT_INDEX_TITLE);
531             String timezone = cursor.getString(EVENT_INDEX_EVENT_TIMEZONE);
532             int calendarId = cursor.getInt(EVENT_INDEX_CALENDAR_ID);
533             boolean allDay = cursor.getInt(EVENT_INDEX_ALL_DAY) != 0;
534             String syncId = cursor.getString(EVENT_INDEX_SYNC_ID);
535             
536             values.put(Events.TITLE, title);
537             values.put(Events.EVENT_TIMEZONE, timezone);
538             values.put(Events.ALL_DAY, allDay ? 1 : 0);
539             values.put(Events.CALENDAR_ID, calendarId);
540             values.put(Events.DTSTART, mStartMillis);
541             values.put(Events.DTEND, mEndMillis);
542             values.put(Events.ORIGINAL_EVENT, syncId);
543             values.put(Events.ORIGINAL_INSTANCE_TIME, mStartMillis);
544             values.put(Events.ORIGINAL_ALL_DAY, allDay ? 1 : 0);
545             values.put(Events.STATUS, Events.STATUS_CONFIRMED);
546             values.put(Events.SELF_ATTENDEE_STATUS, status);
547             
548             // Create a recurrence exception
549             Uri newUri = cr.insert(Events.CONTENT_URI, values);
550         } finally {
551             cursor.close();
552         }
553     }
554
555     private int findResponseIndexFor(int response) {
556         int size = ATTENDEE_VALUES.length;
557         for (int index = 0; index < size; index++) {
558             if (ATTENDEE_VALUES[index] == response) {
559                 return index;
560             }
561         }
562         return 0;
563     }
564
565     private void doEdit() {
566         Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, mEventId);
567         Intent intent = new Intent(Intent.ACTION_EDIT, uri);
568         intent.putExtra(Calendar.EVENT_BEGIN_TIME, mStartMillis);
569         intent.putExtra(Calendar.EVENT_END_TIME, mEndMillis);
570         intent.setClass(EventInfoActivity.this, EditEvent.class);
571         startActivity(intent);
572         finish();
573     }
574
575     private void doDelete() {
576         mDeleteEventHelper.delete(mStartMillis, mEndMillis, mEventCursor, -1);
577     }
578
579     private void updateView() {
580         if (mEventCursor == null) {
581             return;
582         }
583         Resources res = getResources();
584         ContentResolver cr = getContentResolver();
585
586         String eventName = mEventCursor.getString(EVENT_INDEX_TITLE);
587         if (eventName == null || eventName.length() == 0) {
588             eventName = res.getString(R.string.no_title_label);
589         }
590
591         boolean allDay = mEventCursor.getInt(EVENT_INDEX_ALL_DAY) != 0;
592         String location = mEventCursor.getString(EVENT_INDEX_EVENT_LOCATION);
593         String description = mEventCursor.getString(EVENT_INDEX_DESCRIPTION);
594         String rRule = mEventCursor.getString(EVENT_INDEX_RRULE);
595         boolean hasAlarm = mEventCursor.getInt(EVENT_INDEX_HAS_ALARM) != 0;
596         String eventTimezone = mEventCursor.getString(EVENT_INDEX_EVENT_TIMEZONE);
597         int color = mEventCursor.getInt(EVENT_INDEX_COLOR) & 0xbbffffff;
598
599         View calBackground = findViewById(R.id.cal_background);
600         calBackground.setBackgroundColor(color);
601
602         TextView title = (TextView) findViewById(R.id.title);
603         title.setTextColor(color);
604         
605         View divider = (View) findViewById(R.id.divider);
606         divider.getBackground().setColorFilter(color, PorterDuff.Mode.SRC_IN);
607         
608         // What
609         if (eventName != null) {
610             setTextCommon(R.id.title, eventName);
611         }
612
613         // When
614         String when;
615         int flags;
616         if (allDay) {
617             flags = DateUtils.FORMAT_UTC | DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_SHOW_DATE;
618         } else {
619             flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE;
620             if (DateFormat.is24HourFormat(this)) {
621                 flags |= DateUtils.FORMAT_24HOUR;
622             }
623         }
624         when = DateUtils.formatDateRange(this, mStartMillis, mEndMillis, flags);
625         setTextCommon(R.id.when, when);
626
627         // Show the event timezone if it is different from the local timezone
628         Time time = new Time();
629         String localTimezone = time.timezone;
630         if (allDay) {
631             localTimezone = Time.TIMEZONE_UTC;
632         }
633         if (eventTimezone != null && !localTimezone.equals(eventTimezone) && !allDay) {
634             setTextCommon(R.id.timezone, localTimezone);
635         } else {
636             setVisibilityCommon(R.id.timezone_container, View.GONE);
637         }
638
639         // Repeat
640         if (rRule != null) {
641             EventRecurrence eventRecurrence = new EventRecurrence();
642             eventRecurrence.parse(rRule);
643             Time date = new Time();
644             if (allDay) {
645                 date.timezone = Time.TIMEZONE_UTC;
646             }
647             date.set(mStartMillis);
648             eventRecurrence.setStartDate(date);
649             String repeatString = eventRecurrence.getRepeatString();
650             setTextCommon(R.id.repeat, repeatString);
651         } else {
652             setVisibilityCommon(R.id.repeat_container, View.GONE);
653         }
654
655         // Where
656         if (location == null || location.length() == 0) {
657             setVisibilityCommon(R.id.where, View.GONE);
658         } else {
659             setTextCommon(R.id.where, location);
660         }
661
662         // Description
663         if (description == null || description.length() == 0) {
664             setVisibilityCommon(R.id.description, View.GONE);
665         } else {
666             setTextCommon(R.id.description, description);
667         }
668
669         // Calendar
670         if (mCalendarsCursor != null) {
671             mCalendarsCursor.moveToFirst();
672             String calendarName = mCalendarsCursor.getString(CALENDARS_INDEX_DISPLAY_NAME);
673             setTextCommon(R.id.calendar, calendarName);
674         } else {
675             setVisibilityCommon(R.id.calendar_container, View.GONE);
676         }
677
678         // Response
679         updateResponse();
680     }
681
682     void updateResponse() {
683         if (mVisibility < Calendars.CONTRIBUTOR_ACCESS ||
684                 mRelationship != Attendees.RELATIONSHIP_ATTENDEE) {
685             setVisibilityCommon(R.id.response_container, View.GONE);
686             return;
687         }
688
689         setVisibilityCommon(R.id.response_container, View.VISIBLE);
690
691         Spinner spinner = (Spinner) findViewById(R.id.response_value);
692
693         mOriginalAttendeeResponse = ATTENDEE_NO_RESPONSE;
694         if (mAttendeesCursor != null) {
695             mOriginalAttendeeResponse = mAttendeesCursor.getInt(ATTENDEES_INDEX_STATUS);
696         }
697         mResponseOffset = 0;
698
699         /* If the user has previously responded to this event
700          * we should not allow them to select no response again.
701          * Switch the entries to a set of entries without the
702          * no response option.
703          */
704         if ((mOriginalAttendeeResponse != Attendees.ATTENDEE_STATUS_INVITED)
705                 && (mOriginalAttendeeResponse != ATTENDEE_NO_RESPONSE)
706                 && (mOriginalAttendeeResponse != Attendees.ATTENDEE_STATUS_NONE)) {
707             CharSequence[] entries;
708             entries = getResources().getTextArray(R.array.response_labels2);
709             mResponseOffset = -1;
710             ArrayAdapter<CharSequence> adapter =
711                 new ArrayAdapter<CharSequence>(this,
712                         android.R.layout.simple_spinner_item, entries);
713             adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
714             spinner.setAdapter(adapter);
715         }
716
717         int index = findResponseIndexFor(mOriginalAttendeeResponse);
718         spinner.setSelection(index + mResponseOffset);
719         spinner.setOnItemSelectedListener(this);
720     }
721
722     private void setTextCommon(int id, CharSequence text) {
723         TextView textView = (TextView) findViewById(id);
724         if (textView == null)
725             return;
726         textView.setText(text);
727     }
728
729     private void setVisibilityCommon(int id, int visibility) {
730         View v = findViewById(id);
731         if (v != null) {
732             v.setVisibility(visibility);
733         }
734         return;
735     }
736 }