summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSara Ting <sarating@google.com>2012-03-15 15:24:36 -0700
committerSara Ting <sarating@google.com>2012-03-23 10:01:34 -0700
commit42896f76d81dbae873614340a3a78b29e7d463d2 (patch)
tree604a8f9513b00a5a37aec568e25466e2f4ecf0e9
parentfcac9e3091e0c9cf7a3fb37ebbf4ddff44cc4d99 (diff)
downloadandroid_packages_apps_Calendar-42896f76d81dbae873614340a3a78b29e7d463d2.tar.gz
android_packages_apps_Calendar-42896f76d81dbae873614340a3a78b29e7d463d2.tar.bz2
android_packages_apps_Calendar-42896f76d81dbae873614340a3a78b29e7d463d2.zip
Adding 'Email attendees' button to event info screen.
Adding ability to email all the attendees of an event. All declining attendees are put in the 'cc' field, with all others in the 'to' field. Change-Id: Id6b2dbcc70a2aac22ce9027e0d356a5e8c785ba3
-rw-r--r--res/drawable-hdpi/ic_menu_email_holo_light.pngbin0 -> 300 bytes
-rw-r--r--res/drawable-mdpi/ic_menu_email_holo_light.pngbin0 -> 223 bytes
-rw-r--r--res/drawable-xhdpi/ic_menu_email_holo_light.pngbin0 -> 424 bytes
-rw-r--r--res/drawable/event_info_mail_button.xml26
-rw-r--r--res/layout/event_info_dialog.xml14
-rw-r--r--res/menu/event_info_title_bar.xml13
-rw-r--r--res/values/strings.xml6
-rw-r--r--src/com/android/calendar/EventInfoFragment.java191
8 files changed, 238 insertions, 12 deletions
diff --git a/res/drawable-hdpi/ic_menu_email_holo_light.png b/res/drawable-hdpi/ic_menu_email_holo_light.png
new file mode 100644
index 00000000..b590822e
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_email_holo_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_email_holo_light.png b/res/drawable-mdpi/ic_menu_email_holo_light.png
new file mode 100644
index 00000000..4f194f89
--- /dev/null
+++ b/res/drawable-mdpi/ic_menu_email_holo_light.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_email_holo_light.png b/res/drawable-xhdpi/ic_menu_email_holo_light.png
new file mode 100644
index 00000000..a2d821ae
--- /dev/null
+++ b/res/drawable-xhdpi/ic_menu_email_holo_light.png
Binary files differ
diff --git a/res/drawable/event_info_mail_button.xml b/res/drawable/event_info_mail_button.xml
new file mode 100644
index 00000000..82efed8f
--- /dev/null
+++ b/res/drawable/event_info_mail_button.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+ <selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_focused="true" android:state_pressed="false"
+ android:drawable="@drawable/ic_menu_email_holo_light" />
+
+ <item android:state_focused="true" android:state_pressed="true"
+ android:drawable="@drawable/ic_menu_email_holo_light" />
+
+ <item android:state_focused="false" android:state_pressed="true"
+ android:drawable="@drawable/ic_menu_email_holo_light" />
+ <item android:drawable="@drawable/ic_menu_email_holo_light" />
+</selector> \ No newline at end of file
diff --git a/res/layout/event_info_dialog.xml b/res/layout/event_info_dialog.xml
index eded62b7..c625ad91 100644
--- a/res/layout/event_info_dialog.xml
+++ b/res/layout/event_info_dialog.xml
@@ -97,17 +97,31 @@
android:layout_height="32dip"
android:layout_weight="1"
android:enabled="false"
+ android:visibility="gone"
android:layout_marginTop="8dip"
android:layout_marginRight="16dip"
style="?android:attr/buttonBarButtonStyle"
android:background="@drawable/event_info_compose_button" />
<Button
+ android:id="@+id/mail"
+ android:layout_width="32dip"
+ android:layout_height="32dip"
+ android:layout_weight="1"
+ android:layout_marginTop="8dip"
+ android:layout_marginRight="16dip"
+ android:enabled="false"
+ android:visibility="gone"
+ style="?android:attr/buttonBarButtonStyle"
+ android:background="@drawable/event_info_mail_button" />
+ <Button
android:id="@+id/delete"
android:layout_width="32dip"
android:layout_height="32dip"
android:layout_marginTop="8dip"
android:layout_marginLeft="16dip"
android:layout_weight="1"
+ android:enabled="false"
+ android:visibility="gone"
style="?android:attr/buttonBarButtonStyle"
android:background="@drawable/event_info_delete_button" />
</LinearLayout>
diff --git a/res/menu/event_info_title_bar.xml b/res/menu/event_info_title_bar.xml
index 08c98632..ebae7574 100644
--- a/res/menu/event_info_title_bar.xml
+++ b/res/menu/event_info_title_bar.xml
@@ -21,11 +21,20 @@
android:title="@string/edit_label"
android:icon="@drawable/ic_menu_compose_holo_light"
android:showAsAction="withText|always"
- android:visibility="visible" />
+ android:enabled="false"
+ android:visible="false" />
+ <item android:id="@+id/info_action_mail"
+ android:alphabeticShortcut="m"
+ android:title="@string/email_attendees_label"
+ android:icon="@drawable/ic_menu_email_holo_light"
+ android:showAsAction="withText|always"
+ android:enabled="false"
+ android:visible="false" />
<item android:id="@+id/info_action_delete"
android:alphabeticShortcut="x"
android:title="@string/delete_label"
android:icon="@drawable/ic_menu_trash_holo_light"
android:showAsAction="withText|always"
- android:visibility="visible" />
+ android:enabled="false"
+ android:visible="false" />
</menu>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 9471f27a..abc93db4 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -270,6 +270,12 @@
<string name="response_maybe">Maybe</string>
<!-- Response for whether attending an event - declined [CHAR LIMIT=10]-->
<string name="response_no">No</string>
+ <!-- Label for emailing attendees -->
+ <string name="email_attendees_label">Mail</string>
+ <!-- This is shown in the popup picker when emailing attendees -->
+ <string name="email_picker_label">Email with</string>
+ <!-- The is shown in the email subject as the prefix appended to the event title -->
+ <string translatable="false" name="email_subject_prefix">Re: </string>
<!-- Event Info strings-->
<!-- Time Zone Label [CHAR LIMIT=12]-->
diff --git a/src/com/android/calendar/EventInfoFragment.java b/src/com/android/calendar/EventInfoFragment.java
index 465f7b01..6a070eb8 100644
--- a/src/com/android/calendar/EventInfoFragment.java
+++ b/src/com/android/calendar/EventInfoFragment.java
@@ -225,11 +225,13 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange
Calendars._ID, // 0
Calendars.CALENDAR_DISPLAY_NAME, // 1
Calendars.OWNER_ACCOUNT, // 2
- Calendars.CAN_ORGANIZER_RESPOND // 3
+ Calendars.CAN_ORGANIZER_RESPOND, // 3
+ Calendars.ACCOUNT_NAME // 4
};
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 int CALENDARS_INDEX_ACCOUNT_NAME = 4;
static final String CALENDARS_WHERE = Calendars._ID + "=?";
static final String CALENDARS_DUPLICATE_NAME_WHERE = Calendars.CALENDAR_DISPLAY_NAME + "=?";
@@ -255,12 +257,16 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange
private boolean mAllDay;
private boolean mHasAttendeeData;
+ private String mEventOrganizer;
private boolean mIsOrganizer;
private long mCalendarOwnerAttendeeId = EditEventHelper.ATTENDEE_ID_NONE;
private boolean mOwnerCanRespond;
+ private String mSyncAccountName;
private String mCalendarOwnerAccount;
private boolean mCanModifyCalendar;
private boolean mCanModifyEvent;
+ private boolean mVisibleMailButton;
+ private boolean mEnableMailButton;
private boolean mIsBusyFreeCalendar;
private int mNumOfAttendees;
@@ -835,6 +841,9 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange
updateAttendees(view);
}
}
+ // Delay enabling the mail button until after attendees are processed.
+ mEnableMailButton = true;
+ updateMailButton();
}
@Override
@@ -892,6 +901,9 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange
startActivity(intent);
mActivity.finish();
break;
+ case R.id.info_action_mail:
+ emailAttendees();
+ break;
case R.id.info_action_delete:
mDeleteHelper =
new DeleteEventHelper(mActivity, mActivity, true /* exitWhenDone */);
@@ -1534,6 +1546,7 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange
String tempAccount = mCalendarsCursor.getString(CALENDARS_INDEX_OWNER_ACCOUNT);
mCalendarOwnerAccount = (tempAccount == null) ? "" : tempAccount;
mOwnerCanRespond = mCalendarsCursor.getInt(CALENDARS_INDEX_OWNER_CAN_RESPOND) != 0;
+ mSyncAccountName = mCalendarsCursor.getString(CALENDARS_INDEX_ACCOUNT_NAME);
String displayName = mCalendarsCursor.getString(CALENDARS_INDEX_DISPLAY_NAME);
@@ -1542,9 +1555,9 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange
CALENDARS_PROJECTION, CALENDARS_DUPLICATE_NAME_WHERE,
new String[] {displayName}, null);
- String eventOrganizer = mEventCursor.getString(EVENT_INDEX_ORGANIZER);
- mIsOrganizer = mCalendarOwnerAccount.equalsIgnoreCase(eventOrganizer);
- setTextCommon(view, R.id.organizer, eventOrganizer);
+ mEventOrganizer = mEventCursor.getString(EVENT_INDEX_ORGANIZER);
+ mIsOrganizer = mCalendarOwnerAccount.equalsIgnoreCase(mEventOrganizer);
+ setTextCommon(view, R.id.organizer, mEventOrganizer);
if (!mIsOrganizer) {
setVisibilityCommon(view, R.id.organizer_container, View.VISIBLE);
} else {
@@ -1558,6 +1571,9 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange
mIsBusyFreeCalendar =
mEventCursor.getInt(EVENT_INDEX_ACCESS_LEVEL) == Calendars.CAL_ACCESS_FREEBUSY;
+ // Use this var so the mail button doesn't pop up before the others in the title bar.
+ mVisibleMailButton = true;
+
if (!mIsBusyFreeCalendar) {
Button b = (Button) mView.findViewById(R.id.edit);
b.setEnabled(true);
@@ -1577,20 +1593,32 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange
});
}
View button;
- if (!mCanModifyCalendar) {
+ if (mCanModifyCalendar) {
button = mView.findViewById(R.id.delete);
if (button != null) {
- button.setEnabled(false);
- button.setVisibility(View.GONE);
+ button.setEnabled(true);
+ button.setVisibility(View.VISIBLE);
}
}
- if (!mCanModifyEvent) {
+ if (mCanModifyEvent) {
button = mView.findViewById(R.id.edit);
if (button != null) {
- button.setEnabled(false);
- button.setVisibility(View.GONE);
+ button.setEnabled(true);
+ button.setVisibility(View.VISIBLE);
}
}
+ Button mailButton = (Button) mView.findViewById(R.id.mail);
+ if (mailButton != null) {
+ mailButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ emailAttendees();
+ }
+ });
+ // Make the button visible now but enable later after processing attendees.
+ mailButton.setVisibility(View.VISIBLE);
+ }
+
if ((!mIsDialog && !mIsTabletConfig ||
mWindowStyle == EventInfoFragment.FULL_WINDOW_STYLE) && mMenu != null) {
mActivity.invalidateOptionsMenu();
@@ -1601,6 +1629,22 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange
}
}
+ private void updateMailButton() {
+ // For tablet...
+ Button mailButton = (mView == null) ? null : (Button) mView.findViewById(R.id.mail);
+ if (mailButton != null) {
+ mailButton.setEnabled(mEnableMailButton);
+ }
+
+ // For phone...
+ MenuItem mailItem = (mMenu == null) ? null : mMenu.findItem(R.id.info_action_mail);
+ if (mailItem != null) {
+ // The email functionality requires the attendees being processed, so delay
+ // enabling this until after initAttendeesCursor().
+ mailItem.setEnabled(mEnableMailButton);
+ }
+ }
+
/**
*
*/
@@ -1610,6 +1654,7 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange
}
MenuItem delete = mMenu.findItem(R.id.info_action_delete);
MenuItem edit = mMenu.findItem(R.id.info_action_edit);
+ MenuItem mail = mMenu.findItem(R.id.info_action_mail);
if (delete != null) {
delete.setVisible(mCanModifyCalendar);
delete.setEnabled(mCanModifyCalendar);
@@ -1618,6 +1663,15 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange
edit.setVisible(mCanModifyEvent);
edit.setEnabled(mCanModifyEvent);
}
+ if (mail != null) {
+ // The mail button should always be visible but we don't want to show it before
+ // the other buttons, so delay showing it until the others are processed.
+ mail.setVisible(mVisibleMailButton);
+
+ // The email functionality requires the attendees being processed, so delay
+ // enabling this until after initAttendeesCursor().
+ mail.setEnabled(mEnableMailButton);
+ }
}
private void updateAttendees(View view) {
@@ -1968,6 +2022,123 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange
}
/**
+ * Adds the attendee's email to the list if:
+ * (1) the attendee is not a resource like a conference room or another calendar.
+ * Catch most of these by filtering out suffix calendar.google.com.
+ * (2) the attendee is not the viewer, to prevent mailing himself.
+ */
+ private void addIfEmailable(ArrayList<String> emailList, String email) {
+ if (email != null && !email.equals(mSyncAccountName) &&
+ !email.endsWith("calendar.google.com")) {
+ emailList.add(email);
+ }
+ }
+
+ /**
+ * Email all the attendees of the event, except for the viewer (so as to not email
+ * himself) and resources like conference rooms.
+ */
+ private void emailAttendees() {
+ Resources res = getActivity().getResources();
+
+ // Use the event title as the email subject (prepended with 'Re: ').
+ String subject = null;
+ if (mTitle != null && mTitle.getText() != null) {
+ subject = res.getString(R.string.email_subject_prefix) + mTitle.getText().toString();
+ }
+
+ // The declined attendees will go in the 'cc' line, all others will go in the 'to' line.
+ ArrayList<String> toEmails = new ArrayList<String>();
+ for (Attendee attendee : mAcceptedAttendees) {
+ addIfEmailable(toEmails, attendee.mEmail);
+ }
+ for (Attendee attendee : mTentativeAttendees) {
+ addIfEmailable(toEmails, attendee.mEmail);
+ }
+ for (Attendee attendee : mNoResponseAttendees) {
+ addIfEmailable(toEmails, attendee.mEmail);
+ }
+ ArrayList<String> ccEmails = new ArrayList<String>();
+ for (Attendee attendee : this.mDeclinedAttendees) {
+ addIfEmailable(ccEmails, attendee.mEmail);
+ }
+
+ // The meeting organizer doesn't appear as an attendee sometimes (particularly
+ // when viewing someone else's calendar), so add the organizer now.
+ if (mEventOrganizer != null && !toEmails.contains(mEventOrganizer) &&
+ !ccEmails.contains(mEventOrganizer)) {
+ addIfEmailable(toEmails, mEventOrganizer);
+ }
+
+ // The Email app behaves strangely when there is nothing in the 'mailto' part,
+ // so move all the 'cc' emails to the 'to' list. Gmail works fine though.
+ if (toEmails.size() <= 0 && ccEmails.size() > 0) {
+ toEmails.addAll(ccEmails);
+ ccEmails.clear();
+ }
+
+ if (toEmails.size() <= 0) {
+ // If there are no 'to' emails, use the SEND intent. Cannot use SENDTO since
+ // the Email app does not handle an empty 'to' value.
+ Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
+ emailIntent.setType("text/plain");
+ if (subject != null) {
+ emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, subject);
+ }
+ emailIntent.putExtra("account", mCalendarOwnerAccount);
+ startActivity(Intent.createChooser(emailIntent,
+ res.getString(R.string.email_picker_label)));
+ } else {
+ // Otherwise use the SENDTO intent with a 'mailto' URI, because using SEND will
+ // cause the picker to show apps like text messaging, which does not make sense
+ // for email addresses. We put all data in the URI instead of using the extra
+ // Intent fields (ie. EXTRA_CC, etc) because some email apps might not handle
+ // those (though gmail does).
+ Uri.Builder uriBuilder = new Uri.Builder();
+ uriBuilder.scheme("mailto");
+
+ // We will append the first email to the 'mailto' field later (because the
+ // current state of the Email app requires it). Add the remaining 'to' values
+ // here. When the email codebase is updated, we can simplify this.
+ if (toEmails.size() > 1) {
+ for (int i = 1; i < toEmails.size(); i++) {
+ // The Email app requires repeated parameter settings instead of
+ // a single comma-separated list.
+ uriBuilder.appendQueryParameter("to", toEmails.get(i));
+ }
+ }
+
+ // Add the subject parameter.
+ if (subject != null) {
+ uriBuilder.appendQueryParameter("subject", subject);
+ }
+
+ // Add the cc parameters.
+ if (ccEmails.size() > 0) {
+ for (String email : ccEmails) {
+ uriBuilder.appendQueryParameter("cc", email);
+ }
+ }
+
+ // Insert the first email after 'mailto:' in the URI manually since Uri.Builder
+ // doesn't seem to have a way to do this.
+ String uri = uriBuilder.toString();
+ if (uri.startsWith("mailto:")) {
+ StringBuilder builder = new StringBuilder(uri);
+ builder.insert(7, Uri.encode(toEmails.get(0)));
+ uri = builder.toString();
+ }
+
+ // Start the email intent. Email from the account of the calendar owner in case there
+ // are multiple email accounts.
+ Intent emailIntent = new Intent(android.content.Intent.ACTION_SENDTO, Uri.parse(uri));
+ emailIntent.putExtra("account", mCalendarOwnerAccount);
+ startActivity(Intent.createChooser(emailIntent,
+ res.getString(R.string.email_picker_label)));
+ }
+ }
+
+ /**
* Loads an integer array asset into a list.
*/
private static ArrayList<Integer> loadIntegerArray(Resources r, int resNum) {