/** * Copyright (c) 2012 Todoroo Inc * * See the file "LICENSE" for the full license governing this code. */ package com.todoroo.astrid.gcal; import java.util.TimeZone; import android.content.ContentResolver; import android.content.ContentValues; import android.database.Cursor; import android.database.CursorIndexOutOfBoundsException; import android.net.Uri; import android.text.TextUtils; import android.text.format.Time; import android.util.Log; import com.timsu.astrid.R; import com.todoroo.andlib.service.ContextManager; import com.todoroo.andlib.utility.AndroidUtilities; import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.andlib.utility.Preferences; import com.todoroo.astrid.core.PluginServices; import com.todoroo.astrid.data.Task; public class GCalHelper { /** If task has no estimated time, how early to set a task in calendar (seconds)*/ private static final long DEFAULT_CAL_TIME = DateUtilities.ONE_HOUR; public static final String CALENDAR_ID_COLUMN = "calendar_id"; //$NON-NLS-1$ public static String getTaskEventUri(Task task) { String uri; if (!TextUtils.isEmpty(task.getValue(Task.CALENDAR_URI))) uri = task.getValue(Task.CALENDAR_URI); else { task = PluginServices.getTaskService().fetchById(task.getId(), Task.CALENDAR_URI); if(task == null) return null; uri = task.getValue(Task.CALENDAR_URI); } return uri; } public static void createTaskEventIfEnabled(Task t) { if (!t.hasDueDate()) return; createTaskEventIfEnabled(t, true); } private static void createTaskEventIfEnabled(Task t, boolean deleteEventIfExists) { boolean gcalCreateEventEnabled = Preferences.getStringValue(R.string.gcal_p_default) != null && !Preferences.getStringValue(R.string.gcal_p_default).equals("-1"); //$NON-NLS-1$ if (gcalCreateEventEnabled) { ContentResolver cr = ContextManager.getContext().getContentResolver(); Uri calendarUri = GCalHelper.createTaskEvent(t, cr, new ContentValues(), deleteEventIfExists); if (calendarUri != null) t.setValue(Task.CALENDAR_URI, calendarUri.toString()); } } public static Uri createTaskEvent(Task task, ContentResolver cr, ContentValues values) { return createTaskEvent(task, cr, values, true); } @SuppressWarnings("nls") public static Uri createTaskEvent(Task task, ContentResolver cr, ContentValues values, boolean deleteEventIfExists) { String eventuri = getTaskEventUri(task); if(!TextUtils.isEmpty(eventuri) && deleteEventIfExists) { deleteTaskEvent(task); } try{ Uri uri = Calendars.getCalendarContentUri(Calendars.CALENDAR_CONTENT_EVENTS); values.put("title", task.getValue(Task.TITLE)); values.put("description", task.getValue(Task.NOTES)); values.put("hasAlarm", 0); if (AndroidUtilities.getSdkVersion() < 14) { values.put("transparency", 0); values.put("visibility", 0); } boolean valuesContainCalendarId = (values.containsKey(CALENDAR_ID_COLUMN) && !TextUtils.isEmpty(values.getAsString(CALENDAR_ID_COLUMN))); if (!valuesContainCalendarId) { String calendarId = Calendars.getDefaultCalendar(); if (!TextUtils.isEmpty(calendarId)) { values.put("calendar_id", calendarId); } } createStartAndEndDate(task, values); Uri eventUri = cr.insert(uri, values); cr.notifyChange(eventUri, null); return eventUri; } catch (Exception e) { // won't work on emulator Log.v("astrid-gcal", "error-creating-calendar-event", e); } return null; } public static void rescheduleRepeatingTask(Task task, ContentResolver cr) { String taskUri = getTaskEventUri(task); if (TextUtils.isEmpty(taskUri)) return; Uri eventUri = Uri.parse(taskUri); String calendarId = getCalendarId(eventUri, cr); if (calendarId == null) { // Bail out, no calendar id task.setValue(Task.CALENDAR_URI, ""); //$NON-NLS-1$ return; } ContentValues cv = new ContentValues(); cv.put(CALENDAR_ID_COLUMN, calendarId); Uri uri = createTaskEvent(task, cr, cv, false); task.setValue(Task.CALENDAR_URI, uri.toString()); } private static String getCalendarId(Uri uri, ContentResolver cr) { Cursor calendar = cr.query(uri, new String[] { CALENDAR_ID_COLUMN }, null, null, null); try { calendar.moveToFirst(); return calendar.getString(0); } catch (CursorIndexOutOfBoundsException e) { return null; } finally { calendar.close(); } } @SuppressWarnings("nls") public static boolean deleteTaskEvent(Task task) { boolean eventDeleted = false; String uri; if(task.containsNonNullValue(Task.CALENDAR_URI)) uri = task.getValue(Task.CALENDAR_URI); else { task = PluginServices.getTaskService().fetchById(task.getId(), Task.CALENDAR_URI); if(task == null) return false; uri = task.getValue(Task.CALENDAR_URI); } if(!TextUtils.isEmpty(uri)) { try { Uri calendarUri = Uri.parse(uri); // try to load calendar ContentResolver cr = ContextManager.getContext().getContentResolver(); Cursor cursor = cr.query(calendarUri, new String[] { "dtstart" }, null, null, null); //$NON-NLS-1$ try { boolean alreadydeleted = cursor.getCount() == 0; if (!alreadydeleted) { cr.delete(calendarUri, null, null); eventDeleted = true; } } finally { cursor.close(); } task.setValue(Task.CALENDAR_URI,""); } catch (Exception e) { Log.e("astrid-gcal", "error-deleting-calendar-event", e); //$NON-NLS-1$ //$NON-NLS-2$ } } return eventDeleted; } @SuppressWarnings("nls") static void createStartAndEndDate(Task task, ContentValues values) { long dueDate = task.getValue(Task.DUE_DATE); long tzCorrectedDueDate = dueDate + TimeZone.getDefault().getOffset(dueDate); long tzCorrectedDueDateNow = DateUtilities.now() + TimeZone.getDefault().getOffset(DateUtilities.now()); // FIXME: doesnt respect timezones, see story 17443653 if(task.hasDueDate()) { if(task.hasDueTime()) { long estimatedTime = task.getValue(Task.ESTIMATED_SECONDS) * 1000; if(estimatedTime <= 0) estimatedTime = DEFAULT_CAL_TIME; if (Preferences.getBoolean(R.string.p_end_at_deadline, true)) { values.put("dtstart", dueDate); values.put("dtend", dueDate + estimatedTime); }else{ values.put("dtstart", dueDate - estimatedTime); values.put("dtend", dueDate); } // setting a duetime to a previously timeless event requires explicitly setting allDay=0 values.put("allDay", "0"); values.put("eventTimezone", TimeZone.getDefault().getID()); } else { values.put("dtstart", tzCorrectedDueDate); values.put("dtend", tzCorrectedDueDate); values.put("allDay", "1"); } } else { values.put("dtstart", tzCorrectedDueDateNow); values.put("dtend", tzCorrectedDueDateNow); values.put("allDay", "1"); } adjustDateForIcs(values); } @SuppressWarnings("nls") private static void adjustDateForIcs(ContentValues values) { if (AndroidUtilities.getSdkVersion() >= 14) { if ("1".equals(values.get("allDay"))) { values.put("eventTimezone", Time.TIMEZONE_UTC); } else { values.put("eventTimezone", TimeZone.getDefault().getID()); } } } }