diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 6f29f736..b57ef31d 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -175,6 +175,7 @@ + diff --git a/res/values/strings.xml b/res/values/strings.xml index e0e93276..ff0f565b 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -159,6 +159,10 @@ Synchronize with calendar Choose calendar MobileOrg will use Calendar name + Assimilate calendar entries not inserted by MobileOrg and add them to capture file. EXPERIMENTAL! + Assimilate calendar entries + Delete entries that have been assimilated from the calendar + Delete on assimilation Clear phone calendar Are you sure you want to clear the phones\' calendar? Add reminders for scheduled items to calendar diff --git a/res/xml/preferences.xml b/res/xml/preferences.xml index b4a872eb..37d9b82f 100644 --- a/res/xml/preferences.xml +++ b/res/xml/preferences.xml @@ -120,6 +120,18 @@ android:key="calendarHabits" android:summary="@string/preference_calendar_show_habits_summary" android:title="@string/preference_calendar_show_habits" /> + + 0; + String date = OrgNodeDate.getDate(this.dtStart, this.dtEnd, isAllDay); + String formatedDate = OrgNodeTimeDate.formatDate( + OrgNodeTimeDate.TYPE.Timestamp, date); + + String payload = formatedDate + "\n" + this.description; + + if (TextUtils.isEmpty(this.location) == false) + payload += "\n:LOCATION: " + this.location; + + node.setPayload(payload); + return node; + } +} diff --git a/src/com/matburt/mobileorg/OrgData/OrgFileParser.java b/src/com/matburt/mobileorg/OrgData/OrgFileParser.java index 70cf9808..b03617ee 100644 --- a/src/com/matburt/mobileorg/OrgData/OrgFileParser.java +++ b/src/com/matburt/mobileorg/OrgData/OrgFileParser.java @@ -11,7 +11,6 @@ import android.content.ContentResolver; import android.content.Context; import android.text.TextUtils; -import android.util.Log; import android.util.Pair; import com.matburt.mobileorg.OrgData.OrgContract.OrgData; @@ -223,11 +222,8 @@ public static HashMap getFilesFromIndex(String filecontents) { Pattern indexOrgFilePattern = Pattern.compile(fileMatchPattern); Matcher indexOrgFileMatcher = indexOrgFilePattern.matcher(filecontents); HashMap allOrgFiles = new HashMap(); - - Log.d("MobileOrg", filecontents); - + while (indexOrgFileMatcher.find()) { - Log.d("MobileOrg", "Key: " + indexOrgFileMatcher.group(1) + ":" + indexOrgFileMatcher.group(2)); allOrgFiles.put(indexOrgFileMatcher.group(1), indexOrgFileMatcher.group(2)); } diff --git a/src/com/matburt/mobileorg/OrgData/OrgNodeDate.java b/src/com/matburt/mobileorg/OrgData/OrgNodeDate.java index d2c2cd35..a4337a7d 100644 --- a/src/com/matburt/mobileorg/OrgData/OrgNodeDate.java +++ b/src/com/matburt/mobileorg/OrgData/OrgNodeDate.java @@ -3,6 +3,7 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; +import java.util.Date; import java.util.TimeZone; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -16,6 +17,7 @@ public class OrgNodeDate { public long endTime = 0; public int allDay = 0; public String type = ""; + private String title = ""; private static final SimpleDateFormat dateTimeformatter = new SimpleDateFormat("yyyy-MM-dd HH:mm"); private static final SimpleDateFormat dateformatter = new SimpleDateFormat("yyyy-MM-dd"); @@ -73,6 +75,28 @@ private static long getDayInUTC(long time) { return cal.getTimeInMillis(); } + public static String getDate(long dtStart, long dtEnd, boolean allDay) { + String date; + + if (allDay) + date = dateformatter.format(new Date(dtStart)); + else + date = dateTimeformatter.format(new Date(dtStart)); + + if (dtEnd > 0 && dtStart != dtEnd) { + long timeDiff = dtEnd - dtStart; + + if(timeDiff <= DateUtils.DAY_IN_MILLIS) { + SimpleDateFormat timeformatter = new SimpleDateFormat("HH:mm"); + String endTime = timeformatter.format(new Date(dtEnd)); + + date += "-" + endTime; + } + } + + return date; + } + /** * Whether an event is in the past. True if event ended 24 hours ago or * sometime in the future. @@ -80,4 +104,12 @@ private static long getDayInUTC(long time) { public boolean isInPast() { return System.currentTimeMillis() - DateUtils.DAY_IN_MILLIS >= endTime; } + + public void setTitle(String title) { + this.title = title; + } + + public String getTitle() { + return this.type + this.title; + } } diff --git a/src/com/matburt/mobileorg/OrgData/OrgNodePayload.java b/src/com/matburt/mobileorg/OrgData/OrgNodePayload.java index bd26219e..1ebeb334 100644 --- a/src/com/matburt/mobileorg/OrgData/OrgNodePayload.java +++ b/src/com/matburt/mobileorg/OrgData/OrgNodePayload.java @@ -216,24 +216,27 @@ public String getProperty(String property) { } - public ArrayList getDates() { + public ArrayList getDates(String title) { ArrayList result = new ArrayList(); try { OrgNodeDate scheduledEntry = new OrgNodeDate(getScheduled()); scheduledEntry.type = "SC: "; + scheduledEntry.setTitle(title); result.add(scheduledEntry); } catch (IllegalArgumentException e) {} try { OrgNodeDate deadlineEntry = new OrgNodeDate(getDeadline()); deadlineEntry.type = "DL: "; + deadlineEntry.setTitle(title); result.add(deadlineEntry); } catch (IllegalArgumentException e) {} try { OrgNodeDate timestampEntry = new OrgNodeDate(getTimestamp()); timestampEntry.type = ""; + timestampEntry.setTitle(title); result.add(timestampEntry); } catch (IllegalArgumentException e) {} diff --git a/src/com/matburt/mobileorg/Services/CalendarComptabilityWrappers.java b/src/com/matburt/mobileorg/Services/CalendarComptabilityWrappers.java new file mode 100644 index 00000000..55d02af3 --- /dev/null +++ b/src/com/matburt/mobileorg/Services/CalendarComptabilityWrappers.java @@ -0,0 +1,155 @@ +package com.matburt.mobileorg.Services; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.provider.CalendarContract.CalendarAlerts; +import android.provider.CalendarContract.Calendars; +import android.provider.CalendarContract.Events; +import android.provider.CalendarContract.Reminders; + +public class CalendarComptabilityWrappers { + + public intEvents events = new intEvents(); + public intCalendars calendars = new intCalendars(); + public intReminders reminders = new intReminders(); + public intCalendarAlerts calendarAlerts = new intCalendarAlerts(); + private Context context; + + public String[] eventsProjection; + + public CalendarComptabilityWrappers(Context context) { + this.context = context; + initCalendar(); + + this.eventsProjection = new String[] { events.CALENDAR_ID, + events.DTSTART, events.DTEND, events.DESCRIPTION, events.TITLE, + events.EVENT_LOCATION, events._ID, events.ALL_DAY }; + } + + public class intEvents { + public Uri CONTENT_URI; + public String CALENDAR_ID = "calendar_id"; + public String TITLE = "title"; + public String DESCRIPTION = "description"; + public String EVENT_LOCATION = "eventLocation"; + public String ALL_DAY = "allDay"; + public String DTSTART = "dtstart"; + public String DTEND = "dtend"; + public String HAS_ALARM = "hasAlarm"; + public String ORGANIZER = "organizer"; + public String EVENT_TIMEZONE = "eventTimezone"; + public String _ID = "_id"; + }; + + public class intCalendars { + public Uri CONTENT_URI; + public String _ID = "_id"; + public String CALENDAR_DISPLAY_NAME = "displayName"; + public String ACCOUNT_NAME = "accountName"; + public String VISIBLE = "selected"; + }; + + public class intReminders { + public Uri CONTENT_URI; + public String MINUTES = "minutes"; + public String EVENT_ID = "event_id"; + public String METHOD = "method"; + public int METHOD_ALERT = 1; + }; + + public class intCalendarAlerts { + public Uri CONTENT_URI; + public String EVENT_ID = "event_id"; + public String BEGIN = "begin"; + public String END = "end"; + public String ALERT_TIME = "alarmTime"; + public String MINUTES = "minutes"; + public String STATE = "state"; + public int STATE_SCHEDULED = 0; + }; + + /** + * Hack to support phones with Android <3.0 + */ + @SuppressLint("NewApi") + private void initCalendar() { + try { + calendars.CONTENT_URI = Calendars.CONTENT_URI; + calendars._ID = Calendars._ID; + calendars.ACCOUNT_NAME = Calendars.ACCOUNT_NAME; + calendars.CALENDAR_DISPLAY_NAME = Calendars.CALENDAR_DISPLAY_NAME; + calendars.VISIBLE = Calendars.VISIBLE; + + events.CONTENT_URI = Events.CONTENT_URI; + events.CALENDAR_ID = Events.CALENDAR_ID; + events.TITLE = Events.TITLE; + events.DESCRIPTION = Events.DESCRIPTION; + events.EVENT_LOCATION = Events.EVENT_LOCATION; + events.ORGANIZER = Events.ORGANIZER; + events.ALL_DAY = Events.ALL_DAY; + events.DTEND = Events.DTEND; + events.DTSTART = Events.DTSTART; + events.HAS_ALARM = Events.HAS_ALARM; + events.EVENT_TIMEZONE = Events.EVENT_TIMEZONE; + events._ID = Events._ID; + + reminders.CONTENT_URI = Reminders.CONTENT_URI; + reminders.MINUTES = Reminders.MINUTES; + reminders.EVENT_ID = Reminders.EVENT_ID; + reminders.METHOD_ALERT = Reminders.METHOD_ALERT; + reminders.METHOD = Reminders.METHOD; + + calendarAlerts.CONTENT_URI = CalendarAlerts.CONTENT_URI; + calendarAlerts.STATE_SCHEDULED = CalendarAlerts.STATE_SCHEDULED; + calendarAlerts.EVENT_ID = CalendarAlerts.EVENT_ID; + calendarAlerts.BEGIN = CalendarAlerts.BEGIN; + calendarAlerts.END = CalendarAlerts.END; + calendarAlerts.ALERT_TIME = CalendarAlerts.ALARM_TIME; + calendarAlerts.STATE = CalendarAlerts.STATE; + calendarAlerts.MINUTES = CalendarAlerts.MINUTES; + } catch (NoClassDefFoundError e) { + // The classes referenced above are not available on pre 4.0 phones. + setupBaseUris(); + } + } + + private void setupBaseUris() { + String baseUri = getCalendarUriBase(); + + calendars.CONTENT_URI = Uri.parse(baseUri + "/calendars"); + events.CONTENT_URI = Uri.parse(baseUri + "/events"); + reminders.CONTENT_URI = Uri.parse(baseUri + "/reminders"); + calendarAlerts.CONTENT_URI = Uri.parse(baseUri + "/calendar_alerts"); + } + + /** + * Hack to get the proper calendar uri for pre 4.0 phones. + */ + public String getCalendarUriBase() { + String calendarUriBase = null; + Uri calendars = Uri.parse("content://com.android.calendar/calendars"); + Cursor managedCursor = null; + try { + managedCursor = context.getContentResolver().query(calendars, null, null, null, null); + } catch (Exception e) { + } + if (managedCursor != null) { + calendarUriBase = "content://com.android.calendar"; + managedCursor.close(); + } else { + calendars = Uri.parse("content://calendar/calendars"); + try { + managedCursor = context.getContentResolver().query(calendars, null, null, null, null); + } catch (Exception e) { + } + if (managedCursor != null) { + calendarUriBase = "content://calendar"; + managedCursor.close(); + } + } + + return calendarUriBase; + } +} diff --git a/src/com/matburt/mobileorg/Services/CalendarSyncService.java b/src/com/matburt/mobileorg/Services/CalendarSyncService.java index 176727ba..280e57aa 100644 --- a/src/com/matburt/mobileorg/Services/CalendarSyncService.java +++ b/src/com/matburt/mobileorg/Services/CalendarSyncService.java @@ -3,419 +3,271 @@ import java.util.ArrayList; import java.util.HashSet; -import android.annotation.SuppressLint; +import android.app.Service; import android.content.ContentResolver; -import android.content.ContentUris; -import android.content.ContentValues; import android.content.Context; +import android.content.Intent; import android.content.SharedPreferences; import android.database.Cursor; -import android.database.SQLException; -import android.net.Uri; +import android.os.IBinder; import android.preference.PreferenceManager; -import android.provider.CalendarContract.CalendarAlerts; -import android.provider.CalendarContract.Calendars; -import android.provider.CalendarContract.Events; -import android.provider.CalendarContract.Reminders; -import android.text.format.Time; +import android.util.Log; -import com.matburt.mobileorg.R; +import com.matburt.mobileorg.OrgData.CalendarEntriesParser; +import com.matburt.mobileorg.OrgData.CalendarEntry; import com.matburt.mobileorg.OrgData.OrgFile; import com.matburt.mobileorg.OrgData.OrgNode; import com.matburt.mobileorg.OrgData.OrgNodeDate; import com.matburt.mobileorg.OrgData.OrgProviderUtils; +import com.matburt.mobileorg.util.MultiMap; import com.matburt.mobileorg.util.OrgFileNotFoundException; import com.matburt.mobileorg.util.OrgNodeNotFoundException; +import com.matburt.mobileorg.util.OrgUtils; -public class CalendarSyncService { - private final static String CALENDAR_ORGANIZER = "MobileOrg"; - - private intCalendars intCalendars = new intCalendars(); - private intEvents intEvents = new intEvents(); - private intReminders intReminders = new intReminders(); - private intCalendarAlerts intCalendarAlerts = new intCalendarAlerts(); +public class CalendarSyncService extends Service implements + SharedPreferences.OnSharedPreferenceChangeListener { + public final static String CLEARDB = "clearDB"; + public final static String PULL = "pull"; + public final static String PUSH = "push"; + public final static String FILELIST = "filelist"; private Context context; private SharedPreferences sharedPreferences; - private ContentResolver resolver; - private String calendarName = ""; - private int calendarId = -1; - private Integer reminderTime = 0; - private boolean reminderEnabled = false; + private CalendarWrapper calendarWrapper; + private boolean showDone = false; private boolean showPast = true; private boolean showHabits = false; + private boolean pullEnabled = false; + private boolean pullDelete = false; + private HashSet activeTodos = new HashSet(); private HashSet allTodos = new HashSet(); - - public CalendarSyncService(ContentResolver resolver, Context context) { - this.resolver = resolver; - this.context = context; - this.sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); - initCalendar(); - refreshPreferences(); - } - - private void refreshPreferences() { - this.reminderEnabled = sharedPreferences.getBoolean( - "calendarReminder", false); - - if(reminderEnabled) { - String intervalString = sharedPreferences.getString("calendarReminderInterval", "0"); - if(intervalString == null) - throw new IllegalArgumentException("Invalid calendar reminder interval"); - this.reminderTime = Integer.valueOf(intervalString); - } - - this.showDone = sharedPreferences.getBoolean("calendarShowDone", true); - this.showPast = sharedPreferences.getBoolean("calendarShowPast", true); - this.showHabits = sharedPreferences.getBoolean("calendarHabits", true); - this.calendarName = PreferenceManager - .getDefaultSharedPreferences(context).getString("calendarName", - ""); - this.calendarId = getCalendarID(calendarName); - this.activeTodos = new HashSet(OrgProviderUtils.getActiveTodos(resolver)); - this.allTodos = new HashSet(OrgProviderUtils.getTodos(resolver)); - } - - public void syncFiles() { - refreshPreferences(); - this.deleteAllEntries(context); - - ArrayList files = OrgProviderUtils.getFilenames(resolver); - files.remove(OrgFile.AGENDA_FILE); - for(String filename: files) - insertFileEntries(filename); - } - - public void syncFile(String filename) throws IllegalArgumentException { - refreshPreferences(); - deleteFileEntries(filename, context); - insertFileEntries(filename); - } - - public int deleteAllEntries(Context context) { - refreshPreferences(); - return context.getContentResolver() - .delete(intEvents.CONTENT_URI, intEvents.DESCRIPTION + " LIKE ?", - new String[] { CALENDAR_ORGANIZER + "%" }); + @Override + public IBinder onBind(Intent intent) { + return null; } - public int deleteFileEntries(String filename, Context context) { + @Override + public void onCreate() { + super.onCreate(); + this.resolver = getContentResolver(); + this.context = getBaseContext(); + this.sharedPreferences = PreferenceManager + .getDefaultSharedPreferences(context); + this.sharedPreferences.registerOnSharedPreferenceChangeListener(this); + this.calendarWrapper = new CalendarWrapper(context); refreshPreferences(); - return context.getContentResolver().delete(intEvents.CONTENT_URI, - intEvents.DESCRIPTION + " LIKE ?", - new String[] { CALENDAR_ORGANIZER + ":" + filename + "%" }); } - - public CharSequence[] getCalendars(Context context) { - CharSequence[] result = new CharSequence[1]; - result[0] = context.getString(R.string.error_setting_no_calendar); - - try { - Cursor cursor = context.getContentResolver().query( - intCalendars.CONTENT_URI, - new String[] { intCalendars._ID, - intCalendars.CALENDAR_DISPLAY_NAME }, null, null, - null); - if(cursor == null) - return result; - - if (cursor.getCount() == 0) { - cursor.close(); - return result; - } + @Override + public void onDestroy() { + this.sharedPreferences.unregisterOnSharedPreferenceChangeListener(this); + super.onDestroy(); + } - if (cursor.moveToFirst()) { - result = new CharSequence[cursor.getCount()]; + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + refreshPreferences(); + final String[] fileList = intent.getStringArrayExtra(FILELIST); + final boolean clearDB = intent.getBooleanExtra(CLEARDB, false); + final boolean pull = intent.getBooleanExtra(PULL, false); + final boolean push = intent.getBooleanExtra(PUSH, false); + + new Thread() { + public void run() { + if (clearDB) { + if (fileList != null) + calendarWrapper.deleteFileEntries(fileList); + else + calendarWrapper.deleteEntries(); + } - for (int i = 0; i < cursor.getCount(); i++) { - result[i] = cursor.getString(1); - cursor.moveToNext(); + if (push) { + if (fileList != null) + syncFiles(fileList); + else + syncFiles(); + + if (pullEnabled) + assimilateCalendar(); + } + + if (pull) { + assimilateCalendar(); } } - cursor.close(); - } catch (SQLException e) {} + }.start(); - return result; + return 0; } - - - public void insertNode(long node_id) { - OrgNode node; - try { - node = new OrgNode(node_id, resolver); - } catch (OrgNodeNotFoundException e) { - return; - } - try { - insertNode(node, node.getOrgFile(resolver).filename); - } catch (OrgFileNotFoundException e) { - insertNode(node, ""); + private void syncFiles() { + ArrayList files = OrgProviderUtils.getFilenames(resolver); + files.remove(OrgFile.AGENDA_FILE); + for (String filename : files) + syncFile(filename); + } + + private void syncFiles(String[] files) { + for (String filename : files) { + if (filename.equals(OrgFile.AGENDA_FILE) == false) { + syncFile(filename); + } } } - - private void insertFileEntries(String filename) throws IllegalArgumentException { - Cursor scheduled; - + + + private int inserted = 0; + private int deleted = 0; + private int unchanged = 0; + + private void syncFile(String filename) { + inserted = 0; + deleted = 0; + unchanged = 0; + + Cursor scheduledQuery; try { - scheduled = OrgProviderUtils.getFileSchedule(filename, this.showHabits, - resolver); + scheduledQuery = OrgProviderUtils.getFileSchedule(filename, + this.showHabits, resolver); } catch (OrgFileNotFoundException e) { return; } - while (scheduled.isAfterLast() == false) { + MultiMap entries = getCalendarEntries(filename); + + while (scheduledQuery.isAfterLast() == false) { try { - OrgNode node = new OrgNode(scheduled); - insertNode(node, filename); + OrgNode node = new OrgNode(scheduledQuery); + syncNode(node, entries, filename); } catch (OrgNodeNotFoundException e) {} - scheduled.moveToNext(); + scheduledQuery.moveToNext(); } + scheduledQuery.close(); - scheduled.close(); + removeCalendarEntries(entries); + + Log.d("MobileOrg", "Calendar (" + filename + ") Inserted: " + inserted + + " and deleted: " + deleted + " unchanged: " + unchanged); } - - private void insertNode(OrgNode node, String filename) - throws IllegalArgumentException { - boolean isActive = true; - - if(allTodos.contains(node.todo)) - isActive = this.activeTodos.contains(node.todo); - - String cleanedName = node.getCleanedName(); - - for (OrgNodeDate date : node.getOrgNodePayload().getDates()) { - insertEntry(cleanedName, isActive, node.getCleanedPayload(), - Long.toString(node.id), date, filename, - node.getOrgNodePayload().getProperty("LOCATION")); + + private void syncNode(OrgNode node, MultiMap entries, + String filename) { + for (OrgNodeDate date : node.getOrgNodePayload().getDates( + node.getCleanedName())) { + if (shouldInsertEntry(node.todo, date)) + tryToInsertNode(entries, date, filename, node); } } - // TODO Speed up using bulkInserts - private String insertEntry(String name, boolean isTodoActive, String payload, - String orgID, OrgNodeDate date, String filename, String location) throws IllegalArgumentException { - - if (this.showDone == false && isTodoActive == false) - return null; - - if(this.calendarId == -1) - throw new IllegalArgumentException("Couldn't find selected calendar: " + calendarName); - - if(this.showPast == false && date.isInPast()) - return null; + private void tryToInsertNode(MultiMap entries, + OrgNodeDate date, String filename, OrgNode node) { + CalendarEntry insertedEntry = entries.findValue(date.beginTime, date); - ContentValues values = new ContentValues(); - values.put(intEvents.CALENDAR_ID, this.calendarId); - values.put(intEvents.TITLE, date.type + name); - values.put(intEvents.DESCRIPTION, CALENDAR_ORGANIZER + ":" + filename + "\n" + payload); - values.put(intEvents.EVENT_LOCATION, location); - - // Sync with google will overwrite organizer :( - //values.put(intEvents.ORGANIZER, embeddedNodeMetadata); - - values.put(intEvents.DTSTART, date.beginTime); - values.put(intEvents.DTEND, date.endTime); - values.put(intEvents.ALL_DAY, date.allDay); - values.put(intEvents.HAS_ALARM, 0); - values.put(intEvents.EVENT_TIMEZONE, Time.getCurrentTimezone()); - - Uri uri = context.getContentResolver().insert( - intEvents.CONTENT_URI, values); - String nodeID = uri.getLastPathSegment(); + if (insertedEntry != null) { + entries.remove(date.beginTime, insertedEntry); + unchanged++; + } else { + calendarWrapper.insertEntry(date, node.getCleanedPayload(), filename, node + .getOrgNodePayload().getProperty("LOCATION")); + inserted++; + } + } + + private boolean shouldInsertEntry(String todo, OrgNodeDate date) { + boolean isTodoActive = true; + if (allTodos.contains(todo)) + isTodoActive = this.activeTodos.contains(todo); - if (date.allDay == 0 && this.reminderEnabled) - addReminder(nodeID, date.beginTime, date.endTime); + if (this.showDone == false && isTodoActive == false) + return false; - return nodeID; - } - private void addReminder(String eventID, long beginTime, long endTime) { - if(beginTime < System.currentTimeMillis()) - return; - - ContentValues reminderValues = new ContentValues(); - reminderValues.put(intReminders.MINUTES, this.reminderTime); - reminderValues.put(intReminders.EVENT_ID, eventID); - reminderValues.put(intReminders.METHOD, intReminders.METHOD_ALERT); - context.getContentResolver().insert(intReminders.CONTENT_URI, reminderValues); + if (this.showPast == false && date.isInPast()) + return false; - ContentValues alertvalues = new ContentValues(); - alertvalues.put(intCalendarAlerts.EVENT_ID, eventID ); - alertvalues.put(intCalendarAlerts.BEGIN, beginTime ); - alertvalues.put(intCalendarAlerts.END, endTime ); - alertvalues.put(intCalendarAlerts.ALERT_TIME, this.reminderTime ); - alertvalues.put(intCalendarAlerts.STATE, intCalendarAlerts.STATE_SCHEDULED); - alertvalues.put(intCalendarAlerts.MINUTES, this.reminderTime ); - context.getContentResolver().insert(intCalendarAlerts.CONTENT_URI, - alertvalues); - - ContentValues eventValues = new ContentValues(); - eventValues.put(intEvents.HAS_ALARM, 1); - context.getContentResolver().update( - ContentUris.withAppendedId(intEvents.CONTENT_URI, - Long.valueOf(eventID)), eventValues, null, null); + return true; } - private int getCalendarID(String calendarName) { - Cursor cursor = context.getContentResolver().query( - intCalendars.CONTENT_URI, - new String[] { intCalendars._ID, - intCalendars.CALENDAR_DISPLAY_NAME }, null, - null, null); - if (cursor != null && cursor.moveToFirst()) { - for (int i = 0; i < cursor.getCount(); i++) { - int calId = cursor.getInt(0); - String calName = cursor.getString(1); - - if (calName.equals(calendarName)) { - cursor.close(); - return calId; - } - cursor.moveToNext(); - } - cursor.close(); + private MultiMap getCalendarEntries(String filename) { + refreshPreferences(); + + Cursor query = calendarWrapper.getCalendarCursor(filename); + + MultiMap map = new MultiMap(); + CalendarEntriesParser entriesParser = new CalendarEntriesParser(calendarWrapper.calendar.events, + query); + + while (query.isAfterLast() == false) { + CalendarEntry entry = entriesParser.getEntryFromCursor(query); + map.put(entry.dtStart, entry); + + query.moveToNext(); } - return -1; + + return map; } - @SuppressWarnings("unused") - private int deleteEntry(String nodeCalendarID) { - return context.getContentResolver().delete( - ContentUris.withAppendedId(intEvents.CONTENT_URI, - Long.valueOf(nodeCalendarID)), null, null); + private void removeCalendarEntries(MultiMap entries) { + for (Long entryKey : entries.keySet()) { + for (CalendarEntry entry : entries.get(entryKey)) { + calendarWrapper.deleteEntry(entry); + deleted++; + } + } } -/*** Compatibility with Android 4.0 *****/ - private class intEvents { - public Uri CONTENT_URI; - public String CALENDAR_ID = "calendar_id"; - public String TITLE = "title"; - public String DESCRIPTION = "description"; - public String EVENT_LOCATION = "eventLocation"; - public String ALL_DAY = "allDay"; - public String DTSTART = "dtstart"; - public String DTEND = "dtend"; - public String HAS_ALARM = "hasAlarm"; - @SuppressWarnings("unused") - public String ORGANIZER = "organizer"; - public String EVENT_TIMEZONE = "eventTimezone"; - }; - - private class intCalendars { - public Uri CONTENT_URI; - public String _ID = "_id"; - public String CALENDAR_DISPLAY_NAME = "displayName"; - @SuppressWarnings("unused") - public String ACCOUNT_NAME = "accountName"; - @SuppressWarnings("unused") - public String VISIBLE = "selected"; - }; - - private class intReminders { - public Uri CONTENT_URI; - public String MINUTES = "minutes"; - public String EVENT_ID = "event_id"; - public String METHOD = "method"; - public int METHOD_ALERT = 1; - }; - - private class intCalendarAlerts { - public Uri CONTENT_URI; - public String EVENT_ID = "event_id"; - public String BEGIN = "begin"; - public String END = "end"; - public String ALERT_TIME = "alarmTime"; - public String MINUTES = "minutes"; - public String STATE = "state"; - public int STATE_SCHEDULED = 0; - }; - - /** - * Hack to support phones with Android <3.0 - */ - @SuppressLint("NewApi") - private void initCalendar() { - try { - intCalendars.CONTENT_URI = Calendars.CONTENT_URI; - intCalendars._ID = Calendars._ID; - intCalendars.ACCOUNT_NAME = Calendars.ACCOUNT_NAME; - intCalendars.CALENDAR_DISPLAY_NAME = Calendars.CALENDAR_DISPLAY_NAME; - intCalendars.VISIBLE = Calendars.VISIBLE; - - intEvents.CONTENT_URI = Events.CONTENT_URI; - intEvents.CALENDAR_ID = Events.CALENDAR_ID; - intEvents.TITLE = Events.TITLE; - intEvents.DESCRIPTION = Events.DESCRIPTION; - intEvents.EVENT_LOCATION = Events.EVENT_LOCATION; - intEvents.ORGANIZER = Events.ORGANIZER; - intEvents.ALL_DAY = Events.ALL_DAY; - intEvents.DTEND = Events.DTEND; - intEvents.DTSTART = Events.DTSTART; - intEvents.HAS_ALARM = Events.HAS_ALARM; - intEvents.EVENT_TIMEZONE = Events.EVENT_TIMEZONE; - - intReminders.CONTENT_URI = Reminders.CONTENT_URI; - intReminders.MINUTES = Reminders.MINUTES; - intReminders.EVENT_ID = Reminders.EVENT_ID; - intReminders.METHOD_ALERT = Reminders.METHOD_ALERT; - intReminders.METHOD = Reminders.METHOD; - - intCalendarAlerts.CONTENT_URI = CalendarAlerts.CONTENT_URI; - intCalendarAlerts.STATE_SCHEDULED = CalendarAlerts.STATE_SCHEDULED; - intCalendarAlerts.EVENT_ID = CalendarAlerts.EVENT_ID; - intCalendarAlerts.BEGIN = CalendarAlerts.BEGIN; - intCalendarAlerts.END = CalendarAlerts.END; - intCalendarAlerts.ALERT_TIME = CalendarAlerts.ALARM_TIME; - intCalendarAlerts.STATE = CalendarAlerts.STATE; - intCalendarAlerts.MINUTES = CalendarAlerts.MINUTES; - } catch (NoClassDefFoundError e) { - // The classes referenced above are not available on pre 4.0 phones. - setupBaseUris(); + private void assimilateCalendar() { + Cursor query = calendarWrapper.getUnassimilatedCalendarCursor(); + + CalendarEntriesParser entriesParser = new CalendarEntriesParser( + calendarWrapper.calendar.events, query); + + while(query.isAfterLast() == false) { + CalendarEntry entry = entriesParser.getEntryFromCursor(query); + OrgNode node = entry.convertToOrgNode(); + + OrgFile captureFile = OrgProviderUtils + .getOrCreateCaptureFile(getContentResolver()); + node.fileId = captureFile.id; + node.parentId = captureFile.nodeId; + node.level = 1; + + node.write(getContentResolver()); + + if (this.pullDelete) + calendarWrapper.deleteEntry(entry); + + query.moveToNext(); } - } - - private void setupBaseUris() { - String baseUri = getCalendarUriBase(); - intCalendars.CONTENT_URI = Uri.parse(baseUri + "/calendars"); - intEvents.CONTENT_URI = Uri.parse(baseUri + "/events"); - intReminders.CONTENT_URI = Uri.parse(baseUri + "/reminders"); - intCalendarAlerts.CONTENT_URI = Uri.parse(baseUri + "/calendar_alerts"); + query.close(); + OrgUtils.announceSyncDone(this); } - - /** - * Hack to get the proper calendar uri for pre 4.0 phones. - */ - private String getCalendarUriBase() { - String calendarUriBase = null; - Uri calendars = Uri.parse("content://com.android.calendar/calendars"); - Cursor managedCursor = null; - try { - managedCursor = context.getContentResolver().query(calendars, null, null, null, null); - } catch (Exception e) { - } - if (managedCursor != null) { - calendarUriBase = "content://com.android.calendar"; - managedCursor.close(); - } else { - calendars = Uri.parse("content://calendar/calendars"); - try { - managedCursor = context.getContentResolver().query(calendars, null, null, null, null); - } catch (Exception e) { - } - if (managedCursor != null) { - calendarUriBase = "content://calendar"; - managedCursor.close(); - } + + + private void refreshPreferences() { + this.pullEnabled = sharedPreferences.getBoolean("calendarPull", false); + this.pullDelete = sharedPreferences.getBoolean("calendarPullDelete", false); + this.showDone = sharedPreferences.getBoolean("calendarShowDone", true); + this.showPast = sharedPreferences.getBoolean("calendarShowPast", true); + this.showHabits = sharedPreferences.getBoolean("calendarHabits", true); + this.activeTodos = new HashSet( + OrgProviderUtils.getActiveTodos(resolver)); + this.allTodos = new HashSet(OrgProviderUtils.getTodos(resolver)); + this.calendarWrapper.refreshPreferences(); + } + + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, + String key) { + if (key.startsWith("calendar")) { + syncFiles(); } - - return calendarUriBase; } } diff --git a/src/com/matburt/mobileorg/Services/CalendarWrapper.java b/src/com/matburt/mobileorg/Services/CalendarWrapper.java new file mode 100644 index 00000000..f11ec4e1 --- /dev/null +++ b/src/com/matburt/mobileorg/Services/CalendarWrapper.java @@ -0,0 +1,228 @@ +package com.matburt.mobileorg.Services; + +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.Context; +import android.content.SharedPreferences; +import android.database.Cursor; +import android.database.SQLException; +import android.net.Uri; +import android.preference.PreferenceManager; +import android.text.format.Time; + +import com.matburt.mobileorg.R; +import com.matburt.mobileorg.OrgData.CalendarEntry; +import com.matburt.mobileorg.OrgData.OrgNodeDate; + +public class CalendarWrapper { + + private final static String CALENDAR_ORGANIZER = "MobileOrg"; + + private Context context; + private SharedPreferences sharedPreferences; + + public CalendarComptabilityWrappers calendar; + + private String calendarName = ""; + private int calendarId = -1; + private Integer reminderTime = 0; + private boolean reminderEnabled = false; + + public CalendarWrapper(Context context) { + this.context = context; + this.sharedPreferences = PreferenceManager + .getDefaultSharedPreferences(context); + this.calendar = new CalendarComptabilityWrappers(context); + + } + + public int deleteEntries() { + refreshPreferences(); + return context.getContentResolver().delete(calendar.events.CONTENT_URI, + calendar.events.DESCRIPTION + " LIKE ?", + new String[] { CALENDAR_ORGANIZER + "%" }); + } + + public void deleteFileEntries(String[] files) { + for (String file : files) { + deleteFileEntries(file); + } + } + + public int deleteFileEntries(String filename) { + refreshPreferences(); + return context.getContentResolver().delete(calendar.events.CONTENT_URI, + calendar.events.DESCRIPTION + " LIKE ?", + new String[] { CALENDAR_ORGANIZER + ":" + filename + "%" }); + } + + public String insertEntry(OrgNodeDate date, String payload, + String filename, String location) throws IllegalArgumentException { + + if (this.calendarId == -1) + throw new IllegalArgumentException( + "Couldn't find selected calendar: " + calendarName); + + ContentValues values = new ContentValues(); + values.put(calendar.events.CALENDAR_ID, this.calendarId); + values.put(calendar.events.TITLE, date.getTitle()); + values.put(calendar.events.DESCRIPTION, CALENDAR_ORGANIZER + ":" + + filename + "\n" + payload); + values.put(calendar.events.EVENT_LOCATION, location); + + // Sync with google will overwrite organizer :( + // values.put(intEvents.ORGANIZER, embeddedNodeMetadata); + + values.put(calendar.events.DTSTART, date.beginTime); + values.put(calendar.events.DTEND, date.endTime); + values.put(calendar.events.ALL_DAY, date.allDay); + values.put(calendar.events.HAS_ALARM, 0); + values.put(calendar.events.EVENT_TIMEZONE, Time.getCurrentTimezone()); + + Uri uri = context.getContentResolver().insert( + calendar.events.CONTENT_URI, values); + String nodeID = uri.getLastPathSegment(); + + if (date.allDay == 0 && this.reminderEnabled) + addReminder(nodeID, date.beginTime, date.endTime); + + return nodeID; + } + + + private void addReminder(String eventID, long beginTime, long endTime) { + if (beginTime < System.currentTimeMillis()) + return; + + ContentValues reminderValues = new ContentValues(); + reminderValues.put(calendar.reminders.MINUTES, this.reminderTime); + reminderValues.put(calendar.reminders.EVENT_ID, eventID); + reminderValues.put(calendar.reminders.METHOD, + calendar.reminders.METHOD_ALERT); + context.getContentResolver().insert(calendar.reminders.CONTENT_URI, + reminderValues); + + ContentValues alertvalues = new ContentValues(); + alertvalues.put(calendar.calendarAlerts.EVENT_ID, eventID); + alertvalues.put(calendar.calendarAlerts.BEGIN, beginTime); + alertvalues.put(calendar.calendarAlerts.END, endTime); + alertvalues.put(calendar.calendarAlerts.ALERT_TIME, this.reminderTime); + alertvalues.put(calendar.calendarAlerts.STATE, + calendar.calendarAlerts.STATE_SCHEDULED); + alertvalues.put(calendar.calendarAlerts.MINUTES, this.reminderTime); + context.getContentResolver().insert( + calendar.calendarAlerts.CONTENT_URI, alertvalues); + + ContentValues eventValues = new ContentValues(); + eventValues.put(calendar.events.HAS_ALARM, 1); + context.getContentResolver().update( + ContentUris.withAppendedId(calendar.events.CONTENT_URI, + Long.valueOf(eventID)), eventValues, null, null); + } + + + public int deleteEntry(CalendarEntry entry) { + return context.getContentResolver().delete( + ContentUris.withAppendedId(calendar.events.CONTENT_URI, + entry.id), null, null); + } + + public int getCalendarID(String calendarName) { + Cursor cursor = context.getContentResolver().query( + calendar.calendars.CONTENT_URI, + new String[] { calendar.calendars._ID, + calendar.calendars.CALENDAR_DISPLAY_NAME }, null, null, + null); + if (cursor != null && cursor.moveToFirst()) { + for (int i = 0; i < cursor.getCount(); i++) { + int calId = cursor.getInt(0); + String calName = cursor.getString(1); + + if (calName.equals(calendarName)) { + cursor.close(); + return calId; + } + cursor.moveToNext(); + } + cursor.close(); + } + return -1; + } + + public Cursor getUnassimilatedCalendarCursor() { + Cursor query = context.getContentResolver().query( + calendar.events.CONTENT_URI, + calendar.eventsProjection, + calendar.events.CALENDAR_ID + "=? AND " + + calendar.events.DESCRIPTION + " NOT LIKE ?", + new String[] { Integer.toString(this.calendarId), + CALENDAR_ORGANIZER + "%" }, null); + query.moveToFirst(); + + return query; + } + + public Cursor getCalendarCursor(String filename) { + Cursor query = context.getContentResolver().query( + calendar.events.CONTENT_URI, calendar.eventsProjection, + calendar.events.DESCRIPTION + " LIKE ?", + new String[] { CALENDAR_ORGANIZER + ":" + filename + "%" }, + null); + query.moveToFirst(); + + return query; + } + + public static CharSequence[] getCalendars(Context context) { + CharSequence[] result = new CharSequence[1]; + result[0] = context.getString(R.string.error_setting_no_calendar); + + try { + CalendarComptabilityWrappers calendar = new CalendarComptabilityWrappers( + context); + Cursor cursor = context.getContentResolver().query( + calendar.calendars.CONTENT_URI, + new String[] { calendar.calendars._ID, + calendar.calendars.CALENDAR_DISPLAY_NAME }, null, + null, null); + if (cursor == null) + return result; + + if (cursor.getCount() == 0) { + cursor.close(); + return result; + } + + if (cursor.moveToFirst()) { + result = new CharSequence[cursor.getCount()]; + + for (int i = 0; i < cursor.getCount(); i++) { + result[i] = cursor.getString(1); + cursor.moveToNext(); + } + } + cursor.close(); + } catch (SQLException e) { + } + + return result; + } + + public void refreshPreferences() { + this.reminderEnabled = sharedPreferences.getBoolean("calendarReminder", + false); + + if (reminderEnabled) { + String intervalString = sharedPreferences.getString( + "calendarReminderInterval", "0"); + if (intervalString == null) + throw new IllegalArgumentException( + "Invalid calendar reminder interval"); + this.reminderTime = Integer.valueOf(intervalString); + } + + this.calendarName = PreferenceManager.getDefaultSharedPreferences( + context).getString("calendarName", ""); + this.calendarId = getCalendarID(calendarName); + } +} diff --git a/src/com/matburt/mobileorg/Services/MobileOrgStartupIntentReceiver.java b/src/com/matburt/mobileorg/Services/MobileOrgStartupIntentReceiver.java index 887770d7..8246dfa9 100644 --- a/src/com/matburt/mobileorg/Services/MobileOrgStartupIntentReceiver.java +++ b/src/com/matburt/mobileorg/Services/MobileOrgStartupIntentReceiver.java @@ -21,5 +21,9 @@ public void onReceive(Context context, Intent intent) { if (this.shouldStartService(context)) { SyncService.startAlarm(context); } + + Intent calIntent = new Intent(context, CalendarSyncService.class); + calIntent.putExtra(CalendarSyncService.FILELIST, new String[] {}); + context.startService(calIntent); } } \ No newline at end of file diff --git a/src/com/matburt/mobileorg/Services/SyncService.java b/src/com/matburt/mobileorg/Services/SyncService.java index 07cdd35a..5d3b674a 100644 --- a/src/com/matburt/mobileorg/Services/SyncService.java +++ b/src/com/matburt/mobileorg/Services/SyncService.java @@ -1,5 +1,7 @@ package com.matburt.mobileorg.Services; +import java.util.ArrayList; + import android.app.AlarmManager; import android.app.PendingIntent; import android.app.Service; @@ -50,6 +52,8 @@ public void onCreate() { @Override public void onDestroy() { unsetAlarm(); + this.appSettings.unregisterOnSharedPreferenceChangeListener(this); + super.onDestroy(); } public static void stopAlarm(Context context) { @@ -113,11 +117,14 @@ private void runSynchronizer() { Thread syncThread = new Thread() { public void run() { - synchronizer.runSynchronizer(parser); - + ArrayList changedFiles = synchronizer.runSynchronizer(parser); + String[] files = changedFiles.toArray(new String[changedFiles.size()]); + if(calendarEnabled) { - CalendarSyncService cal = new CalendarSyncService(getContentResolver(), getBaseContext()); - cal.syncFiles(); + Intent calIntent = new Intent(getBaseContext(), CalendarSyncService.class); + calIntent.putExtra(CalendarSyncService.PUSH, true); + calIntent.putExtra(CalendarSyncService.FILELIST, files); + getBaseContext().startService(calIntent); } synchronizer.close(); db.close(); diff --git a/src/com/matburt/mobileorg/Settings/DirectoryBrowser.java b/src/com/matburt/mobileorg/Settings/DirectoryBrowser.java index e1f43204..01117a1f 100644 --- a/src/com/matburt/mobileorg/Settings/DirectoryBrowser.java +++ b/src/com/matburt/mobileorg/Settings/DirectoryBrowser.java @@ -7,18 +7,12 @@ import android.content.Context; import android.util.Log; -import com.matburt.mobileorg.R; -import com.matburt.mobileorg.Synchronizers.UbuntuOneSynchronizer; - import com.dropbox.client2.DropboxAPI; -import com.dropbox.client2.android.AndroidAuthSession; -import com.dropbox.client2.android.AuthActivity; -import com.dropbox.client2.session.AccessTokenPair; -import com.dropbox.client2.session.AppKeyPair; -import com.dropbox.client2.session.Session.AccessType; -import com.dropbox.client2.session.TokenPair; import com.dropbox.client2.DropboxAPI.Entry; +import com.dropbox.client2.android.AndroidAuthSession; import com.dropbox.client2.exception.DropboxException; +import com.matburt.mobileorg.R; +import com.matburt.mobileorg.Synchronizers.UbuntuOneSynchronizer; public interface DirectoryBrowser { diff --git a/src/com/matburt/mobileorg/Settings/SettingsActivity.java b/src/com/matburt/mobileorg/Settings/SettingsActivity.java index 2e24cc24..6b399373 100644 --- a/src/com/matburt/mobileorg/Settings/SettingsActivity.java +++ b/src/com/matburt/mobileorg/Settings/SettingsActivity.java @@ -14,12 +14,12 @@ import android.preference.ListPreference; import android.preference.Preference; import android.preference.PreferenceManager; -import android.util.Log; import com.actionbarsherlock.app.SherlockPreferenceActivity; import com.matburt.mobileorg.R; import com.matburt.mobileorg.OrgData.OrgProviderUtils; import com.matburt.mobileorg.Services.CalendarSyncService; +import com.matburt.mobileorg.Services.CalendarWrapper; import com.matburt.mobileorg.Settings.Synchronizers.SDCardSettingsActivity; import com.matburt.mobileorg.Settings.Synchronizers.ScpSettingsActivity; import com.matburt.mobileorg.Settings.Synchronizers.UbuntuOneSettingsActivity; @@ -37,7 +37,6 @@ public class SettingsActivity extends SherlockPreferenceActivity implements public static final String KEY_CALENDAR_NAME = "calendarName"; public static final String KEY_CALENDAR_REMINDER_INTERVAL = "calendarReminderInterval"; public static final String KEY_DO_AUTO_SYNC = "doAutoSync"; - private boolean updateCalendar = false; @Override protected void onCreate(Bundle savedInstanceState) { @@ -76,18 +75,6 @@ protected void onCreate(Bundle savedInstanceState) { @Override public void onPause() { - if (this.updateCalendar) { - this.updateCalendar = false; - - if (isCalendarEnabled()) { - Log.d("MobileOrg", "onPause(): syncFiles"); - getCalendarSyncService().syncFiles(); - } else { - Log.d("MobileOrg", "onPause(): deleteAllEntries"); - getCalendarSyncService().deleteAllEntries( - getApplicationContext()); - } - } getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this); super.onPause(); } @@ -100,10 +87,6 @@ public void onResume() { @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - if(key.startsWith("calendar")) { - Log.d("MobileOrg", "Set to update calendar"); - this.updateCalendar = true; - } // Set up the initial values following the Settings design guidelines for Ice Cream Sandwich // Settings should show their current value instead of a description setPreferenceSummary(sharedPreferences, key); @@ -137,10 +120,10 @@ public void onClick(DialogInterface dialog, OrgProviderUtils .clearDB(getContentResolver()); OrgUtils.announceSyncDone(getApplicationContext()); - if (isCalendarEnabled()) - getCalendarSyncService() - .deleteAllEntries( - getApplicationContext()); + + Intent clearCalDBIntent = new Intent(getBaseContext(), CalendarSyncService.class); + clearCalDBIntent.putExtra(CalendarSyncService.CLEARDB, true); + startService(clearCalDBIntent); } }).setNegativeButton(R.string.no, null).show(); @@ -148,10 +131,6 @@ public void onClick(DialogInterface dialog, } }; - private CalendarSyncService getCalendarSyncService() { - return new CalendarSyncService(getContentResolver(), this); - } - private boolean isCalendarEnabled() { return getPreferenceManager().getSharedPreferences().getBoolean( "calendarEnabled", false); @@ -160,7 +139,7 @@ private boolean isCalendarEnabled() { private void populateCalendarNames() { ListPreference calendarName = (ListPreference) findPreference("calendarName"); - CharSequence[] calendars = getCalendarSyncService().getCalendars( + CharSequence[] calendars = CalendarWrapper.getCalendars( getApplicationContext()); calendarName.setEntries(calendars); diff --git a/src/com/matburt/mobileorg/Settings/WizardActivity.java b/src/com/matburt/mobileorg/Settings/WizardActivity.java index 207ca2ad..51e067c1 100644 --- a/src/com/matburt/mobileorg/Settings/WizardActivity.java +++ b/src/com/matburt/mobileorg/Settings/WizardActivity.java @@ -32,6 +32,13 @@ import android.widget.TextView; import android.widget.Toast; +import com.dropbox.client2.DropboxAPI; +import com.dropbox.client2.DropboxAPI.Account; +import com.dropbox.client2.android.AndroidAuthSession; +import com.dropbox.client2.exception.DropboxException; +import com.dropbox.client2.session.AccessTokenPair; +import com.dropbox.client2.session.AppKeyPair; +import com.dropbox.client2.session.Session.AccessType; import com.matburt.mobileorg.R; import com.matburt.mobileorg.Synchronizers.SSHSynchronizer; import com.matburt.mobileorg.Synchronizers.UbuntuOneSynchronizer; @@ -39,23 +46,6 @@ import com.matburt.mobileorg.Views.PageFlipView; import com.matburt.mobileorg.util.OrgUtils; -import com.dropbox.client2.DropboxAPI; -import com.dropbox.client2.android.AndroidAuthSession; -import com.dropbox.client2.android.AuthActivity; -import com.dropbox.client2.session.AccessTokenPair; -import com.dropbox.client2.session.AppKeyPair; -import com.dropbox.client2.session.Session.AccessType; -import com.dropbox.client2.session.TokenPair; -import com.dropbox.client2.exception.DropboxException; -import com.dropbox.client2.exception.DropboxFileSizeException; -import com.dropbox.client2.exception.DropboxIOException; -import com.dropbox.client2.exception.DropboxParseException; -import com.dropbox.client2.exception.DropboxPartialFileException; -import com.dropbox.client2.exception.DropboxServerException; -import com.dropbox.client2.exception.DropboxUnlinkedException; -import com.dropbox.client2.DropboxAPI.Entry; -import com.dropbox.client2.DropboxAPI.Account; - public class WizardActivity extends Activity { private final class UIHandler extends Handler diff --git a/src/com/matburt/mobileorg/Synchronizers/DropboxSynchronizer.java b/src/com/matburt/mobileorg/Synchronizers/DropboxSynchronizer.java index 22f60d61..b4532522 100644 --- a/src/com/matburt/mobileorg/Synchronizers/DropboxSynchronizer.java +++ b/src/com/matburt/mobileorg/Synchronizers/DropboxSynchronizer.java @@ -3,38 +3,27 @@ import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; -import java.io.FileInputStream; import android.content.Context; import android.content.SharedPreferences; +import android.os.Handler; import android.preference.PreferenceManager; -import android.widget.Toast; import android.util.Log; -import android.view.View; -import android.os.Handler; - -import com.matburt.mobileorg.R; -import com.matburt.mobileorg.util.FileUtils; +import android.widget.Toast; import com.dropbox.client2.DropboxAPI; +import com.dropbox.client2.DropboxAPI.DropboxInputStream; import com.dropbox.client2.android.AndroidAuthSession; -import com.dropbox.client2.android.AuthActivity; +import com.dropbox.client2.exception.DropboxException; +import com.dropbox.client2.exception.DropboxUnlinkedException; import com.dropbox.client2.session.AccessTokenPair; import com.dropbox.client2.session.AppKeyPair; import com.dropbox.client2.session.Session.AccessType; -import com.dropbox.client2.session.TokenPair; -import com.dropbox.client2.DropboxAPI.DropboxInputStream; -import com.dropbox.client2.DropboxAPI.Entry; - -import com.dropbox.client2.exception.DropboxException; -import com.dropbox.client2.exception.DropboxFileSizeException; -import com.dropbox.client2.exception.DropboxIOException; -import com.dropbox.client2.exception.DropboxParseException; -import com.dropbox.client2.exception.DropboxPartialFileException; -import com.dropbox.client2.exception.DropboxServerException; -import com.dropbox.client2.exception.DropboxUnlinkedException; +import com.matburt.mobileorg.R; +import com.matburt.mobileorg.util.FileUtils; public class DropboxSynchronizer implements SynchronizerInterface { diff --git a/src/com/matburt/mobileorg/Synchronizers/Synchronizer.java b/src/com/matburt/mobileorg/Synchronizers/Synchronizer.java index 62f501ff..0defabe8 100644 --- a/src/com/matburt/mobileorg/Synchronizers/Synchronizer.java +++ b/src/com/matburt/mobileorg/Synchronizers/Synchronizer.java @@ -62,20 +62,25 @@ public boolean isEnabled() { return true; } - public void runSynchronizer(OrgFileParser parser) { + /** + * @return List of files that where changed. + */ + public ArrayList runSynchronizer(OrgFileParser parser) { if (!syncher.isConfigured()) { notify.errorNotification("Sync not configured"); - return; + return new ArrayList(); } try { announceStartSync(); - pull(parser); + ArrayList changedFiles = pull(parser); pushCaptures(); announceSyncDone(); + return changedFiles; } catch (Exception e) { showErrorNotification(e); OrgUtils.announceSyncDone(context); + return new ArrayList(); } } @@ -120,13 +125,14 @@ public void pushCaptures() throws IOException, * This method will download index.org and checksums.dat from the remote * host. Using those files, it determines the other files that need updating * and downloads them. + * @return */ - public void pull(OrgFileParser parser) throws SSLHandshakeException, CertificateException, IOException { + public ArrayList pull(OrgFileParser parser) throws SSLHandshakeException, CertificateException, IOException { HashMap remoteChecksums = getAndParseChecksumFile(); ArrayList changedFiles = getFilesThatChangedRemotely(remoteChecksums); if(changedFiles.size() == 0) - return; + return changedFiles; changedFiles.remove(INDEX_FILE); announceProgressDownload(INDEX_FILE, 0, changedFiles.size() + 2); @@ -136,6 +142,8 @@ public void pull(OrgFileParser parser) throws SSLHandshakeException, Certificate pull(parser, changedFiles, filenameMap, remoteChecksums); announceProgressDownload("", changedFiles.size() + 1, changedFiles.size() + 2); + + return changedFiles; } private void pull(OrgFileParser parser, ArrayList filesToGet, diff --git a/src/com/matburt/mobileorg/util/MultiMap.java b/src/com/matburt/mobileorg/util/MultiMap.java new file mode 100644 index 00000000..8c287127 --- /dev/null +++ b/src/com/matburt/mobileorg/util/MultiMap.java @@ -0,0 +1,51 @@ +package com.matburt.mobileorg.util; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Set; + +public class MultiMap { + + private HashMap> entryMap = new HashMap>(); + + public void put(Long key, T value) { + ArrayList valueList = entryMap.get(key); + + if(valueList == null) { + valueList = new ArrayList(); + entryMap.put(key, valueList); + } + + valueList.add(value); + } + + public ArrayList get(Long key) { + return entryMap.get(key); + } + + public void remove(long key, T value) { + ArrayList valueList = entryMap.get(key); + + if(valueList != null) { + valueList.remove(value); + } + } + + public Set keySet() { + return entryMap.keySet(); + } + + public T findValue(long key, Object object) throws IllegalArgumentException { + ArrayList matches = entryMap.get(key); + + if (matches == null) + return null; + + for (T match : matches) { + if (match.equals(object)) + return match; + } + + return null; + } +} diff --git a/tests/src/com/matburt/mobileorg/test/OrgData/OrgNodeDateTest.java b/tests/src/com/matburt/mobileorg/test/OrgData/OrgNodeDateTest.java new file mode 100644 index 00000000..f83ffee5 --- /dev/null +++ b/tests/src/com/matburt/mobileorg/test/OrgData/OrgNodeDateTest.java @@ -0,0 +1,51 @@ +package com.matburt.mobileorg.test.OrgData; + +import java.util.Calendar; + +import com.matburt.mobileorg.OrgData.OrgNodeDate; + +import android.test.AndroidTestCase; + +public class OrgNodeDateTest extends AndroidTestCase { + + private static final String dateString = "2000-11-24"; + private static final String timeBeginString = "13:15"; + private static final String timeEndString = "15:15"; + private Calendar getDefaultCalendar() { + Calendar cal = Calendar.getInstance(); + cal.set(Calendar.YEAR, 2000); + cal.set(Calendar.MONTH, 10); + cal.set(Calendar.DAY_OF_MONTH, 24); + cal.set(Calendar.HOUR_OF_DAY, 13); + cal.set(Calendar.MINUTE, 15); + return cal; + } + + public void testGetDateDate() { + final long timeInMillis = getDefaultCalendar().getTimeInMillis(); + + String date = OrgNodeDate.getDate(timeInMillis, 0, true); + + assertEquals(dateString, date); + } + + public void testGetDateDateTime() { + final long timeInMillis = getDefaultCalendar().getTimeInMillis(); + + String date = OrgNodeDate.getDate(timeInMillis, timeInMillis, false); + + assertEquals(dateString + " " + timeBeginString, date); + } + + public void testGetDateTimeSpan() { + Calendar cal = getDefaultCalendar(); + final long startTimeInMillis = cal.getTimeInMillis(); + cal.set(Calendar.HOUR_OF_DAY, 15); + final long endTimeInMillis = cal.getTimeInMillis(); + + String date = OrgNodeDate.getDate(startTimeInMillis, endTimeInMillis, false); + + assertEquals(dateString + " " + timeBeginString + "-" + timeEndString, date); + } + +} diff --git a/tests/src/com/matburt/mobileorg/test/Synchronizers/CalendarSyncServiceTest.java b/tests/src/com/matburt/mobileorg/test/Synchronizers/CalendarSyncServiceTest.java index b2459ce8..af9b05a1 100644 --- a/tests/src/com/matburt/mobileorg/test/Synchronizers/CalendarSyncServiceTest.java +++ b/tests/src/com/matburt/mobileorg/test/Synchronizers/CalendarSyncServiceTest.java @@ -12,7 +12,7 @@ public class CalendarSyncServiceTest extends AndroidTestCase { public void testOrgNodePayloadGetDates() { OrgNode node = new OrgNode(); node.setPayload("<2012-09-13 Thu>"); - ArrayList dates = node.getOrgNodePayload().getDates(); + ArrayList dates = node.getOrgNodePayload().getDates(node.getCleanedName()); assertEquals(1, dates.size()); }