diff --git a/RememBirthday-UI/src/main/java/com/kunzisoft/remembirthday/element/CalendarEvent.java b/RememBirthday-UI/src/main/java/com/kunzisoft/remembirthday/element/CalendarEvent.java index 35383bc..79a6167 100644 --- a/RememBirthday-UI/src/main/java/com/kunzisoft/remembirthday/element/CalendarEvent.java +++ b/RememBirthday-UI/src/main/java/com/kunzisoft/remembirthday/element/CalendarEvent.java @@ -19,6 +19,8 @@ public class CalendarEvent implements Parcelable { + public static final long ID_UNDEFINED = -1; + private long id; private Date dateStart; private Date dateStop; @@ -27,17 +29,38 @@ public class CalendarEvent implements Parcelable { private List reminders; - public CalendarEvent(Date date) { - this(date, date); + public CalendarEvent(String title, Date date) { + this(title, date, date); + } + + public CalendarEvent(String title, Date date, boolean allDay) { + this(title, date, date); + setAllDay(allDay); } - public CalendarEvent(Date dateStart, Date dateStop) { - this.id = -1; + public CalendarEvent(String title, Date dateStart, Date dateStop) { + this.id = ID_UNDEFINED; this.dateStart = dateStart; this.dateStop = dateStop; this.allDay = false; - this.title = ""; + this.title = title; + this.reminders = new ArrayList<>(); + } + + /** + * Create a copy of event + * @param another Base event + */ + public CalendarEvent(CalendarEvent another) { + this.id = another.id; + this.dateStart = another.dateStart; + this.dateStop = another.dateStop; + setAllDay(another.allDay); + this.title = another.title; this.reminders = new ArrayList<>(); + for(Reminder reminder : another.reminders) { + reminders.add(new Reminder(reminder)); + } } private CalendarEvent(Parcel in) { @@ -49,6 +72,10 @@ private CalendarEvent(Parcel in) { reminders = in.readArrayList(Reminder.class.getClassLoader()); } + public boolean hasId() { + return id != ID_UNDEFINED; + } + public Long getId() { return id; } diff --git a/RememBirthday-UI/src/main/java/com/kunzisoft/remembirthday/element/Contact.java b/RememBirthday-UI/src/main/java/com/kunzisoft/remembirthday/element/Contact.java index b908b43..4474c65 100644 --- a/RememBirthday-UI/src/main/java/com/kunzisoft/remembirthday/element/Contact.java +++ b/RememBirthday-UI/src/main/java/com/kunzisoft/remembirthday/element/Contact.java @@ -28,7 +28,7 @@ public class Contact implements Parcelable{ private Uri imageThumbnailUri; private Uri imageUri; private DateUnknownYear birthday; - private CalendarEvent birthdayEvent; + private EventWithoutYear birthdayEvent; private List phoneNumbers; public Contact(long id, String lookupKey, String name) { @@ -139,17 +139,13 @@ public DateUnknownYear getBirthday() { public void setBirthday(DateUnknownYear date) { this.birthday = date; - if(date != null) { - setBirthdayEvent(new CalendarEvent(date.getDate())); - } else - setBirthdayEvent(null); } - public CalendarEvent getBirthdayEvent() { + public EventWithoutYear getBirthdayEvent() { return birthdayEvent; } - private void setBirthdayEvent(CalendarEvent birthdayEvent) { + public void setBirthdayEvent(EventWithoutYear birthdayEvent) { this.birthdayEvent = birthdayEvent; } diff --git a/RememBirthday-UI/src/main/java/com/kunzisoft/remembirthday/element/EventWithoutYear.java b/RememBirthday-UI/src/main/java/com/kunzisoft/remembirthday/element/EventWithoutYear.java new file mode 100644 index 0000000..019e78c --- /dev/null +++ b/RememBirthday-UI/src/main/java/com/kunzisoft/remembirthday/element/EventWithoutYear.java @@ -0,0 +1,50 @@ +package com.kunzisoft.remembirthday.element; + +import android.util.Log; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +/** + * Created by joker on 28/07/17. + */ + +public class EventWithoutYear { + + + private static final String TAG = "EventWithoutYear"; + + private List calendarEvents; + + /** + * Manage calendar between X years and Y Years of current year + */ + private static final int X_YEAR = 3; + private static final int Y_YEAR = 5; + + public EventWithoutYear(CalendarEvent baseEvent) { + + calendarEvents = new ArrayList<>(); + + Calendar currCal = Calendar.getInstance(); + int currYear = currCal.get(Calendar.YEAR); + int startYear = currYear - X_YEAR; + int endYear = currYear + Y_YEAR; + + for (int iteratedYear = startYear; iteratedYear <= endYear; iteratedYear++) { + CalendarEvent calendarEvent = new CalendarEvent(baseEvent); + calendarEvent.setYear(iteratedYear); + calendarEvent.setAllDay(true); + calendarEvents.add(calendarEvent); + } + } + + /** + * Get events for the past X years and the next Y years + */ + public List getEventsAroundThisYear() { + return calendarEvents; + } +} diff --git a/RememBirthday-UI/src/main/java/com/kunzisoft/remembirthday/element/Reminder.java b/RememBirthday-UI/src/main/java/com/kunzisoft/remembirthday/element/Reminder.java index 26a1ec5..e386b67 100644 --- a/RememBirthday-UI/src/main/java/com/kunzisoft/remembirthday/element/Reminder.java +++ b/RememBirthday-UI/src/main/java/com/kunzisoft/remembirthday/element/Reminder.java @@ -27,6 +27,7 @@ public Reminder(Date dateEvent, int hourOfDay, int minuteOfHour, int deltaDay) { this.dateEvent = new DateTime(this.dateEvent) .withHourOfDay(0) .withMinuteOfHour(0) + .withSecondOfMinute(0) .toDate(); this.hourOfDay = hourOfDay; this.minuteOfHour = minuteOfHour; @@ -40,6 +41,23 @@ public Reminder(Date anniversary, int hourOfDay, int minuteOfHour) { this(anniversary, hourOfDay, minuteOfHour, 0); } + /** + * Create a copy of Reminder + * @param another + */ + public Reminder(Reminder another) { + this.id = ID_UNDEFINED; + this.dateEvent = another.dateEvent; + this.dateEvent = another.dateEvent; + this.hourOfDay = another.hourOfDay; + this.minuteOfHour = another.minuteOfHour; + this.daysBefore = another.daysBefore; + } + + public boolean hasId() { + return id != ID_UNDEFINED; + } + public long getId() { return id; } @@ -83,4 +101,12 @@ public int getMinuteOfHour() { public void setMinuteOfHour(int minute) { this.minuteOfHour = minute; } + + @Override + public String toString() { + return "Reminder{" + + "id=" + id + + ", date=" + getDate() + + '}'; + } } diff --git a/RememBirthday-UI/src/main/java/com/kunzisoft/remembirthday/provider/CalendarProvider.java b/RememBirthday-UI/src/main/java/com/kunzisoft/remembirthday/provider/CalendarProvider.java index dc0bdb7..6b8a701 100644 --- a/RememBirthday-UI/src/main/java/com/kunzisoft/remembirthday/provider/CalendarProvider.java +++ b/RememBirthday-UI/src/main/java/com/kunzisoft/remembirthday/provider/CalendarProvider.java @@ -125,12 +125,12 @@ public static long getCalendar(Context context) { } } - public static void cleanTables(Context context, ContentResolver contentResolver, long calendarId) { + public static void cleanTables(Context context, long calendarId) { // empty table // with additional selection of calendar id, necessary on Android < 4 to remove events only // from birthday calendar - int delEventsRows = contentResolver.delete(getBirthdayAdapterUri(context, CalendarContract.Events.CONTENT_URI), + int delEventsRows = context.getContentResolver().delete(getBirthdayAdapterUri(context, CalendarContract.Events.CONTENT_URI), CalendarContract.Events.CALENDAR_ID + " = ?", new String[]{String.valueOf(calendarId)}); Log.i(TAG, "Events of birthday calendar is now empty, deleted " + delEventsRows + " rows!"); diff --git a/RememBirthday-UI/src/main/java/com/kunzisoft/remembirthday/provider/ContactProvider.java b/RememBirthday-UI/src/main/java/com/kunzisoft/remembirthday/provider/ContactProvider.java new file mode 100644 index 0000000..03eecff --- /dev/null +++ b/RememBirthday-UI/src/main/java/com/kunzisoft/remembirthday/provider/ContactProvider.java @@ -0,0 +1,231 @@ +package com.kunzisoft.remembirthday.provider; + +import android.accounts.Account; +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.Context; +import android.database.Cursor; +import android.database.MatrixCursor; +import android.net.Uri; +import android.provider.BaseColumns; +import android.provider.ContactsContract; +import android.text.TextUtils; +import android.util.Log; + +import com.kunzisoft.remembirthday.element.CalendarEvent; +import com.kunzisoft.remembirthday.element.Contact; +import com.kunzisoft.remembirthday.element.DateUnknownYear; +import com.kunzisoft.remembirthday.element.EventWithoutYear; +import com.kunzisoft.remembirthday.element.Reminder; +import com.kunzisoft.remembirthday.preference.PreferencesManager; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +/** + * Created by joker on 28/07/17. + */ + +public class ContactProvider { + + private static final String TAG = "ContactProvider"; + + /** + * Get Cursor of contacts with events, but only those from Accounts not in our blacklist! + *

+ * This is really complicated, because we can't query SQLite directly. We need to use the provided Content Provider + * and query several times for different tables. + * + * @return Cursor over all contacts with events, where accounts are not blacklisted + */ + public static List getAllContacts(Context context) { + + ContentResolver contentResolver = context.getContentResolver(); + + // 0. get blacklist of Account names from own provider + HashSet blacklist = //TODO blacklist ProviderHelper.getAccountBlacklist(context); + new HashSet<>(); + + // HashSet of already added events using generated identifiers to check for duplicates before adding + HashSet addedEventsIdentifiers = new HashSet<>(); + + /* + * 1. Get all raw contacts with their corresponding Account name and type (only raw contacts get get Account + * affiliation + */ + Uri rawContactsUri = ContactsContract.RawContacts.CONTENT_URI; + String[] rawContactsProjection = new String[]{ + ContactsContract.RawContacts._ID, + ContactsContract.RawContacts.CONTACT_ID, + ContactsContract.RawContacts.DISPLAY_NAME_PRIMARY, + ContactsContract.RawContacts.ACCOUNT_NAME, + ContactsContract.RawContacts.ACCOUNT_TYPE,}; + Cursor rawContacts = contentResolver.query(rawContactsUri, rawContactsProjection, null, null, null); + + /* + * 2. Go over all raw contacts and check if the Account is allowed. + * If Account is allowed, get display name and lookup key and all events for this contact. + * Build a new MatrixCursor out of this data that can be used. + */ + String[] columns = new String[]{ + BaseColumns._ID, + ContactsContract.Data.DISPLAY_NAME, + ContactsContract.Data.LOOKUP_KEY, + ContactsContract.CommonDataKinds.Event.START_DATE, + ContactsContract.CommonDataKinds.Event.TYPE, + ContactsContract.CommonDataKinds.Event.LABEL + }; + MatrixCursor matrixCursor = new MatrixCursor(columns); + int mcIndex = 0; + try { + while (rawContacts != null && rawContacts.moveToNext()) { + long rawId = rawContacts.getLong(rawContacts.getColumnIndex(ContactsContract.RawContacts._ID)); + String accType = rawContacts.getString(rawContacts.getColumnIndex(ContactsContract.RawContacts.ACCOUNT_TYPE)); + String accName = rawContacts.getString(rawContacts.getColumnIndex(ContactsContract.RawContacts.ACCOUNT_NAME)); + + /* + * 2a. Check if Account is allowed (not blacklisted) + */ + boolean addEvent = false; + if (TextUtils.isEmpty(accType) || TextUtils.isEmpty(accName)) { + // Workaround: Simply add events without proper Account + addEvent = true; + } else { + Account acc = new Account(accName, accType); + + if (!blacklist.contains(acc)) { + addEvent = true; + } + } + + if (addEvent) { + String displayName = null; + String lookupKey = null; + String startDate; + int type; + String label; + + /* + * 2b. Get display name and lookup key from normal contact table + */ + String[] displayProjection = new String[]{ + ContactsContract.Data.RAW_CONTACT_ID, + ContactsContract.Data.DISPLAY_NAME, + ContactsContract.Data.LOOKUP_KEY, + }; + String displayWhere = ContactsContract.Data.RAW_CONTACT_ID + "= ?"; + String[] displaySelectionArgs = new String[]{ + String.valueOf(rawId) + }; + Cursor displayCursor = contentResolver.query(ContactsContract.Data.CONTENT_URI, displayProjection, + displayWhere, displaySelectionArgs, null); + try { + if (displayCursor != null && displayCursor.moveToFirst()) { + displayName = displayCursor.getString(displayCursor.getColumnIndex(ContactsContract.Data.DISPLAY_NAME)); + lookupKey = displayCursor.getString(displayCursor.getColumnIndex(ContactsContract.Data.LOOKUP_KEY)); + } + } finally { + if (displayCursor != null && !displayCursor.isClosed()) + displayCursor.close(); + } + + /* + * 2c. Get all events for this raw contact. + * We don't get this information for the (merged) contact table, but from the raw contact. + * If we would query this infos from the contact table we would also get events that should have been filtered! + */ + Uri thisRawContactUri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI, rawId); + Uri entityUri = Uri.withAppendedPath(thisRawContactUri, ContactsContract.RawContacts.Entity.CONTENT_DIRECTORY); + String[] eventsProjection = new String[]{ + ContactsContract.RawContacts._ID, + ContactsContract.RawContacts.Entity.DATA_ID, + ContactsContract.CommonDataKinds.Event.START_DATE, + ContactsContract.CommonDataKinds.Event.TYPE, + ContactsContract.CommonDataKinds.Event.LABEL + }; + String eventsWhere = ContactsContract.RawContacts.Entity.MIMETYPE + "= ? AND " + + ContactsContract.RawContacts.Entity.DATA_ID + " IS NOT NULL"; + String[] eventsSelectionArgs = new String[]{ + ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE + }; + Cursor eventsCursor = contentResolver.query(entityUri, eventsProjection, eventsWhere, + eventsSelectionArgs, null); + try { + while (eventsCursor != null && eventsCursor.moveToNext()) { + startDate = eventsCursor.getString(eventsCursor.getColumnIndex(ContactsContract.CommonDataKinds.Event.START_DATE)); + type = eventsCursor.getInt(eventsCursor.getColumnIndex(ContactsContract.CommonDataKinds.Event.TYPE)); + label = eventsCursor.getString(eventsCursor.getColumnIndex(ContactsContract.CommonDataKinds.Event.LABEL)); + + /* + * 2d. Add this information to our MatrixCursor if not already added previously. + * + * If two SyncAdapter Accounts have the same contact with duplicated events, the event will already be in + * the HashSet addedEventsIdentifiers. + * + * eventIdentifier does not include startDate, because the String formats of startDate differ between accounts. + */ + String eventIdentifier = lookupKey + type + label; + if (addedEventsIdentifiers.contains(eventIdentifier)) { + Log.d(TAG, "Event was NOT added, duplicate! Identifier: " + eventIdentifier); + } else { + Log.d(TAG, "Event was added! Identifier " + eventIdentifier); + addedEventsIdentifiers.add(eventIdentifier); + + matrixCursor.newRow().add(mcIndex).add(displayName).add(lookupKey).add(startDate).add(type).add(label); + mcIndex++; + } + } + } finally { + if (eventsCursor != null && !eventsCursor.isClosed()) + eventsCursor.close(); + } + } + } + } finally { + if (rawContacts != null && !rawContacts.isClosed()) + rawContacts.close(); + } + + List contactList = new ArrayList<>(); + try { + int eventDateColumn = matrixCursor + .getColumnIndex(ContactsContract.CommonDataKinds.Event.START_DATE); + int displayNameColumn = matrixCursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME); + int eventTypeColumn = matrixCursor + .getColumnIndex(ContactsContract.CommonDataKinds.Event.TYPE); + int eventCustomLabelColumn = matrixCursor + .getColumnIndex(ContactsContract.CommonDataKinds.Event.LABEL); + int eventLookupKeyColumn = matrixCursor + .getColumnIndex(ContactsContract.CommonDataKinds.Event.LOOKUP_KEY); + + // for every event... + while (matrixCursor.moveToNext()) { + Contact contact = new Contact(matrixCursor.getString(displayNameColumn)); + contact.setBirthday(DateUnknownYear.stringToDate(matrixCursor.getString(eventDateColumn))); + + if(contact.getBirthday() != null) { + + CalendarEvent calendarEvent = new CalendarEvent( + contact.getName(), + contact.getBirthday().getDate(), + true); + int[] defaultTime = PreferencesManager.getDefaultTime(context); + calendarEvent.addReminder( + new Reminder(calendarEvent.getDate(), defaultTime[0], defaultTime[1])); + + contact.setBirthdayEvent(new EventWithoutYear(calendarEvent)); + } + //contact.setType(matrixCursor.getInt(eventTypeColumn)); + //contact.setLabel(matrixCursor.getString(eventCustomLabelColumn)); + contact.setLookUpKey(matrixCursor.getString(eventLookupKeyColumn)); + + contactList.add(contact); + } + } finally { + if (!matrixCursor.isClosed()) + matrixCursor.close(); + } + return contactList; + } +} diff --git a/RememBirthday-UI/src/main/java/com/kunzisoft/remembirthday/provider/EventProvider.java b/RememBirthday-UI/src/main/java/com/kunzisoft/remembirthday/provider/EventProvider.java index 9212a52..1f19515 100644 --- a/RememBirthday-UI/src/main/java/com/kunzisoft/remembirthday/provider/EventProvider.java +++ b/RememBirthday-UI/src/main/java/com/kunzisoft/remembirthday/provider/EventProvider.java @@ -33,16 +33,11 @@ public class EventProvider { private static final String TAG = "EventProvider"; /** - * Get a new ContentProviderOperation to insert an event + * Utility method for add values in Builder + * @param builder ContentProviderOperation.Builder + * @param event Event to add */ - public static ContentProviderOperation insert(Context context, long calendarId, - CalendarEvent event, @Nullable Contact contact) { - ContentProviderOperation.Builder builder; - - builder = ContentProviderOperation.newInsert(CalendarProvider.getBirthdayAdapterUri(context, CalendarContract.Events.CONTENT_URI)); - - - builder.withValue(CalendarContract.Events.CALENDAR_ID, calendarId); + private static void assignValuesInBuilder(ContentProviderOperation.Builder builder, CalendarEvent event) { builder.withValue(CalendarContract.Events.DTSTART, event.getDateStart().getTime()); builder.withValue(CalendarContract.Events.DTEND, event.getDateStop().getTime()); if(event.isAllDay()) { @@ -51,18 +46,29 @@ public static ContentProviderOperation insert(Context context, long calendarId, builder.withValue(CalendarContract.Events.ALL_DAY, 1); } builder.withValue(CalendarContract.Events.TITLE, event.getTitle()); + } + + /** + * Get a new ContentProviderOperation to insert an event + */ + public static ContentProviderOperation insert(Context context, long calendarId, + CalendarEvent event, @Nullable Contact contact) { + ContentProviderOperation.Builder builder; + + builder = ContentProviderOperation.newInsert(CalendarProvider.getBirthdayAdapterUri(context, CalendarContract.Events.CONTENT_URI)); + builder.withValue(CalendarContract.Events.CALENDAR_ID, calendarId); + assignValuesInBuilder(builder, event); + builder.withValue(CalendarContract.Events.STATUS, CalendarContract.Events.STATUS_CONFIRMED); /* * Enable reminders for this event - * * Note: Needs to be explicitly set on Android < 4 to enable reminders */ builder.withValue(CalendarContract.Events.HAS_ALARM, 1); /* * Set availability to free. - * * Note: HTC calendar (4.0.3 Android + HTC Sense 4.0) will show a conflict with other events * if availability is not set to free! */ @@ -78,189 +84,50 @@ public static ContentProviderOperation insert(Context context, long calendarId, builder.withValue(CalendarContract.Events.CUSTOM_APP_URI, contactLookupUri.toString()); } + Log.d(TAG, "Add event : " + event); return builder.build(); } /** - * Get Cursor of contacts with events, but only those from Accounts not in our blacklist! - *

- * This is really complicated, because we can't query SQLite directly. We need to use the provided Content Provider - * and query several times for different tables. - * - * @return Cursor over all contacts with events, where accounts are not blacklisted + * Update the specific event, id must be specified + * @param event Event to update + * @return ContentProviderOperation to apply or null if no id */ - public static List getContacts(Context context, ContentResolver contentResolver) { - // 0. get blacklist of Account names from own provider - HashSet blacklist = //TODO blacklist ProviderHelper.getAccountBlacklist(context); - new HashSet<>(); - - // HashSet of already added events using generated identifiers to check for duplicates before adding - HashSet addedEventsIdentifiers = new HashSet<>(); - - /* - * 1. Get all raw contacts with their corresponding Account name and type (only raw contacts get get Account - * affiliation - */ - Uri rawContactsUri = ContactsContract.RawContacts.CONTENT_URI; - String[] rawContactsProjection = new String[]{ - ContactsContract.RawContacts._ID, - ContactsContract.RawContacts.CONTACT_ID, - ContactsContract.RawContacts.DISPLAY_NAME_PRIMARY, - ContactsContract.RawContacts.ACCOUNT_NAME, - ContactsContract.RawContacts.ACCOUNT_TYPE,}; - Cursor rawContacts = contentResolver.query(rawContactsUri, rawContactsProjection, null, null, null); - - /* - * 2. Go over all raw contacts and check if the Account is allowed. - * If Account is allowed, get display name and lookup key and all events for this contact. - * Build a new MatrixCursor out of this data that can be used. - */ - String[] columns = new String[]{ - BaseColumns._ID, - ContactsContract.Data.DISPLAY_NAME, - ContactsContract.Data.LOOKUP_KEY, - ContactsContract.CommonDataKinds.Event.START_DATE, - ContactsContract.CommonDataKinds.Event.TYPE, - ContactsContract.CommonDataKinds.Event.LABEL - }; - MatrixCursor matrixCursor = new MatrixCursor(columns); - int mcIndex = 0; - try { - while (rawContacts != null && rawContacts.moveToNext()) { - long rawId = rawContacts.getLong(rawContacts.getColumnIndex(ContactsContract.RawContacts._ID)); - String accType = rawContacts.getString(rawContacts.getColumnIndex(ContactsContract.RawContacts.ACCOUNT_TYPE)); - String accName = rawContacts.getString(rawContacts.getColumnIndex(ContactsContract.RawContacts.ACCOUNT_NAME)); - - /* - * 2a. Check if Account is allowed (not blacklisted) - */ - boolean addEvent = false; - if (TextUtils.isEmpty(accType) || TextUtils.isEmpty(accName)) { - // Workaround: Simply add events without proper Account - addEvent = true; - } else { - Account acc = new Account(accName, accType); - - if (!blacklist.contains(acc)) { - addEvent = true; - } - } - - if (addEvent) { - String displayName = null; - String lookupKey = null; - String startDate; - int type; - String label; - - /* - * 2b. Get display name and lookup key from normal contact table - */ - String[] displayProjection = new String[]{ - ContactsContract.Data.RAW_CONTACT_ID, - ContactsContract.Data.DISPLAY_NAME, - ContactsContract.Data.LOOKUP_KEY, - }; - String displayWhere = ContactsContract.Data.RAW_CONTACT_ID + "= ?"; - String[] displaySelectionArgs = new String[]{ - String.valueOf(rawId) - }; - Cursor displayCursor = contentResolver.query(ContactsContract.Data.CONTENT_URI, displayProjection, - displayWhere, displaySelectionArgs, null); - try { - if (displayCursor != null && displayCursor.moveToFirst()) { - displayName = displayCursor.getString(displayCursor.getColumnIndex(ContactsContract.Data.DISPLAY_NAME)); - lookupKey = displayCursor.getString(displayCursor.getColumnIndex(ContactsContract.Data.LOOKUP_KEY)); - } - } finally { - if (displayCursor != null && !displayCursor.isClosed()) - displayCursor.close(); - } - - /* - * 2c. Get all events for this raw contact. - * We don't get this information for the (merged) contact table, but from the raw contact. - * If we would query this infos from the contact table we would also get events that should have been filtered! - */ - Uri thisRawContactUri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI, rawId); - Uri entityUri = Uri.withAppendedPath(thisRawContactUri, ContactsContract.RawContacts.Entity.CONTENT_DIRECTORY); - String[] eventsProjection = new String[]{ - ContactsContract.RawContacts._ID, - ContactsContract.RawContacts.Entity.DATA_ID, - ContactsContract.CommonDataKinds.Event.START_DATE, - ContactsContract.CommonDataKinds.Event.TYPE, - ContactsContract.CommonDataKinds.Event.LABEL - }; - String eventsWhere = ContactsContract.RawContacts.Entity.MIMETYPE + "= ? AND " - + ContactsContract.RawContacts.Entity.DATA_ID + " IS NOT NULL"; - String[] eventsSelectionArgs = new String[]{ - ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE - }; - Cursor eventsCursor = contentResolver.query(entityUri, eventsProjection, eventsWhere, - eventsSelectionArgs, null); - try { - while (eventsCursor != null && eventsCursor.moveToNext()) { - startDate = eventsCursor.getString(eventsCursor.getColumnIndex(ContactsContract.CommonDataKinds.Event.START_DATE)); - type = eventsCursor.getInt(eventsCursor.getColumnIndex(ContactsContract.CommonDataKinds.Event.TYPE)); - label = eventsCursor.getString(eventsCursor.getColumnIndex(ContactsContract.CommonDataKinds.Event.LABEL)); - - /* - * 2d. Add this information to our MatrixCursor if not already added previously. - * - * If two SyncAdapter Accounts have the same contact with duplicated events, the event will already be in - * the HashSet addedEventsIdentifiers. - * - * eventIdentifier does not include startDate, because the String formats of startDate differ between accounts. - */ - String eventIdentifier = lookupKey + type + label; - if (addedEventsIdentifiers.contains(eventIdentifier)) { - Log.d(TAG, "Event was NOT added, duplicate! Identifier: " + eventIdentifier); - } else { - Log.d(TAG, "Event was added! Identifier " + eventIdentifier); - addedEventsIdentifiers.add(eventIdentifier); + public static ContentProviderOperation update(CalendarEvent event) { + + if(event.hasId()) { + ContentProviderOperation.Builder builder; + builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, event.getId())); + // Push values + assignValuesInBuilder(builder, event); + return builder.build(); + } else { + Log.e(TAG, "Can't update the event, there is no id"); + return null; + } + } - matrixCursor.newRow().add(mcIndex).add(displayName).add(lookupKey).add(startDate).add(type).add(label); - mcIndex++; - } - } - } finally { - if (eventsCursor != null && !eventsCursor.isClosed()) - eventsCursor.close(); - } - } - } - } finally { - if (rawContacts != null && !rawContacts.isClosed()) - rawContacts.close(); + /** + * Delete the specific event, id must be specified + * @param event Event to delete + * @return ContentProviderOperation to apply or null if no id + */ + public static ContentProviderOperation delete(CalendarEvent event) { + + if(event.hasId()) { + ContentProviderOperation.Builder builder; + builder = ContentProviderOperation.newDelete(ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, event.getId())); + return builder.build(); + } else { + Log.e(TAG, "Can't delete the event, there is no id"); + return null; } + } - List contactList = new ArrayList<>(); - try { - int eventDateColumn = matrixCursor - .getColumnIndex(ContactsContract.CommonDataKinds.Event.START_DATE); - int displayNameColumn = matrixCursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME); - int eventTypeColumn = matrixCursor - .getColumnIndex(ContactsContract.CommonDataKinds.Event.TYPE); - int eventCustomLabelColumn = matrixCursor - .getColumnIndex(ContactsContract.CommonDataKinds.Event.LABEL); - int eventLookupKeyColumn = matrixCursor - .getColumnIndex(ContactsContract.CommonDataKinds.Event.LOOKUP_KEY); + public static List getEventFromContact(Contact contact) { + return null; + } - // for every event... - while (matrixCursor.moveToNext()) { - Contact contact = new Contact(matrixCursor.getString(displayNameColumn)); - contact.setBirthday(DateUnknownYear.stringToDate(matrixCursor.getString(eventDateColumn))); - //contact.setType(matrixCursor.getInt(eventTypeColumn)); - //contact.setLabel(matrixCursor.getString(eventCustomLabelColumn)); - contact.setLookUpKey(matrixCursor.getString(eventLookupKeyColumn)); - contactList.add(contact); - } - } finally { - if (!matrixCursor.isClosed()) - matrixCursor.close(); - } - return contactList; - } } diff --git a/RememBirthday-UI/src/main/java/com/kunzisoft/remembirthday/provider/ReminderProvider.java b/RememBirthday-UI/src/main/java/com/kunzisoft/remembirthday/provider/ReminderProvider.java new file mode 100644 index 0000000..4574f4c --- /dev/null +++ b/RememBirthday-UI/src/main/java/com/kunzisoft/remembirthday/provider/ReminderProvider.java @@ -0,0 +1,46 @@ +package com.kunzisoft.remembirthday.provider; + +import android.content.ContentProviderOperation; +import android.content.Context; +import android.provider.CalendarContract; +import android.util.Log; + +import com.kunzisoft.remembirthday.element.Reminder; + +/** + * Created by joker on 28/07/17. + */ + +public class ReminderProvider { + + private static final String TAG = "ReminderProvider"; + + public static ContentProviderOperation insert(Context context, long eventId, Reminder reminder) { + + ContentProviderOperation.Builder builder = ContentProviderOperation + .newInsert(CalendarProvider.getBirthdayAdapterUri(context, CalendarContract.Reminders.CONTENT_URI)); + + builder.withValue(CalendarContract.Reminders.EVENT_ID, eventId); + builder.withValue(CalendarContract.Reminders.MINUTES, reminder.getMinutesBeforeEvent()); + builder.withValue(CalendarContract.Reminders.METHOD, CalendarContract.Reminders.METHOD_ALERT); + Log.d(TAG, "Add reminder : " + reminder); + return builder.build(); + } + + + public static ContentProviderOperation insert(Context context, Reminder reminder, int backref) { + ContentProviderOperation.Builder builder = ContentProviderOperation + .newInsert(CalendarProvider.getBirthdayAdapterUri(context, CalendarContract.Reminders.CONTENT_URI)); + + /* + * add reminder to last added event identified by backRef + * see http://stackoverflow.com/questions/4655291/semantics-of- + * withvaluebackreference + */ + builder.withValueBackReference(CalendarContract.Reminders.EVENT_ID, backref); + builder.withValue(CalendarContract.Reminders.MINUTES, reminder.getMinutesBeforeEvent()); + builder.withValue(CalendarContract.Reminders.METHOD, CalendarContract.Reminders.METHOD_ALERT); + Log.d(TAG, "Add reminder : " + reminder); + return builder.build(); + } +} diff --git a/RememBirthday-UI/src/main/java/com/kunzisoft/remembirthday/service/CalendarSyncAdapterService.java b/RememBirthday-UI/src/main/java/com/kunzisoft/remembirthday/service/CalendarSyncAdapterService.java index 34c9c22..cc250be 100644 --- a/RememBirthday-UI/src/main/java/com/kunzisoft/remembirthday/service/CalendarSyncAdapterService.java +++ b/RememBirthday-UI/src/main/java/com/kunzisoft/remembirthday/service/CalendarSyncAdapterService.java @@ -7,6 +7,7 @@ import android.content.AbstractThreadedSyncAdapter; import android.content.ContentProviderClient; import android.content.ContentProviderOperation; +import android.content.ContentProviderResult; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -21,14 +22,12 @@ import com.kunzisoft.remembirthday.element.CalendarEvent; import com.kunzisoft.remembirthday.element.Contact; import com.kunzisoft.remembirthday.element.Reminder; -import com.kunzisoft.remembirthday.preference.PreferencesManager; import com.kunzisoft.remembirthday.provider.CalendarProvider; +import com.kunzisoft.remembirthday.provider.ContactProvider; import com.kunzisoft.remembirthday.provider.EventProvider; - -import org.joda.time.DateTime; +import com.kunzisoft.remembirthday.provider.ReminderProvider; import java.util.ArrayList; -import java.util.Calendar; import java.util.List; @SuppressLint("NewApi") @@ -36,12 +35,6 @@ public class CalendarSyncAdapterService extends Service { private static final String TAG = "CalendarSyncService"; - /** - * Manage calendar between X years and Y Years of current year - */ - private static final int X_YEAR = 3; - private static final int Y_YEAR = 5; - public CalendarSyncAdapterService() { super(); } @@ -75,46 +68,31 @@ public static void performSync(Context context) { // Sync flow: // 1. Clear events table for this account completely - CalendarProvider.cleanTables(context, contentResolver, calendarId); + CalendarProvider.cleanTables(context, calendarId); // 2. Get birthdays from contacts // 3. Create events and reminders for each birthday - // collection of birthdays that will later be added to the calendar - ArrayList operationList = new ArrayList<>(); + List contactEventOperationList = new ArrayList<>(); + ArrayList reminderOperationList = new ArrayList<>(); - // iterate through all Contact Events - List contactList = EventProvider.getContacts(context, contentResolver); + // iterate through all Contact + List contactList = ContactProvider.getAllContacts(context); int backRef = 0; for (Contact contact : contactList) { + ContactEventOperation contactEventOperation = new ContactEventOperation(contact); + + for (CalendarEvent calendarEvent : contact.getBirthdayEvent().getEventsAroundThisYear()) { - /* - * Insert events for the past 3 years and the next 5 years. - */ - Calendar currCal = Calendar.getInstance(); - int currYear = currCal.get(Calendar.YEAR); - int startYear = currYear - X_YEAR; - int endYear = currYear + Y_YEAR; - - for (int iteratedYear = startYear; iteratedYear <= endYear; iteratedYear++) { - Log.d(TAG, "iteratedYear: " + iteratedYear); - - // calculate age - int age = iteratedYear - new DateTime(contact.getBirthday().getDate()).getYear(); - CalendarEvent calendarEvent = new CalendarEvent(contact.getBirthday().getDate()); - calendarEvent.setYear(iteratedYear); - calendarEvent.setAllDay(true); - calendarEvent.setTitle(contact.getName() + " " + age); - // Assign new reminder with default values - int[] defaultTime = PreferencesManager.getDefaultTime(context); - for(int dayBefore : PreferencesManager.getDefaultDays(context)) - calendarEvent.addReminder(new Reminder(calendarEvent.getDate(), - defaultTime[0], defaultTime[1], - dayBefore)); - Log.d(TAG, "Add event : " + calendarEvent); // TODO Ids Log.d(TAG, "BackRef is " + backRef); - operationList.add(EventProvider.insert(context, calendarId, calendarEvent, contact)); + + // Add event operation in list of contact manager + contactEventOperation.addContentProviderOperation( + EventProvider.insert(context, calendarId, calendarEvent, contact)); + + //TODO REMOVE REMINDERS BUG + /* * Gets ContentProviderOperation to insert new reminder to the * ContentProviderOperation with the given backRef. This is done using @@ -122,57 +100,76 @@ public static void performSync(Context context) { */ int noOfReminderOperations = 0; for(Reminder reminder : calendarEvent.getReminders()) { - - ContentProviderOperation.Builder builder = ContentProviderOperation - .newInsert(CalendarProvider.getBirthdayAdapterUri(context, CalendarContract.Reminders.CONTENT_URI)); - - /* - * add reminder to last added event identified by backRef - * - * see http://stackoverflow.com/questions/4655291/semantics-of- - * withvaluebackreference - */ - builder.withValueBackReference(CalendarContract.Reminders.EVENT_ID, backRef); - builder.withValue(CalendarContract.Reminders.MINUTES, reminder.getMinutesBeforeEvent()); - builder.withValue(CalendarContract.Reminders.METHOD, CalendarContract.Reminders.METHOD_ALERT); - operationList.add(builder.build()); - Log.d(TAG, "Add reminder : " + reminder); - + reminderOperationList.add(ReminderProvider.insert(context, reminder, backRef)); noOfReminderOperations += 1; } - // back references for the next reminders, 1 is for the event backRef += 1 + noOfReminderOperations; - - /* - * intermediate commit - otherwise the binder transaction fails on large - * operationList - */ - applyBatchByOperationListSize(operationList, contentResolver, 200); } + + contactEventOperationList.add(contactEventOperation); + } - /* Create events */ - applyBatchByOperationListSize(operationList, contentResolver, 0); + /* Create events with reminders and linkEventContract + * intermediate commit - otherwise the binder transaction fails on large + * operationList + * TODO for large list > 200, make multiple apply + */ + try { + Log.d(TAG, "Start applying the batch..."); + + /* + * Apply all Event Operations and do link with contact + */ + for(ContactEventOperation contactEventOperation : contactEventOperationList) { + ContentProviderResult[] contentProviderResults = + contentResolver.applyBatch(CalendarContract.AUTHORITY, contactEventOperation.getContentProviderOperations()); + + for(ContentProviderResult contentProviderResult : contentProviderResults) { + Log.d(TAG, "EventOperation apply : " + contentProviderResult.toString()); + long eventId = Long.parseLong(contentProviderResult.uri.getLastPathSegment()); + //Log.d(TAG, contactEventOperation.getContact() + " : " + eventId); + } + } + + /* + * Apply all Reminder Operations + */ + ContentProviderResult[] contentProviderResults = + contentResolver.applyBatch(CalendarContract.AUTHORITY, reminderOperationList); + for(ContentProviderResult contentProviderResult : contentProviderResults) { + Log.d(TAG, "ReminderOperation apply : " + contentProviderResult.toString()); + } + + Log.d(TAG, "Applying the batch was successful!"); + } catch (Exception e) { + Log.e(TAG, "Applying batch error!", e); + } } /** - * Apply batch if operationList is larger than "listSize" - * @param operationList List of Operations - * @param contentResolver Content Resolver - * @param listSize Size minimal + * Utility class for link a contact to specific ContentProviderOperation of event */ - private static void applyBatchByOperationListSize(ArrayList operationList, - ContentResolver contentResolver, - int listSize) { - if (operationList.size() > listSize) { - try { - Log.d(TAG, "Start applying the batch..."); - contentResolver.applyBatch(CalendarContract.AUTHORITY, operationList); - Log.d(TAG, "Applying the batch was successful!"); - } catch (Exception e) { - Log.e(TAG, "Applying batch error!", e); - } + private static class ContactEventOperation { + private Contact contact; + private ArrayList contentProviderOperations; + + ContactEventOperation(Contact contact) { + this.contact = contact; + this.contentProviderOperations = new ArrayList<>(); + } + + public Contact getContact() { + return contact; + } + + void addContentProviderOperation(ContentProviderOperation contentProviderOperation) { + contentProviderOperations.add(contentProviderOperation); + } + + ArrayList getContentProviderOperations() { + return contentProviderOperations; } }