summaryrefslogtreecommitdiffstats
path: root/src/com/android/calendar/DeleteEventHelper.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/calendar/DeleteEventHelper.java')
-rw-r--r--src/com/android/calendar/DeleteEventHelper.java306
1 files changed, 306 insertions, 0 deletions
diff --git a/src/com/android/calendar/DeleteEventHelper.java b/src/com/android/calendar/DeleteEventHelper.java
new file mode 100644
index 00000000..7f958b71
--- /dev/null
+++ b/src/com/android/calendar/DeleteEventHelper.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+package com.android.calendar;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.DialogInterface;
+import android.database.Cursor;
+import android.net.Uri;
+import android.pim.EventRecurrence;
+import android.pim.Time;
+import android.provider.Calendar;
+import android.provider.Calendar.Events;
+
+/**
+ * A helper class for deleting events. If a normal event is selected for
+ * deletion, then this pops up a confirmation dialog. If the user confirms,
+ * then the normal event is deleted.
+ *
+ * <p>
+ * If a repeating event is selected for deletion, then this pops up dialog
+ * asking if the user wants to delete just this one instance, or all the
+ * events in the series, or this event plus all following events. The user
+ * may also cancel the delete.
+ * </p>
+ *
+ * <p>
+ * To use this class, create an instance, passing in the parent activity
+ * and a boolean that determines if the parent activity should exit if the
+ * event is deleted. Then to use the instance, call one of the
+ * {@link delete()} methods on this class.
+ *
+ * An instance of this class may be created once and reused (by calling
+ * {@link #delete()} multiple times).
+ */
+public class DeleteEventHelper {
+
+ private final Activity mParent;
+ private final ContentResolver mContentResolver;
+
+ private long mStartMillis;
+ private long mEndMillis;
+ private Cursor mCursor;
+
+ /**
+ * If true, then call finish() on the parent activity when done.
+ */
+ private boolean mExitWhenDone;
+
+ /**
+ * These are the corresponding indices into the array of strings
+ * "R.array.delete_repeating_labels" in the resource file.
+ */
+ static final int DELETE_SELECTED = 0;
+ static final int DELETE_ALL_FOLLOWING = 1;
+ static final int DELETE_ALL = 2;
+
+ private int mWhichDelete;
+
+ private static final String[] EVENT_PROJECTION = new String[] {
+ Events._ID,
+ Events.TITLE,
+ Events.ALL_DAY,
+ Events.CALENDAR_ID,
+ Events.RRULE,
+ Events.DTSTART,
+ Events._SYNC_ID,
+ Events.EVENT_TIMEZONE,
+ };
+
+ private int mEventIndexId;
+ private int mEventIndexRrule;
+
+ public DeleteEventHelper(Activity parent, boolean exitWhenDone) {
+ mParent = parent;
+ mContentResolver = mParent.getContentResolver();
+ mExitWhenDone = exitWhenDone;
+ }
+
+ public void setExitWhenDone(boolean exitWhenDone) {
+ mExitWhenDone = exitWhenDone;
+ }
+
+ /**
+ * This callback is used when a normal event is deleted.
+ */
+ private DialogInterface.OnClickListener mDeleteNormalDialogListener =
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int button) {
+ long id = mCursor.getInt(mEventIndexId);
+ Uri uri = ContentUris.withAppendedId(Calendar.Events.CONTENT_URI, id);
+ mContentResolver.delete(uri, null /* where */, null /* selectionArgs */);
+ if (mExitWhenDone) {
+ mParent.finish();
+ }
+ }
+ };
+
+ /**
+ * This callback is used when a list item for a repeating event is selected
+ */
+ private DialogInterface.OnClickListener mDeleteListListener =
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int button) {
+ mWhichDelete = button;
+ }
+ };
+
+ /**
+ * This callback is used when a repeating event is deleted.
+ */
+ private DialogInterface.OnClickListener mDeleteRepeatingDialogListener =
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int button) {
+ if (mWhichDelete != -1) {
+ deleteRepeatingEvent(mWhichDelete);
+ }
+ }
+ };
+
+ /**
+ * Does the required processing for deleting an event, which includes
+ * first popping up a dialog asking for confirmation (if the event is
+ * a normal event) or a dialog asking which events to delete (if the
+ * event is a repeating event). The "which" parameter is used to check
+ * the initial selection and is only used for repeating events. Set
+ * "which" to -1 to have nothing selected initially.
+ *
+ * @param begin the begin time of the event, in UTC milliseconds
+ * @param end the end time of the event, in UTC milliseconds
+ * @param eventId the event id
+ * @param which one of the values {@link DELETE_SELECTED},
+ * {@link DELETE_ALL_FOLLOWING}, {@link DELETE_ALL}, or -1
+ */
+ public void delete(long begin, long end, long eventId, int which) {
+ Uri uri = ContentUris.withAppendedId(Calendar.Events.CONTENT_URI, eventId);
+ Cursor cursor = mParent.managedQuery(uri, EVENT_PROJECTION, null, null);
+ if (cursor == null) {
+ return;
+ }
+ cursor.moveToFirst();
+ delete(begin, end, cursor, which);
+ }
+
+ /**
+ * Does the required processing for deleting an event. This method
+ * takes a {@link Cursor} object as a parameter, which must point to
+ * a row in the Events table containing the required database fields.
+ * The required fields for a normal event are:
+ *
+ * <ul>
+ * <li> Events._ID </li>
+ * <li> Events.TITLE </li>
+ * <li> Events.RRULE </li>
+ * </ul>
+ *
+ * The required fields for a repeating event include the above plus the
+ * following fields:
+ *
+ * <ul>
+ * <li> Events.ALL_DAY </li>
+ * <li> Events.CALENDAR_ID </li>
+ * <li> Events.DTSTART </li>
+ * <li> Events._SYNC_ID </li>
+ * <li> Events.EVENT_TIMEZONE </li>
+ * </ul>
+ *
+ * @param begin the begin time of the event, in UTC milliseconds
+ * @param end the end time of the event, in UTC milliseconds
+ * @param cursor the database cursor containing the required fields
+ * @param which one of the values {@link DELETE_SELECTED},
+ * {@link DELETE_ALL_FOLLOWING}, {@link DELETE_ALL}, or -1
+ */
+ public void delete(long begin, long end, Cursor cursor, int which) {
+ mWhichDelete = which;
+ mStartMillis = begin;
+ mEndMillis = end;
+ mCursor = cursor;
+ mEventIndexId = mCursor.getColumnIndexOrThrow(Events._ID);
+ mEventIndexRrule = mCursor.getColumnIndexOrThrow(Events.RRULE);
+
+ // If this is a repeating event, then pop up a dialog asking the
+ // user if they want to delete all of the repeating events or
+ // just some of them.
+ String rRule = mCursor.getString(mEventIndexRrule);
+ if (rRule == null) {
+ // This is a normal event. Pop up a confirmation dialog.
+ new AlertDialog.Builder(mParent)
+ .setTitle(R.string.delete_title)
+ .setMessage(R.string.delete_this_event_title)
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setPositiveButton(R.string.ok_label, mDeleteNormalDialogListener)
+ .setNegativeButton(R.string.cancel_label, null)
+ .show();
+ } else {
+ // This is a repeating event. Pop up a dialog asking which events
+ // to delete.
+ new AlertDialog.Builder(mParent)
+ .setTitle(R.string.delete_title)
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setSingleChoiceItems(R.array.delete_repeating_labels, which, mDeleteListListener)
+ .setPositiveButton(R.string.ok_label, mDeleteRepeatingDialogListener)
+ .setNegativeButton(R.string.cancel_label, null)
+ .show();
+ }
+ }
+
+ private void deleteRepeatingEvent(int which) {
+ int indexDtstart = mCursor.getColumnIndexOrThrow(Events.DTSTART);
+ int indexSyncId = mCursor.getColumnIndexOrThrow(Events._SYNC_ID);
+ int indexAllDay = mCursor.getColumnIndexOrThrow(Events.ALL_DAY);
+ int indexTitle = mCursor.getColumnIndexOrThrow(Events.TITLE);
+ int indexTimezone = mCursor.getColumnIndexOrThrow(Events.EVENT_TIMEZONE);
+ int indexCalendarId = mCursor.getColumnIndexOrThrow(Events.CALENDAR_ID);
+
+ String rRule = mCursor.getString(mEventIndexRrule);
+ boolean allDay = mCursor.getInt(indexAllDay) != 0;
+ long dtstart = mCursor.getLong(indexDtstart);
+ long id = mCursor.getInt(mEventIndexId);
+
+ switch (which) {
+ case DELETE_SELECTED:
+ {
+ // Create a recurrence exception by creating a new event
+ // with the status "cancelled".
+ ContentValues values = new ContentValues();
+
+ // The title might not be necessary, but it makes it easier
+ // to find this entry in the database when there is a problem.
+ String title = mCursor.getString(indexTitle);
+ values.put(Events.TITLE, title);
+
+ String syncId = mCursor.getString(indexSyncId);
+ String timezone = mCursor.getString(indexTimezone);
+ int calendarId = mCursor.getInt(indexCalendarId);
+ values.put(Events.EVENT_TIMEZONE, timezone);
+ values.put(Events.CALENDAR_ID, calendarId);
+ values.put(Events.DTSTART, mStartMillis);
+ values.put(Events.DTEND, mEndMillis);
+ values.put(Events.ORIGINAL_EVENT, syncId);
+ values.put(Events.ORIGINAL_INSTANCE_TIME, mStartMillis);
+ values.put(Events.STATUS, Events.STATUS_CANCELED);
+
+ mContentResolver.insert(Events.CONTENT_URI, values);
+ break;
+ }
+ case DELETE_ALL: {
+ Uri uri = ContentUris.withAppendedId(Calendar.Events.CONTENT_URI, id);
+ mContentResolver.delete(uri, null /* where */, null /* selectionArgs */);
+ break;
+ }
+ case DELETE_ALL_FOLLOWING: {
+ // If we are deleting the first event in the series and all
+ // following events, then delete them all.
+ if (dtstart == mStartMillis) {
+ Uri uri = ContentUris.withAppendedId(Calendar.Events.CONTENT_URI, id);
+ mContentResolver.delete(uri, null /* where */, null /* selectionArgs */);
+ break;
+ }
+
+ // Modify the repeating event to end just before this event time
+ EventRecurrence eventRecurrence = new EventRecurrence();
+ eventRecurrence.parse(rRule);
+ Time date = new Time();
+ if (allDay) {
+ date.timezone = Time.TIMEZONE_UTC;
+ }
+ date.set(mStartMillis);
+ date.second--;
+ date.normalize(false);
+
+ // Google calendar seems to require the UNTIL string to be
+ // in UTC.
+ date.switchTimezone(Time.TIMEZONE_UTC);
+ eventRecurrence.until = date.format2445();
+
+ ContentValues values = new ContentValues();
+ values.put(Events.DTSTART, dtstart);
+ values.put(Events.RRULE, eventRecurrence.toString());
+ Uri uri = ContentUris.withAppendedId(Calendar.Events.CONTENT_URI, id);
+ mContentResolver.update(uri, values, null, null);
+ break;
+ }
+ }
+ if (mExitWhenDone) {
+ mParent.finish();
+ }
+ }
+}