EventInfoActivity.java revision b84a151490b03c7083b8f517b83366bb11f17f51
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
17package com.android.calendar;
18
19import static android.provider.Calendar.EVENT_BEGIN_TIME;
20import static android.provider.Calendar.EVENT_END_TIME;
21import static android.provider.Calendar.AttendeesColumns.ATTENDEE_STATUS;
22
23import android.app.Activity;
24import android.content.ContentResolver;
25import android.content.ContentUris;
26import android.content.ContentValues;
27import android.content.Intent;
28import android.content.SharedPreferences;
29import android.content.res.Resources;
30import android.database.Cursor;
31import android.graphics.PorterDuff;
32import android.net.Uri;
33import android.os.Bundle;
34import android.pim.EventRecurrence;
35import android.preference.PreferenceManager;
36import android.provider.Calendar;
37import android.provider.Calendar.Attendees;
38import android.provider.Calendar.Calendars;
39import android.provider.Calendar.Events;
40import android.provider.Calendar.Reminders;
41import android.text.format.DateFormat;
42import android.text.format.DateUtils;
43import android.text.format.Time;
44import android.text.util.Linkify;
45import android.util.Log;
46import android.view.KeyEvent;
47import android.view.Menu;
48import android.view.MenuItem;
49import android.view.View;
50import android.widget.AdapterView;
51import android.widget.ArrayAdapter;
52import android.widget.ImageButton;
53import android.widget.LinearLayout;
54import android.widget.Spinner;
55import android.widget.TextView;
56import android.widget.Toast;
57
58import java.util.ArrayList;
59import java.util.Arrays;
60import java.util.regex.Pattern;
61
62public 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}
755