import static android.provider.Calendar.AttendeesColumns.ATTENDEE_STATUS;
import android.app.Activity;
+import android.content.ActivityNotFoundException;
import android.content.AsyncQueryHandler;
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.net.Uri;
import android.os.Bundle;
import android.os.RemoteException;
-import android.pim.ContactsAsyncHelper;
import android.pim.EventRecurrence;
-import android.preference.PreferenceManager;
import android.provider.Calendar;
-import android.provider.Contacts;
+import android.provider.ContactsContract;
import android.provider.Calendar.Attendees;
import android.provider.Calendar.Calendars;
import android.provider.Calendar.Events;
import android.provider.Calendar.Reminders;
-import android.provider.Contacts.ContactMethods;
-import android.provider.Contacts.Intents;
-import android.provider.Contacts.People;
-import android.provider.Contacts.Presence;
+import android.provider.ContactsContract.CommonDataKinds;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.Intents;
+import android.provider.ContactsContract.Presence;
+import android.provider.ContactsContract.QuickContact;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.text.TextUtils;
import android.text.format.DateFormat;
import android.text.format.DateUtils;
import android.text.format.Time;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
+import android.view.MotionEvent;
import android.view.View;
-import android.view.View.OnClickListener;
+import android.view.View.OnTouchListener;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
+import android.widget.QuickContactBadge;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
public class EventInfoActivity extends Activity implements View.OnClickListener,
AdapterView.OnItemSelectedListener {
- private static final String TAG = "EventInfoActivity";
+ public static final boolean DEBUG = false;
+
+ public static final String TAG = "EventInfoActivity";
+
private static final int MAX_REMINDERS = 5;
/**
Events.HAS_ALARM, // 10
Events.ACCESS_LEVEL, // 11
Events.COLOR, // 12
+ Events.HAS_ATTENDEE_DATA, // 13
+ Events.GUESTS_CAN_MODIFY, // 14
+ // TODO Events.GUESTS_CAN_INVITE_OTHERS has not been implemented in calendar provider
+ Events.GUESTS_CAN_INVITE_OTHERS, // 15
+ Events.ORGANIZER, // 16
};
private static final int EVENT_INDEX_ID = 0;
private static final int EVENT_INDEX_TITLE = 1;
private static final int EVENT_INDEX_HAS_ALARM = 10;
private static final int EVENT_INDEX_ACCESS_LEVEL = 11;
private static final int EVENT_INDEX_COLOR = 12;
+ private static final int EVENT_INDEX_HAS_ATTENDEE_DATA = 13;
+ private static final int EVENT_INDEX_GUESTS_CAN_MODIFY = 14;
+ private static final int EVENT_INDEX_CAN_INVITE_OTHERS = 15;
+ private static final int EVENT_INDEX_ORGANIZER = 16;
private static final String[] ATTENDEES_PROJECTION = new String[] {
Attendees._ID, // 0
private static final String ATTENDEES_WHERE = Attendees.EVENT_ID + "=%d";
+ private static final String ATTENDEES_SORT_ORDER = Attendees.ATTENDEE_NAME + " ASC, "
+ + Attendees.ATTENDEE_EMAIL + " ASC";
+
static final String[] CALENDARS_PROJECTION = new String[] {
- Calendars._ID, // 0
- Calendars.DISPLAY_NAME, // 1
+ Calendars._ID, // 0
+ Calendars.DISPLAY_NAME, // 1
+ Calendars.OWNER_ACCOUNT, // 2
+ Calendars.ORGANIZER_CAN_RESPOND // 3
};
static final int CALENDARS_INDEX_DISPLAY_NAME = 1;
+ static final int CALENDARS_INDEX_OWNER_ACCOUNT = 2;
+ static final int CALENDARS_INDEX_OWNER_CAN_RESPOND = 3;
+
static final String CALENDARS_WHERE = Calendars._ID + "=%d";
+ static final String CALENDARS_DUPLICATE_NAME_WHERE = Calendars.DISPLAY_NAME + "=?";
private static final String[] REMINDERS_PROJECTION = new String[] {
Reminders._ID, // 0
private static final String REMINDERS_WHERE = Reminders.EVENT_ID + "=%d AND (" +
Reminders.METHOD + "=" + Reminders.METHOD_ALERT + " OR " + Reminders.METHOD + "=" +
Reminders.METHOD_DEFAULT + ")";
+ private static final String REMINDERS_SORT = Reminders.MINUTES;
private static final int MENU_GROUP_REMINDER = 1;
private static final int MENU_GROUP_EDIT = 2;
private static final int MENU_EDIT = 2;
private static final int MENU_DELETE = 3;
+ private static final int ATTENDEE_ID_NONE = -1;
private static final int ATTENDEE_NO_RESPONSE = -1;
private static final int[] ATTENDEE_VALUES = {
ATTENDEE_NO_RESPONSE,
};
private LinearLayout mRemindersContainer;
+ private LinearLayout mOrganizerContainer;
+ private TextView mOrganizerView;
private Uri mUri;
private long mEventId;
private long mStartMillis;
private long mEndMillis;
- private int mVisibility = Calendars.NO_ACCESS;
- private int mRelationship = Attendees.RELATIONSHIP_ORGANIZER;
+
+ private boolean mHasAttendeeData;
+ private boolean mIsOrganizer;
+ private long mCalendarOwnerAttendeeId = ATTENDEE_ID_NONE;
+ private boolean mOrganizerCanRespond;
+ private String mCalendarOwnerAccount;
+ private boolean mCanModifyCalendar;
+ private boolean mIsBusyFreeCalendar;
+ private boolean mCanModifyEvent;
+ private int mNumOfAttendees;
+ private String mOrganizer;
private ArrayList<Integer> mOriginalMinutes = new ArrayList<Integer>();
private ArrayList<LinearLayout> mReminderItems = new ArrayList<LinearLayout>(0);
private ArrayList<Integer> mReminderValues;
private ArrayList<String> mReminderLabels;
private int mDefaultReminderMinutes;
+ private boolean mOriginalHasAlarm;
private DeleteEventHelper mDeleteEventHelper;
private EditResponseHelper mEditResponseHelper;
private int mOriginalAttendeeResponse;
private int mAttendeeResponseFromIntent = ATTENDEE_NO_RESPONSE;
private boolean mIsRepeating;
+ private boolean mIsDuplicateName;
private Pattern mWildcardPattern = Pattern.compile("^.*$");
private LayoutInflater mLayoutInflater;
+ private LinearLayout mReminderAdder;
+ // TODO This can be removed when the contacts content provider doesn't return duplicates
+ private int mUpdateCounts;
private static class ViewHolder {
- ImageView avatar;
+ QuickContactBadge badge;
ImageView presence;
+ int updateCounts;
}
- private HashMap<String, ViewHolder> mPresenceStatuses = new HashMap<String, ViewHolder>();
+ private HashMap<String, ViewHolder> mViewHolders = new HashMap<String, ViewHolder>();
private PresenceQueryHandler mPresenceQueryHandler;
- static final String[] PEOPLE_PROJECTION = new String[] {
- People._ID,
- };
+ private static final Uri CONTACT_DATA_WITH_PRESENCE_URI = Data.CONTENT_URI;
+
+ int PRESENCE_PROJECTION_CONTACT_ID_INDEX = 0;
+ int PRESENCE_PROJECTION_PRESENCE_INDEX = 1;
+ int PRESENCE_PROJECTION_EMAIL_INDEX = 2;
+ int PRESENCE_PROJECTION_PHOTO_ID_INDEX = 3;
- Uri CONTACT_PRESENCE_URI = Uri.withAppendedPath(Contacts.ContactMethods.CONTENT_URI,
- "with_presence");
- int PRESENCE_PROJECTION_EMAIL_INDEX = 1;
- int PRESENCE_PROJECTION_PRESENCE_INDEX = 2;
private static final String[] PRESENCE_PROJECTION = new String[] {
- ContactMethods._ID, // 0
- ContactMethods.DATA, // 1
- People.PRESENCE_STATUS, // 2
+ Email.CONTACT_ID, // 0
+ Email.CONTACT_PRESENCE, // 1
+ Email.DATA, // 2
+ Email.PHOTO_ID, // 3
};
ArrayList<Attendee> mAcceptedAttendees = new ArrayList<Attendee>();
ArrayList<Attendee> mDeclinedAttendees = new ArrayList<Attendee>();
ArrayList<Attendee> mTentativeAttendees = new ArrayList<Attendee>();
- private OnClickListener contactOnClickListener = new OnClickListener() {
- public void onClick(View v) {
- Attendee attendee = (Attendee) v.getTag();
- Rect rect = new Rect();
- v.getDrawingRect(rect);
- showContactInfo(attendee, rect);
- }
- };
+ ArrayList<Attendee> mNoResponseAttendees = new ArrayList<Attendee>();
private int mColor;
// This is called when one of the "remove reminder" buttons is selected.
updateRemindersVisibility();
}
- public void onItemSelected(AdapterView parent, View v, int position, long id) {
+ public void onItemSelected(AdapterView<?> parent, View v, int position, long id) {
// If they selected the "No response" option, then don't display the
// dialog asking which events to change.
if (id == 0 && mResponseOffset == 0) {
mEditResponseHelper.showDialog(mEditResponseHelper.getWhichEvents());
}
- public void onNothingSelected(AdapterView parent) {
+ public void onNothingSelected(AdapterView<?> parent) {
}
@Override
mStartMillis = intent.getLongExtra(EVENT_BEGIN_TIME, 0);
mEndMillis = intent.getLongExtra(EVENT_END_TIME, 0);
mAttendeeResponseFromIntent = intent.getIntExtra(ATTENDEE_STATUS, ATTENDEE_NO_RESPONSE);
- mEventCursor = managedQuery(mUri, EVENT_PROJECTION, null, null);
+ mEventCursor = managedQuery(mUri, EVENT_PROJECTION, null, null, null);
if (initEventCursor()) {
// The cursor is empty. This can happen if the event was deleted.
finish();
}
setContentView(R.layout.event_info_activity);
-
- // Attendees cursor
- Uri uri = Attendees.CONTENT_URI;
- String where = String.format(ATTENDEES_WHERE, mEventId);
- mAttendeesCursor = managedQuery(uri, ATTENDEES_PROJECTION, where, null);
+ mPresenceQueryHandler = new PresenceQueryHandler(this, cr);
+ mLayoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mRemindersContainer = (LinearLayout) findViewById(R.id.reminders_container);
+ mOrganizerContainer = (LinearLayout) findViewById(R.id.organizer_container);
+ mOrganizerView = (TextView) findViewById(R.id.organizer);
// Calendars cursor
- uri = Calendars.CONTENT_URI;
- where = String.format(CALENDARS_WHERE, mEventCursor.getLong(EVENT_INDEX_CALENDAR_ID));
- mCalendarsCursor = managedQuery(uri, CALENDARS_PROJECTION, where, null);
- initCalendarsCursor();
-
- Resources res = getResources();
+ Uri uri = Calendars.CONTENT_URI;
+ String where = String.format(CALENDARS_WHERE, mEventCursor.getLong(EVENT_INDEX_CALENDAR_ID));
+ mCalendarsCursor = managedQuery(uri, CALENDARS_PROJECTION, where, null, null);
+ mCalendarOwnerAccount = "";
+ if (mCalendarsCursor != null) {
+ mCalendarsCursor.moveToFirst();
+ mCalendarOwnerAccount = mCalendarsCursor.getString(CALENDARS_INDEX_OWNER_ACCOUNT);
+ mOrganizerCanRespond = mCalendarsCursor.getInt(CALENDARS_INDEX_OWNER_CAN_RESPOND) != 0;
- if (mVisibility >= Calendars.CONTRIBUTOR_ACCESS &&
- mRelationship == Attendees.RELATIONSHIP_ATTENDEE) {
- setTitle(res.getString(R.string.event_info_title_invite));
- } else {
- setTitle(res.getString(R.string.event_info_title));
+ String displayName = mCalendarsCursor.getString(CALENDARS_INDEX_DISPLAY_NAME);
+ mIsDuplicateName = isDuplicateName(displayName);
}
+ String eventOrganizer = mEventCursor.getString(EVENT_INDEX_ORGANIZER);
+ mIsOrganizer = mCalendarOwnerAccount.equalsIgnoreCase(eventOrganizer);
+ mHasAttendeeData = mEventCursor.getInt(EVENT_INDEX_HAS_ATTENDEE_DATA) != 0;
+
+ updateView();
+
+ // Attendees cursor
+ uri = Attendees.CONTENT_URI;
+ where = String.format(ATTENDEES_WHERE, mEventId);
+ mAttendeesCursor = managedQuery(uri, ATTENDEES_PROJECTION, where, null,
+ ATTENDEES_SORT_ORDER);
+ initAttendeesCursor();
+
+ mOrganizer = eventOrganizer;
+ mCanModifyCalendar =
+ mEventCursor.getInt(EVENT_INDEX_ACCESS_LEVEL) >= Calendars.CONTRIBUTOR_ACCESS;
+ mIsBusyFreeCalendar =
+ mEventCursor.getInt(EVENT_INDEX_ACCESS_LEVEL) == Calendars.FREEBUSY_ACCESS;
+
+ mCanModifyEvent = mCanModifyCalendar
+ && (mIsOrganizer || (mEventCursor.getInt(EVENT_INDEX_GUESTS_CAN_MODIFY) != 0));
// Initialize the reminder values array.
Resources r = getResources();
String[] labels = r.getStringArray(R.array.reminder_minutes_labels);
mReminderLabels = new ArrayList<String>(Arrays.asList(labels));
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
+ SharedPreferences prefs = CalendarPreferenceActivity.getSharedPreferences(this);
String durationString =
prefs.getString(CalendarPreferenceActivity.KEY_DEFAULT_REMINDER, "0");
mDefaultReminderMinutes = Integer.parseInt(durationString);
- mRemindersContainer = (LinearLayout) findViewById(R.id.reminder_items_container);
-
// Reminders cursor
boolean hasAlarm = mEventCursor.getInt(EVENT_INDEX_HAS_ALARM) != 0;
if (hasAlarm) {
uri = Reminders.CONTENT_URI;
where = String.format(REMINDERS_WHERE, mEventId);
- Cursor reminderCursor = cr.query(uri, REMINDERS_PROJECTION, where, null, null);
+ Cursor reminderCursor = cr.query(uri, REMINDERS_PROJECTION, where, null,
+ REMINDERS_SORT);
try {
// First pass: collect all the custom reminder minutes (e.g.,
// a reminder of 8 minutes) into a global list.
reminderCursor.close();
}
}
-
- updateView();
- updateRemindersVisibility();
+ mOriginalHasAlarm = hasAlarm;
// Setup the + Add Reminder Button
View.OnClickListener addReminderOnClickListener = new View.OnClickListener() {
addReminder();
}
};
- ImageButton reminderRemoveButton = (ImageButton) findViewById(R.id.reminder_add);
- reminderRemoveButton.setOnClickListener(addReminderOnClickListener);
+ ImageButton reminderAddButton = (ImageButton) findViewById(R.id.reminder_add);
+ reminderAddButton.setOnClickListener(addReminderOnClickListener);
+
+ mReminderAdder = (LinearLayout) findViewById(R.id.reminder_adder);
+ updateRemindersVisibility();
mDeleteEventHelper = new DeleteEventHelper(this, true /* exit when done */);
mEditResponseHelper = new EditResponseHelper(this);
-
- mPresenceQueryHandler = new PresenceQueryHandler(this, cr);
- mLayoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
finish();
return;
}
- initAttendeesCursor();
initCalendarsCursor();
updateResponse();
+ updateTitle();
+ }
+
+ private void updateTitle() {
+ Resources res = getResources();
+ if (mCanModifyCalendar && !mIsOrganizer) {
+ setTitle(res.getString(R.string.event_info_title_invite));
+ } else {
+ setTitle(res.getString(R.string.event_info_title));
+ }
+ }
+
+ boolean isDuplicateName(String displayName) {
+ Cursor dupNameCursor = managedQuery(Calendars.CONTENT_URI, CALENDARS_PROJECTION,
+ CALENDARS_DUPLICATE_NAME_WHERE, new String[] {displayName}, null);
+ boolean isDuplicateName = false;
+ if(dupNameCursor != null) {
+ if (dupNameCursor.getCount() > 1) {
+ isDuplicateName = true;
+ }
+ dupNameCursor.close();
+ }
+ return isDuplicateName;
}
/**
return true;
}
mEventCursor.moveToFirst();
- mVisibility = mEventCursor.getInt(EVENT_INDEX_ACCESS_LEVEL);
mEventId = mEventCursor.getInt(EVENT_INDEX_ID);
String rRule = mEventCursor.getString(EVENT_INDEX_RRULE);
mIsRepeating = (rRule != null);
}
}
+ @SuppressWarnings("fallthrough")
private void initAttendeesCursor() {
+ mOriginalAttendeeResponse = ATTENDEE_NO_RESPONSE;
+ mCalendarOwnerAttendeeId = ATTENDEE_ID_NONE;
+ mNumOfAttendees = 0;
if (mAttendeesCursor != null) {
+ mNumOfAttendees = mAttendeesCursor.getCount();
if (mAttendeesCursor.moveToFirst()) {
mAcceptedAttendees.clear();
mDeclinedAttendees.clear();
mTentativeAttendees.clear();
+ mNoResponseAttendees.clear();
- /*
- * TODO: We have been relying on the fact that "our user" appears
- * in the first row. The right way is to look up the email addr
- * associated with the calendar and do a match here.
- */
- mRelationship = mAttendeesCursor.getInt(ATTENDEES_INDEX_RELATIONSHIP);
do {
int status = mAttendeesCursor.getInt(ATTENDEES_INDEX_STATUS);
String name = mAttendeesCursor.getString(ATTENDEES_INDEX_NAME);
String email = mAttendeesCursor.getString(ATTENDEES_INDEX_EMAIL);
- switch(status) {
- case Attendees.ATTENDEE_STATUS_ACCEPTED:
- mAcceptedAttendees.add(new Attendee(name, email));
- break;
- case Attendees.ATTENDEE_STATUS_DECLINED:
- mDeclinedAttendees.add(new Attendee(name, email));
- break;
- default:
- mTentativeAttendees.add(new Attendee(name, email));
+
+ if (mAttendeesCursor.getInt(ATTENDEES_INDEX_RELATIONSHIP) ==
+ Attendees.RELATIONSHIP_ORGANIZER) {
+ // Overwrites the one from Event table if available
+ if (name != null && name.length() > 0) {
+ mOrganizer = name;
+ } else if (email != null && email.length() > 0) {
+ mOrganizer = email;
+ }
+ }
+
+ if (mCalendarOwnerAttendeeId == ATTENDEE_ID_NONE &&
+ mCalendarOwnerAccount.equalsIgnoreCase(email)) {
+ mCalendarOwnerAttendeeId = mAttendeesCursor.getInt(ATTENDEES_INDEX_ID);
+ mOriginalAttendeeResponse = mAttendeesCursor.getInt(ATTENDEES_INDEX_STATUS);
+ } else {
+ // Don't show your own status in the list because:
+ // 1) it doesn't make sense for event without other guests.
+ // 2) there's a spinner for that for events with guests.
+ switch(status) {
+ case Attendees.ATTENDEE_STATUS_ACCEPTED:
+ mAcceptedAttendees.add(new Attendee(name, email));
+ break;
+ case Attendees.ATTENDEE_STATUS_DECLINED:
+ mDeclinedAttendees.add(new Attendee(name, email));
+ break;
+ case Attendees.ATTENDEE_STATUS_NONE:
+ mNoResponseAttendees.add(new Attendee(name, email));
+ // Fallthrough so that no response is a subset of tentative
+ default:
+ mTentativeAttendees.add(new Attendee(name, email));
+ }
}
} while (mAttendeesCursor.moveToNext());
mAttendeesCursor.moveToFirst();
updateAttendees();
}
}
+ // only show the organizer if we're not the organizer and if
+ // we have attendee data (might have been removed by the server
+ // for events with a lot of attendees).
+ if (!mIsOrganizer && mHasAttendeeData) {
+ mOrganizerContainer.setVisibility(View.VISIBLE);
+ mOrganizerView.setText(mOrganizer);
+ } else {
+ mOrganizerContainer.setVisibility(View.GONE);
+ }
}
private void initCalendarsCursor() {
boolean changed = EditEvent.saveReminders(ops, mEventId, reminderMinutes, mOriginalMinutes,
false /* no force save */);
try {
+ // TODO Run this in a background process.
cr.applyBatch(Calendars.CONTENT_URI.getAuthority(), ops);
+ // Update the "hasAlarm" field for the event
+ Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, mEventId);
+ int len = reminderMinutes.size();
+ boolean hasAlarm = len > 0;
+ if (hasAlarm != mOriginalHasAlarm) {
+ ContentValues values = new ContentValues();
+ values.put(Events.HAS_ALARM, hasAlarm ? 1 : 0);
+ cr.update(uri, values, null, null);
+ }
} catch (RemoteException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
+ Log.w(TAG, "Ignoring exception: ", e);
} catch (OperationApplicationException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
+ Log.w(TAG, "Ignoring exception: ", e);
}
changed |= saveResponse(cr);
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
- // Cannot add reminders to a shared calendar with only free/busy
- // permissions
- if (mVisibility >= Calendars.READ_ACCESS && mReminderItems.size() < MAX_REMINDERS) {
- menu.setGroupVisible(MENU_GROUP_REMINDER, true);
- menu.setGroupEnabled(MENU_GROUP_REMINDER, true);
- } else {
- menu.setGroupVisible(MENU_GROUP_REMINDER, false);
- menu.setGroupEnabled(MENU_GROUP_REMINDER, false);
- }
+ boolean canAddReminders = canAddReminders();
+ menu.setGroupVisible(MENU_GROUP_REMINDER, canAddReminders);
+ menu.setGroupEnabled(MENU_GROUP_REMINDER, canAddReminders);
- if (mVisibility >= Calendars.CONTRIBUTOR_ACCESS &&
- mRelationship >= Attendees.RELATIONSHIP_ORGANIZER) {
- menu.setGroupVisible(MENU_GROUP_EDIT, true);
- menu.setGroupEnabled(MENU_GROUP_EDIT, true);
- menu.setGroupVisible(MENU_GROUP_DELETE, true);
- menu.setGroupEnabled(MENU_GROUP_DELETE, true);
- } else {
- menu.setGroupVisible(MENU_GROUP_EDIT, false);
- menu.setGroupEnabled(MENU_GROUP_EDIT, false);
- menu.setGroupVisible(MENU_GROUP_DELETE, false);
- menu.setGroupEnabled(MENU_GROUP_DELETE, false);
- }
+ menu.setGroupVisible(MENU_GROUP_EDIT, mCanModifyEvent);
+ menu.setGroupEnabled(MENU_GROUP_EDIT, mCanModifyEvent);
+ menu.setGroupVisible(MENU_GROUP_DELETE, mCanModifyCalendar);
+ menu.setGroupEnabled(MENU_GROUP_DELETE, mCanModifyCalendar);
return super.onPrepareOptionsMenu(menu);
}
+ private boolean canAddReminders() {
+ return !mIsBusyFreeCalendar && mReminderItems.size() < MAX_REMINDERS;
+ }
+
private void addReminder() {
// TODO: when adding a new reminder, make it different from the
// last one in the list (if any).
}
private void updateRemindersVisibility() {
- if (mReminderItems.size() == 0) {
+ if (mIsBusyFreeCalendar) {
mRemindersContainer.setVisibility(View.GONE);
} else {
mRemindersContainer.setVisibility(View.VISIBLE);
+ mReminderAdder.setVisibility(canAddReminders() ? View.VISIBLE : View.GONE);
}
}
return false;
}
- long attendeeId = mAttendeesCursor.getInt(ATTENDEES_INDEX_ID);
+ // If we never got an owner attendee id we can't set the status
+ if (mCalendarOwnerAttendeeId == ATTENDEE_ID_NONE) {
+ return false;
+ }
+
if (!mIsRepeating) {
// This is a non-repeating event
- updateResponse(cr, mEventId, attendeeId, status);
+ updateResponse(cr, mEventId, mCalendarOwnerAttendeeId, status);
return true;
}
case -1:
return false;
case UPDATE_SINGLE:
- createExceptionResponse(cr, mEventId, attendeeId, status);
+ createExceptionResponse(cr, mEventId, mCalendarOwnerAttendeeId, status);
return true;
case UPDATE_ALL:
- updateResponse(cr, mEventId, attendeeId, status);
+ updateResponse(cr, mEventId, mCalendarOwnerAttendeeId, status);
return true;
default:
- Log.e("Calendar", "Unexpected choice for updating invitation response");
+ Log.e(TAG, "Unexpected choice for updating invitation response");
break;
}
return false;
}
private void updateResponse(ContentResolver cr, long eventId, long attendeeId, int status) {
- // Update the "selfAttendeeStatus" field for the event
+ // Update the attendee status in the attendees table. the provider
+ // takes care of updating the self attendance status.
ContentValues values = new ContentValues();
- // Will need to add email when MULTIPLE_ATTENDEES_PER_EVENT supported.
+ if (!TextUtils.isEmpty(mCalendarOwnerAccount)) {
+ values.put(Attendees.ATTENDEE_EMAIL, mCalendarOwnerAccount);
+ }
values.put(Attendees.ATTENDEE_STATUS, status);
values.put(Attendees.EVENT_ID, eventId);
if (cursor == null) {
return;
}
+ if(!cursor.moveToFirst()) {
+ cursor.close();
+ return;
+ }
try {
- cursor.moveToFirst();
ContentValues values = new ContentValues();
String title = cursor.getString(EVENT_INDEX_TITLE);
if (mEventCursor == null) {
return;
}
- Resources res = getResources();
String eventName = mEventCursor.getString(EVENT_INDEX_TITLE);
if (eventName == null || eventName.length() == 0) {
+ Resources res = getResources();
eventName = res.getString(R.string.no_title_label);
}
}
date.set(mStartMillis);
eventRecurrence.setStartDate(date);
- String repeatString = eventRecurrence.getRepeatString();
+ String repeatString = EventRecurrenceFormatter.getRepeatString(getResources(),
+ eventRecurrence);
setTextCommon(R.id.repeat, repeatString);
} else {
setVisibilityCommon(R.id.repeat_container, View.GONE);
if (location == null || location.length() == 0) {
setVisibilityCommon(R.id.where, View.GONE);
} else {
- TextView textView = (TextView) findViewById(R.id.where);
+ final TextView textView = (TextView) findViewById(R.id.where);
if (textView != null) {
textView.setAutoLinkMask(0);
textView.setText(location);
Linkify.addLinks(textView, mWildcardPattern, "geo:0,0?q=");
+ textView.setOnTouchListener(new OnTouchListener() {
+ public boolean onTouch(View v, MotionEvent event) {
+ try {
+ return v.onTouchEvent(event);
+ } catch (ActivityNotFoundException e) {
+ // ignore
+ return true;
+ }
+ }
+ });
}
}
// Calendar
if (mCalendarsCursor != null) {
- mCalendarsCursor.moveToFirst();
String calendarName = mCalendarsCursor.getString(CALENDARS_INDEX_DISPLAY_NAME);
+ String ownerAccount = mCalendarsCursor.getString(CALENDARS_INDEX_OWNER_ACCOUNT);
+ if (mIsDuplicateName && !calendarName.equalsIgnoreCase(ownerAccount)) {
+ Resources res = getResources();
+ TextView ownerText = (TextView) findViewById(R.id.owner);
+ ownerText.setText(ownerAccount);
+ ownerText.setTextColor(res.getColor(R.color.calendar_owner_text_color));
+ } else {
+ setVisibilityCommon(R.id.owner, View.GONE);
+ }
setTextCommon(R.id.calendar, calendarName);
} else {
setVisibilityCommon(R.id.calendar_container, View.GONE);
}
private void updateAttendees() {
- CharSequence[] entries;
- entries = getResources().getTextArray(R.array.response_labels2);
LinearLayout attendeesLayout = (LinearLayout) findViewById(R.id.attendee_list);
attendeesLayout.removeAllViewsInLayout();
- addAttendeesToLayout(mAcceptedAttendees, attendeesLayout, entries[0]);
- addAttendeesToLayout(mDeclinedAttendees, attendeesLayout, entries[2]);
- addAttendeesToLayout(mTentativeAttendees, attendeesLayout, entries[1]);
+ ++mUpdateCounts;
+ if(mAcceptedAttendees.size() == 0 && mDeclinedAttendees.size() == 0 &&
+ mTentativeAttendees.size() == mNoResponseAttendees.size()) {
+ // If all guests have no response just list them as guests,
+ CharSequence guestsLabel = getResources().getText(R.string.attendees_label);
+ addAttendeesToLayout(mNoResponseAttendees, attendeesLayout, guestsLabel);
+ } else {
+ // If we have any responses then divide them up by response
+ CharSequence[] entries;
+ entries = getResources().getTextArray(R.array.response_labels2);
+ addAttendeesToLayout(mAcceptedAttendees, attendeesLayout, entries[0]);
+ addAttendeesToLayout(mDeclinedAttendees, attendeesLayout, entries[2]);
+ addAttendeesToLayout(mTentativeAttendees, attendeesLayout, entries[1]);
+ }
}
private void addAttendeesToLayout(ArrayList<Attendee> attendees, LinearLayout attendeeList,
ContentResolver cr = getContentResolver();
// Yes/No/Maybe Title
View titleView = mLayoutInflater.inflate(R.layout.contact_item, null);
- titleView.findViewById(R.id.avatar).setVisibility(View.GONE);
+ titleView.findViewById(R.id.badge).setVisibility(View.GONE);
View divider = titleView.findViewById(R.id.separator);
divider.getBackground().setColorFilter(mColor, PorterDuff.Mode.SRC_IN);
// Attendees
int numOfAttendees = attendees.size();
- StringBuilder selection = new StringBuilder(Contacts.ContactMethods.DATA + " IN (");
+ StringBuilder selection = new StringBuilder(Email.DATA + " IN (");
String[] selectionArgs = new String[numOfAttendees];
for (int i = 0; i < numOfAttendees; ++i) {
selectionArgs[i] = attendee.mEmail;
View v = mLayoutInflater.inflate(R.layout.contact_item, null);
- v.setOnClickListener(contactOnClickListener);
v.setTag(attendee);
View separator = v.findViewById(R.id.separator);
tv.setText(name);
ViewHolder vh = new ViewHolder();
- vh.avatar = (ImageView) v.findViewById(R.id.avatar);
+ vh.badge = (QuickContactBadge) v.findViewById(R.id.badge);
+ vh.badge.assignContactFromEmail(attendee.mEmail, true);
vh.presence = (ImageView) v.findViewById(R.id.presence);
- mPresenceStatuses.put(attendee.mEmail, vh);
+ mViewHolders.put(attendee.mEmail, vh);
if (i == 0) {
selection.append('?');
}
selection.append(')');
- mPresenceQueryHandler.startQuery(0, attendees, CONTACT_PRESENCE_URI, PRESENCE_PROJECTION,
- selection.toString(), selectionArgs, null);
+ mPresenceQueryHandler.startQuery(mUpdateCounts, attendees, CONTACT_DATA_WITH_PRESENCE_URI,
+ PRESENCE_PROJECTION, selection.toString(), selectionArgs, null);
}
private class PresenceQueryHandler extends AsyncQueryHandler {
mContext = context;
}
- @SuppressWarnings("unchecked")
@Override
- protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
- cursor.moveToPosition(-1);
- while (cursor.moveToNext()) {
- String email = cursor.getString(PRESENCE_PROJECTION_EMAIL_INDEX);
- ViewHolder vh = mPresenceStatuses.get(email);
- ImageView presenceView = vh.presence;
- if (presenceView != null) {
- int status = cursor.getInt(PRESENCE_PROJECTION_PRESENCE_INDEX);
- presenceView.setImageResource(Presence.getPresenceIconResourceId(status));
- presenceView.setVisibility(View.VISIBLE);
+ protected void onQueryComplete(int queryIndex, Object cookie, Cursor cursor) {
+ if (cursor == null) {
+ if (DEBUG) {
+ Log.e(TAG, "onQueryComplete: cursor == null");
}
+ return;
}
- ArrayList<Attendee> attendees = (ArrayList<Attendee>) cookie;
- for (Attendee attendee : attendees) {
- Uri uri = Uri.withAppendedPath(People.WITH_EMAIL_OR_IM_FILTER_URI, Uri
- .encode(attendee.mEmail));
- // TODO Get rid of this query.
- Cursor personCursor = mContentResolver.query(uri, PEOPLE_PROJECTION, null, null,
- null);
- if (personCursor != null) {
- if (personCursor.moveToFirst()) {
- Uri personUri = ContentUris.withAppendedId(People.CONTENT_URI, personCursor
- .getInt(0));
- ViewHolder vh = mPresenceStatuses.get(attendee.mEmail);
- if (vh != null) {
- ContactsAsyncHelper.updateImageViewWithContactPhotoAsync(mContext,
- vh.avatar, personUri, -1);
- }
+ try {
+ cursor.moveToPosition(-1);
+ while (cursor.moveToNext()) {
+ String email = cursor.getString(PRESENCE_PROJECTION_EMAIL_INDEX);
+ int contactId = cursor.getInt(PRESENCE_PROJECTION_CONTACT_ID_INDEX);
+ ViewHolder vh = mViewHolders.get(email);
+ int photoId = cursor.getInt(PRESENCE_PROJECTION_PHOTO_ID_INDEX);
+ if (DEBUG) {
+ Log.e(TAG, "onQueryComplete Id: " + contactId + " PhotoId: " + photoId
+ + " Email: " + email);
+ }
+ if (vh == null) {
+ continue;
+ }
+ ImageView presenceView = vh.presence;
+ if (presenceView != null) {
+ int status = cursor.getInt(PRESENCE_PROJECTION_PRESENCE_INDEX);
+ presenceView.setImageResource(Presence.getPresenceIconResourceId(status));
+ presenceView.setVisibility(View.VISIBLE);
+ }
+
+ if (photoId > 0 && vh.updateCounts < queryIndex) {
+ vh.updateCounts = queryIndex;
+ Uri personUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
+
+ // TODO, modify to batch queries together
+ ContactsAsyncHelper.updateImageViewWithContactPhotoAsync(mContext, vh.badge,
+ personUri, R.drawable.ic_contact_picture);
}
- personCursor.close();
}
+ } finally {
+ cursor.close();
}
}
}
void updateResponse() {
- if (mVisibility < Calendars.CONTRIBUTOR_ACCESS ||
- // TODO Temp fix to get past FAST tests.
- // Remove this once content provider provides user relationship info via event table
- (false && mRelationship != Attendees.RELATIONSHIP_ATTENDEE)) {
+ // we only let the user accept/reject/etc. a meeting if:
+ // a) you can edit the event's containing calendar AND
+ // b) you're not the organizer and only attendee AND
+ // c) organizerCanRespond is enabled for the calendar
+ // (if the attendee data has been hidden, the visible number of attendees
+ // will be 1 -- the calendar owner's).
+ // (there are more cases involved to be 100% accurate, such as
+ // paying attention to whether or not an attendee status was
+ // included in the feed, but we're currently omitting those corner cases
+ // for simplicity).
+ if (!mCanModifyCalendar || (mHasAttendeeData && mIsOrganizer && mNumOfAttendees <= 1) ||
+ (mIsOrganizer && !mOrganizerCanRespond)) {
setVisibilityCommon(R.id.response_container, View.GONE);
return;
}
Spinner spinner = (Spinner) findViewById(R.id.response_value);
- mOriginalAttendeeResponse = ATTENDEE_NO_RESPONSE;
- if (mAttendeesCursor != null) {
- mOriginalAttendeeResponse = mAttendeesCursor.getInt(ATTENDEES_INDEX_STATUS);
- }
mResponseOffset = 0;
/* If the user has previously responded to this event
* Send the intent that shows the Contact info corresponding to the email address.
*/
public void showContactInfo(Attendee attendee, Rect rect) {
- Uri contactUri = Uri.fromParts("mailto", attendee.mEmail, null);
-
- Intent contactIntent = new Intent(Contacts.Intents.SHOW_OR_CREATE_CONTACT);
- contactIntent.setData(contactUri);
-
- // Pass along full E-mail string for possible create dialog
- Rfc822Token sender = new Rfc822Token(attendee.mName, attendee.mEmail, null);
- contactIntent.putExtra(Contacts.Intents.EXTRA_CREATE_DESCRIPTION,
- sender.toString());
-
- // Mark target position using on-screen coordinates
- // TODO uncomment when contacts code is in.
- // contactIntent.putExtra(Intents.EXTRA_TARGET_RECT, rect);
+ // First perform lookup query to find existing contact
+ final ContentResolver resolver = getContentResolver();
+ final String address = attendee.mEmail;
+ final Uri dataUri = Uri.withAppendedPath(CommonDataKinds.Email.CONTENT_FILTER_URI,
+ Uri.encode(address));
+ final Uri lookupUri = ContactsContract.Data.getContactLookupUri(resolver, dataUri);
+
+ if (lookupUri != null) {
+ // Found matching contact, trigger QuickContact
+ QuickContact.showQuickContact(this, rect, lookupUri, QuickContact.MODE_MEDIUM, null);
+ } else {
+ // No matching contact, ask user to create one
+ final Uri mailUri = Uri.fromParts("mailto", address, null);
+ final Intent intent = new Intent(Intents.SHOW_OR_CREATE_CONTACT, mailUri);
+
+ // Pass along full E-mail string for possible create dialog
+ Rfc822Token sender = new Rfc822Token(attendee.mName, attendee.mEmail, null);
+ intent.putExtra(Intents.EXTRA_CREATE_DESCRIPTION, sender.toString());
+
+ // Only provide personal name hint if we have one
+ final String senderPersonal = attendee.mName;
+ if (!TextUtils.isEmpty(senderPersonal)) {
+ intent.putExtra(Intents.Insert.NAME, senderPersonal);
+ }
- // Only provide personal name hint if we have one
- if (attendee.mName != null && attendee.mName.length() > 0) {
- contactIntent.putExtra(Intents.Insert.NAME, attendee.mName);
+ startActivity(intent);
}
-
- startActivity(contactIntent);
}
}