Loading src/com/android/calendar/EventInfoFragment.java +1 −1 Original line number Diff line number Diff line Loading @@ -2089,7 +2089,7 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange * (2) the attendee is not the viewer, to prevent mailing himself. */ private void addIfEmailable(ArrayList<String> emailList, String email) { if (Utils.isValidEmail(email) && !email.equals(mSyncAccountName)) { if (Utils.isEmailableFrom(email, mSyncAccountName)) { emailList.add(email); } } Loading src/com/android/calendar/Utils.java +10 −0 Original line number Diff line number Diff line Loading @@ -1371,6 +1371,16 @@ public class Utils { return email != null && !email.endsWith(MACHINE_GENERATED_ADDRESS); } /** * Returns true if: * (1) the email is not a resource like a conference room or another calendar. * Catch most of these by filtering out suffix calendar.google.com. * (2) the email is not equal to the sync account to prevent mailing himself. */ public static boolean isEmailableFrom(String email, String syncAccountName) { return Utils.isValidEmail(email) && !email.equals(syncAccountName); } /** * Inserts a drawable with today's day into the today's icon in the option menu * @param icon - today's icon from the options menu Loading src/com/android/calendar/alerts/AlertReceiver.java +138 −32 Original line number Diff line number Diff line Loading @@ -30,6 +30,8 @@ import android.content.Intent; import android.content.res.Resources; import android.database.Cursor; import android.net.Uri; import android.os.Handler; import android.os.HandlerThread; import android.os.PowerManager; import android.provider.CalendarContract.Attendees; import android.provider.CalendarContract.Calendars; Loading Loading @@ -59,7 +61,9 @@ import java.util.List; public class AlertReceiver extends BroadcastReceiver { private static final String TAG = "AlertReceiver"; private static final String DELETE_ACTION = "delete"; private static final String DELETE_ALL_ACTION = "com.android.calendar.DELETEALL"; private static final String MAIL_ACTION = "com.android.calendar.MAIL"; private static final String EXTRA_EVENT_ID = "eventid"; static final Object mStartingServiceSync = new Object(); static PowerManager.WakeLock mStartingService; Loading @@ -67,12 +71,19 @@ public class AlertReceiver extends BroadcastReceiver { public static final String ACTION_DISMISS_OLD_REMINDERS = "removeOldReminders"; private static final int NOTIFICATION_DIGEST_MAX_LENGTH = 3; private static Handler sAsyncHandler; static { HandlerThread thr = new HandlerThread("AlertReceiver async"); thr.start(); sAsyncHandler = new Handler(thr.getLooper()); } @Override public void onReceive(Context context, Intent intent) { public void onReceive(final Context context, final Intent intent) { if (AlertService.DEBUG) { Log.d(TAG, "onReceive: a=" + intent.getAction() + " " + intent.toString()); } if (DELETE_ACTION.equals(intent.getAction())) { if (DELETE_ALL_ACTION.equals(intent.getAction())) { /* The user has clicked the "Clear All Notifications" * buttons so dismiss all Calendar alerts. Loading @@ -80,6 +91,27 @@ public class AlertReceiver extends BroadcastReceiver { // TODO Grab a wake lock here? Intent serviceIntent = new Intent(context, DismissAlarmsService.class); context.startService(serviceIntent); } else if (MAIL_ACTION.equals(intent.getAction())) { // Close the notification shade. Intent closeNotificationShadeIntent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); context.sendBroadcast(closeNotificationShadeIntent); // Now start the email intent. final long eventId = intent.getLongExtra(EXTRA_EVENT_ID, -1); if (eventId != -1) { final PendingResult result = goAsync(); Runnable worker = new Runnable() { @Override public void run() { Intent emailIntent = createEmailIntent(context, eventId); if (emailIntent != null) { context.startActivity(emailIntent); } result.finish(); } }; sAsyncHandler.post(worker); } } else { Intent i = new Intent(); i.setClass(context, AlertService.class); Loading Loading @@ -218,7 +250,7 @@ public class AlertReceiver extends BroadcastReceiver { resources.getString(R.string.snooze_5min_label), snoozeIntent); // Create an email button. PendingIntent emailIntent = createEmailIntent(context, eventId, title); PendingIntent emailIntent = createBroadcastMailIntent(context, eventId, title); if (emailIntent != null) { notificationBuilder.addAction(R.drawable.ic_menu_email_holo_dark, resources.getString(R.string.email_guests_label), emailIntent); Loading Loading @@ -293,7 +325,7 @@ public class AlertReceiver extends BroadcastReceiver { // expired events. Intent deleteIntent = new Intent(); deleteIntent.setClass(context, DismissAlarmsService.class); deleteIntent.setAction(DELETE_ACTION); deleteIntent.setAction(DELETE_ALL_ACTION); deleteIntent.putExtra(AlertUtils.DELETE_EXPIRED_ONLY_KEY, true); PendingIntent pendingDeleteIntent = PendingIntent.getService(context, 0, deleteIntent, PendingIntent.FLAG_UPDATE_CURRENT); Loading Loading @@ -380,17 +412,78 @@ public class AlertReceiver extends BroadcastReceiver { private static final String[] EVENT_PROJECTION = new String[] { Calendars.OWNER_ACCOUNT, // 0 Calendars.ACCOUNT_NAME // 1 Calendars.ACCOUNT_NAME, // 1 Events.TITLE, // 2 }; private static final int EVENT_INDEX_OWNER_ACCOUNT = 0; private static final int EVENT_INDEX_ACCOUNT_NAME = 1; private static final int EVENT_INDEX_TITLE = 2; private static Cursor getEventCursor(Context context, long eventId) { return context.getContentResolver().query( ContentUris.withAppendedId(Events.CONTENT_URI, eventId), EVENT_PROJECTION, null, null, null); } private static Cursor getAttendeesCursor(Context context, long eventId) { return context.getContentResolver().query(Attendees.CONTENT_URI, ATTENDEES_PROJECTION, ATTENDEES_WHERE, new String[] { Long.toString(eventId) }, ATTENDEES_SORT_ORDER); } /** * Creates a broadcast pending intent that fires to AlertReceiver when the email button * is clicked. */ private static PendingIntent createBroadcastMailIntent(Context context, long eventId, String eventTitle) { // Query for viewer account. String syncAccount = null; Cursor eventCursor = getEventCursor(context, eventId); try { if (eventCursor != null && eventCursor.moveToFirst()) { syncAccount = eventCursor.getString(EVENT_INDEX_ACCOUNT_NAME); } } finally { if (eventCursor != null) { eventCursor.close(); } } // Query attendees to see if there are any to email. Cursor attendeesCursor = getAttendeesCursor(context, eventId); try { if (attendeesCursor != null && attendeesCursor.moveToFirst()) { do { String email = attendeesCursor.getString(ATTENDEES_INDEX_EMAIL); if (Utils.isEmailableFrom(email, syncAccount)) { // Send intent back to ourself first for a couple reasons: // 1) Workaround issue where clicking action button in notification does // not automatically close the notification shade. // 2) Attendees list in email will always be up to date. Intent broadcastIntent = new Intent(MAIL_ACTION); broadcastIntent.setClass(context, AlertReceiver.class); broadcastIntent.putExtra(EXTRA_EVENT_ID, eventId); return PendingIntent.getBroadcast(context, Long.valueOf(eventId).hashCode(), broadcastIntent, PendingIntent.FLAG_CANCEL_CURRENT); } } while (attendeesCursor.moveToNext()); } return null; } finally { if (attendeesCursor != null) { attendeesCursor.close(); } } } /** * Creates an Intent for emailing the attendees of the event. Returns null if there * are no emailable attendees. */ private static PendingIntent createEmailIntent(Context context, long eventId, String eventTitle) { private static Intent createEmailIntent(Context context, long eventId) { ContentResolver resolver = context.getContentResolver(); // TODO: Refactor to move query part into Utils.createEmailAttendeeIntent, to Loading @@ -399,20 +492,29 @@ public class AlertReceiver extends BroadcastReceiver { // Query for the owner account(s). String ownerAccount = null; String syncAccount = null; Cursor eventCursor = resolver.query( ContentUris.withAppendedId(Events.CONTENT_URI, eventId), EVENT_PROJECTION, null, null, null); if (eventCursor.moveToFirst()) { String eventTitle = null; Cursor eventCursor = getEventCursor(context, eventId); try { if (eventCursor != null && eventCursor.moveToFirst()) { ownerAccount = eventCursor.getString(EVENT_INDEX_OWNER_ACCOUNT); syncAccount = eventCursor.getString(EVENT_INDEX_ACCOUNT_NAME); eventTitle = eventCursor.getString(EVENT_INDEX_TITLE); } } finally { if (eventCursor != null) { eventCursor.close(); } } if (TextUtils.isEmpty(eventTitle)) { eventTitle = context.getResources().getString(R.string.no_title_label); } // Query for the attendees. List<String> toEmails = new ArrayList<String>(); List<String> ccEmails = new ArrayList<String>(); Cursor attendeesCursor = resolver.query(Attendees.CONTENT_URI, ATTENDEES_PROJECTION, ATTENDEES_WHERE, new String[] { Long.toString(eventId) }, ATTENDEES_SORT_ORDER); if (attendeesCursor.moveToFirst()) { Cursor attendeesCursor = getAttendeesCursor(context, eventId); try { if (attendeesCursor != null && attendeesCursor.moveToFirst()) { do { int status = attendeesCursor.getInt(ATTENDEES_INDEX_STATUS); String email = attendeesCursor.getString(ATTENDEES_INDEX_EMAIL); Loading @@ -425,6 +527,11 @@ public class AlertReceiver extends BroadcastReceiver { } } while (attendeesCursor.moveToNext()); } } finally { if (attendeesCursor != null) { attendeesCursor.close(); } } Intent intent = null; if (ownerAccount != null && (toEmails.size() > 0 || ccEmails.size() > 0)) { Loading @@ -436,14 +543,13 @@ public class AlertReceiver extends BroadcastReceiver { return null; } else { intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); return PendingIntent.getActivity(context, Long.valueOf(eventId).hashCode(), intent, PendingIntent.FLAG_CANCEL_CURRENT); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); return intent; } } private static void addIfEmailable(List<String> emailList, String email, String syncAccount) { if (Utils.isValidEmail(email) && !email.equals(syncAccount)) { if (Utils.isEmailableFrom(email, syncAccount)) { emailList.add(email); } } Loading Loading
src/com/android/calendar/EventInfoFragment.java +1 −1 Original line number Diff line number Diff line Loading @@ -2089,7 +2089,7 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange * (2) the attendee is not the viewer, to prevent mailing himself. */ private void addIfEmailable(ArrayList<String> emailList, String email) { if (Utils.isValidEmail(email) && !email.equals(mSyncAccountName)) { if (Utils.isEmailableFrom(email, mSyncAccountName)) { emailList.add(email); } } Loading
src/com/android/calendar/Utils.java +10 −0 Original line number Diff line number Diff line Loading @@ -1371,6 +1371,16 @@ public class Utils { return email != null && !email.endsWith(MACHINE_GENERATED_ADDRESS); } /** * Returns true if: * (1) the email is not a resource like a conference room or another calendar. * Catch most of these by filtering out suffix calendar.google.com. * (2) the email is not equal to the sync account to prevent mailing himself. */ public static boolean isEmailableFrom(String email, String syncAccountName) { return Utils.isValidEmail(email) && !email.equals(syncAccountName); } /** * Inserts a drawable with today's day into the today's icon in the option menu * @param icon - today's icon from the options menu Loading
src/com/android/calendar/alerts/AlertReceiver.java +138 −32 Original line number Diff line number Diff line Loading @@ -30,6 +30,8 @@ import android.content.Intent; import android.content.res.Resources; import android.database.Cursor; import android.net.Uri; import android.os.Handler; import android.os.HandlerThread; import android.os.PowerManager; import android.provider.CalendarContract.Attendees; import android.provider.CalendarContract.Calendars; Loading Loading @@ -59,7 +61,9 @@ import java.util.List; public class AlertReceiver extends BroadcastReceiver { private static final String TAG = "AlertReceiver"; private static final String DELETE_ACTION = "delete"; private static final String DELETE_ALL_ACTION = "com.android.calendar.DELETEALL"; private static final String MAIL_ACTION = "com.android.calendar.MAIL"; private static final String EXTRA_EVENT_ID = "eventid"; static final Object mStartingServiceSync = new Object(); static PowerManager.WakeLock mStartingService; Loading @@ -67,12 +71,19 @@ public class AlertReceiver extends BroadcastReceiver { public static final String ACTION_DISMISS_OLD_REMINDERS = "removeOldReminders"; private static final int NOTIFICATION_DIGEST_MAX_LENGTH = 3; private static Handler sAsyncHandler; static { HandlerThread thr = new HandlerThread("AlertReceiver async"); thr.start(); sAsyncHandler = new Handler(thr.getLooper()); } @Override public void onReceive(Context context, Intent intent) { public void onReceive(final Context context, final Intent intent) { if (AlertService.DEBUG) { Log.d(TAG, "onReceive: a=" + intent.getAction() + " " + intent.toString()); } if (DELETE_ACTION.equals(intent.getAction())) { if (DELETE_ALL_ACTION.equals(intent.getAction())) { /* The user has clicked the "Clear All Notifications" * buttons so dismiss all Calendar alerts. Loading @@ -80,6 +91,27 @@ public class AlertReceiver extends BroadcastReceiver { // TODO Grab a wake lock here? Intent serviceIntent = new Intent(context, DismissAlarmsService.class); context.startService(serviceIntent); } else if (MAIL_ACTION.equals(intent.getAction())) { // Close the notification shade. Intent closeNotificationShadeIntent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); context.sendBroadcast(closeNotificationShadeIntent); // Now start the email intent. final long eventId = intent.getLongExtra(EXTRA_EVENT_ID, -1); if (eventId != -1) { final PendingResult result = goAsync(); Runnable worker = new Runnable() { @Override public void run() { Intent emailIntent = createEmailIntent(context, eventId); if (emailIntent != null) { context.startActivity(emailIntent); } result.finish(); } }; sAsyncHandler.post(worker); } } else { Intent i = new Intent(); i.setClass(context, AlertService.class); Loading Loading @@ -218,7 +250,7 @@ public class AlertReceiver extends BroadcastReceiver { resources.getString(R.string.snooze_5min_label), snoozeIntent); // Create an email button. PendingIntent emailIntent = createEmailIntent(context, eventId, title); PendingIntent emailIntent = createBroadcastMailIntent(context, eventId, title); if (emailIntent != null) { notificationBuilder.addAction(R.drawable.ic_menu_email_holo_dark, resources.getString(R.string.email_guests_label), emailIntent); Loading Loading @@ -293,7 +325,7 @@ public class AlertReceiver extends BroadcastReceiver { // expired events. Intent deleteIntent = new Intent(); deleteIntent.setClass(context, DismissAlarmsService.class); deleteIntent.setAction(DELETE_ACTION); deleteIntent.setAction(DELETE_ALL_ACTION); deleteIntent.putExtra(AlertUtils.DELETE_EXPIRED_ONLY_KEY, true); PendingIntent pendingDeleteIntent = PendingIntent.getService(context, 0, deleteIntent, PendingIntent.FLAG_UPDATE_CURRENT); Loading Loading @@ -380,17 +412,78 @@ public class AlertReceiver extends BroadcastReceiver { private static final String[] EVENT_PROJECTION = new String[] { Calendars.OWNER_ACCOUNT, // 0 Calendars.ACCOUNT_NAME // 1 Calendars.ACCOUNT_NAME, // 1 Events.TITLE, // 2 }; private static final int EVENT_INDEX_OWNER_ACCOUNT = 0; private static final int EVENT_INDEX_ACCOUNT_NAME = 1; private static final int EVENT_INDEX_TITLE = 2; private static Cursor getEventCursor(Context context, long eventId) { return context.getContentResolver().query( ContentUris.withAppendedId(Events.CONTENT_URI, eventId), EVENT_PROJECTION, null, null, null); } private static Cursor getAttendeesCursor(Context context, long eventId) { return context.getContentResolver().query(Attendees.CONTENT_URI, ATTENDEES_PROJECTION, ATTENDEES_WHERE, new String[] { Long.toString(eventId) }, ATTENDEES_SORT_ORDER); } /** * Creates a broadcast pending intent that fires to AlertReceiver when the email button * is clicked. */ private static PendingIntent createBroadcastMailIntent(Context context, long eventId, String eventTitle) { // Query for viewer account. String syncAccount = null; Cursor eventCursor = getEventCursor(context, eventId); try { if (eventCursor != null && eventCursor.moveToFirst()) { syncAccount = eventCursor.getString(EVENT_INDEX_ACCOUNT_NAME); } } finally { if (eventCursor != null) { eventCursor.close(); } } // Query attendees to see if there are any to email. Cursor attendeesCursor = getAttendeesCursor(context, eventId); try { if (attendeesCursor != null && attendeesCursor.moveToFirst()) { do { String email = attendeesCursor.getString(ATTENDEES_INDEX_EMAIL); if (Utils.isEmailableFrom(email, syncAccount)) { // Send intent back to ourself first for a couple reasons: // 1) Workaround issue where clicking action button in notification does // not automatically close the notification shade. // 2) Attendees list in email will always be up to date. Intent broadcastIntent = new Intent(MAIL_ACTION); broadcastIntent.setClass(context, AlertReceiver.class); broadcastIntent.putExtra(EXTRA_EVENT_ID, eventId); return PendingIntent.getBroadcast(context, Long.valueOf(eventId).hashCode(), broadcastIntent, PendingIntent.FLAG_CANCEL_CURRENT); } } while (attendeesCursor.moveToNext()); } return null; } finally { if (attendeesCursor != null) { attendeesCursor.close(); } } } /** * Creates an Intent for emailing the attendees of the event. Returns null if there * are no emailable attendees. */ private static PendingIntent createEmailIntent(Context context, long eventId, String eventTitle) { private static Intent createEmailIntent(Context context, long eventId) { ContentResolver resolver = context.getContentResolver(); // TODO: Refactor to move query part into Utils.createEmailAttendeeIntent, to Loading @@ -399,20 +492,29 @@ public class AlertReceiver extends BroadcastReceiver { // Query for the owner account(s). String ownerAccount = null; String syncAccount = null; Cursor eventCursor = resolver.query( ContentUris.withAppendedId(Events.CONTENT_URI, eventId), EVENT_PROJECTION, null, null, null); if (eventCursor.moveToFirst()) { String eventTitle = null; Cursor eventCursor = getEventCursor(context, eventId); try { if (eventCursor != null && eventCursor.moveToFirst()) { ownerAccount = eventCursor.getString(EVENT_INDEX_OWNER_ACCOUNT); syncAccount = eventCursor.getString(EVENT_INDEX_ACCOUNT_NAME); eventTitle = eventCursor.getString(EVENT_INDEX_TITLE); } } finally { if (eventCursor != null) { eventCursor.close(); } } if (TextUtils.isEmpty(eventTitle)) { eventTitle = context.getResources().getString(R.string.no_title_label); } // Query for the attendees. List<String> toEmails = new ArrayList<String>(); List<String> ccEmails = new ArrayList<String>(); Cursor attendeesCursor = resolver.query(Attendees.CONTENT_URI, ATTENDEES_PROJECTION, ATTENDEES_WHERE, new String[] { Long.toString(eventId) }, ATTENDEES_SORT_ORDER); if (attendeesCursor.moveToFirst()) { Cursor attendeesCursor = getAttendeesCursor(context, eventId); try { if (attendeesCursor != null && attendeesCursor.moveToFirst()) { do { int status = attendeesCursor.getInt(ATTENDEES_INDEX_STATUS); String email = attendeesCursor.getString(ATTENDEES_INDEX_EMAIL); Loading @@ -425,6 +527,11 @@ public class AlertReceiver extends BroadcastReceiver { } } while (attendeesCursor.moveToNext()); } } finally { if (attendeesCursor != null) { attendeesCursor.close(); } } Intent intent = null; if (ownerAccount != null && (toEmails.size() > 0 || ccEmails.size() > 0)) { Loading @@ -436,14 +543,13 @@ public class AlertReceiver extends BroadcastReceiver { return null; } else { intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); return PendingIntent.getActivity(context, Long.valueOf(eventId).hashCode(), intent, PendingIntent.FLAG_CANCEL_CURRENT); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); return intent; } } private static void addIfEmailable(List<String> emailList, String email, String syncAccount) { if (Utils.isValidEmail(email) && !email.equals(syncAccount)) { if (Utils.isEmailableFrom(email, syncAccount)) { emailList.add(email); } } Loading