OSDN Git Service

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