Is it possible to add a reminder programatically without creating event in android calendar?

5.1k views Asked by At

I use Google Calendar Provider for Android to add Events in my Google Calendar: https://developer.android.com/guide/topics/providers/calendar-provider.html#overview

And this is the example to add Reminder for the Event:

ContentValues values = new ContentValues();
values.put(CalendarContract.Reminders.MINUTES, 15);
values.put(CalendarContract.Reminders.EVENT_ID, reminderEventId);
values.put(CalendarContract.Reminders.METHOD, CalendarContract.Reminders.METHOD_ALERT);
getContentResolver().insert(CalendarContract.Reminders.CONTENT_URI, values);

But in my Google Calendar app I can create Reminders without having EVENT_ID. Is it possible to create the same reminders with Google Calendar Provider for Android?

The same like this: https://gsuiteupdates.googleblog.com/2016/04/launch-of-reminders-for-google-calendar.html

3

There are 3 answers

0
sytolk On BEST ANSWER

I have see that Google launch reminders for Google Calendar 2016 (This is a different Reminders from Event Reminders) and at the moment I can`t find in Android SDK (API 25) this kind of Google Calendar Reminders (and possibility to add programmatically appointment slots too).

1
Ngako Kgothatso On

As stated in https://developer.android.com/guide/topics/providers/calendar-provider.html#overview

Reminders are specified in minutes before the event and have a method that determines how the user will be alerted.

A reminder that doesn't belong to an event will not have a time of reference to go off.

I think for what you are trying to achieve () it would be best for you to look at Androids AlarmManager https://developer.android.com/reference/android/app/AlarmManager.html

Or see what other people are saying about similar things https://stackoverflow.com/a/16549111/6431430

0
Sourcerer On

A tricky approach I'm using is creating the remainders, instead of calendar, in the contacts, using Custom Reminder (and the ContactContract provider). So, you can use any label, and the reminder is shown in the specific date. Cons: - You have to have the "Birthday" calendar enable - The remainder is date based (not time) - They don't repeat the reminder (as the new calendar does)

package es.goodsal.mobile.provider.contact;

import android.content.ContentUris;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.provider.ContactsContract;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import android.provider.ContactsContract.CommonDataKinds.Event;


import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import es.goodsal.mobile.app.App;
import es.goodsal.mobile.model.ReminderType;

/**
 * Created by jmalbarran on 16/9/17.
 */

public class Reminder implements Comparable<Reminder> {
    private static final String TAG = Reminder.class.getSimpleName();
    private static final SimpleDateFormat fullPeriodicDateFormat = new SimpleDateFormat("--MM-dd");
    private static final SimpleDateFormat periodicDateFormat = new SimpleDateFormat("MM-dd");
    private static final SimpleDateFormat nonPeriodicDateFormat = new SimpleDateFormat("yyyy-MM-dd");
    private static final String DUE_DATE = Event.DATA4;
    private static final String COMMENT = Event.DATA5;

    private static final Uri CONTENT_URI = ContactsContract.Data.CONTENT_URI;
    private static final String[] PROJECTION = new String[]{
                    Event._ID,           //0
                    Event.CONTACT_ID,    //1
                    Event.LOOKUP_KEY,    //2
                    Event.START_DATE,    //3
                    Event.TYPE,          //4
                    Event.LABEL,         //5 Label for custom event
                    Reminder.DUE_DATE,   // 6
                    Reminder.COMMENT     // 7 Use this field to save Comments
    };

    static {
        periodicDateFormat.setCalendar(Calendar.getInstance());   // Set local time zone
        nonPeriodicDateFormat.setCalendar(Calendar.getInstance());   // Set local time zone
    }

    private long id = -1;
    private ConContact contact;
    private Date reminderDate;
    private Date dueDate;
    private ReminderType type;
    private String comment;

    // region Constructor

    public Reminder(ConContact contact, Date reminderDate,
                    Date dueDate, ReminderType type, String comment) {
        this.contact = contact;
        this.reminderDate = reminderDate;
        this.dueDate = dueDate;
        this.type = type;
        this.comment = comment;
    }

    public Reminder(@NonNull String reminderUri) {
        this(Uri.parse(reminderUri));
    }

    public Reminder(@NonNull Uri reminderUri) {
        Cursor cursor;
        cursor = App.getContentResolverInstance().query(reminderUri,
                        PROJECTION, null, null, null);
        if (cursor == null) {
            throw new IllegalArgumentException(reminderUri.toString() + " opportunity not found.");
        } else {
            cursor.moveToFirst();
            setFromCursor(null, cursor);
        }
    }

    private Reminder(@NonNull Cursor cursor) {
        this(null, cursor);
    }

    private Reminder(@Nullable ConContact contact, @NonNull Cursor cursor) {
        setFromCursor(contact, cursor);
    }

    private void setFromCursor(@Nullable ConContact contact, @NonNull Cursor cursor) {
        int eventType;
        String eventLabel;
        Calendar calDueDate, calReminderDate;
        Date now;
        String strSplitDate[];
        String strDueDate;

        this.id = cursor.getLong(0);
        if (contact != null) {
            this.contact = contact;
        } else {
            this.contact = new ConContact(cursor.getLong(1), cursor.getString(2));
        }

        // Type
        eventType = cursor.getInt(4); //Event.TYPE
        eventLabel = cursor.getString(5); //Event.LABEL
        switch (eventType) {
            case ContactsContract.CommonDataKinds.Event.TYPE_ANNIVERSARY:
                type = ReminderType.anniversary;
                break;
            case ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY:
                type = ReminderType.birthday;
                break;
            case ContactsContract.CommonDataKinds.Event.TYPE_CUSTOM:
                type = ReminderType.other;
                eventLabel = cursor.getString(5);
                for (ReminderType iType : ReminderType.values()) {
                    if (iType.getLabel().equals(eventLabel)) {
                        type = iType;
                        break;
                    }
                }
                break;
            default:
                type = ReminderType.other;
        }

        // Due / Reminder date

        try {
            now = new Date();
            if (type.isPeriodic()) {

                // Periodic Due date
                calDueDate = Calendar.getInstance();
                calDueDate.setTime(now);
                strSplitDate = cursor.getString(6).split("-"); // DATA4
                if (strSplitDate != null && strSplitDate.length == 3) {
                    calDueDate.set(Calendar.MONTH, Integer.parseInt(strSplitDate[1]));
                    calDueDate.set(Calendar.DAY_OF_MONTH, Integer.parseInt(strSplitDate[2]));
                    if (calDueDate.before(now)) {  // Crossed end-of-year boundary
                        calDueDate.add(Calendar.YEAR, 1); // Next year
                    }
                } else {
                    Log.e(TAG, "getReminders: Invalid date for periodic reminder due date " +
                                    cursor.getString(6));
                }
                dueDate = calDueDate.getTime();

                // Periodic Reminder date
                calReminderDate = Calendar.getInstance();
                calReminderDate.setTime(dueDate);
                strSplitDate = cursor.getString(3).split("-"); // START_DATE
                if (strSplitDate != null && strSplitDate.length == 3) {
                    calReminderDate.set(Calendar.MONTH, Integer.parseInt(strSplitDate[1]));
                    calReminderDate.set(Calendar.DAY_OF_MONTH, Integer.parseInt(strSplitDate[2]));
                    if (calReminderDate.after(dueDate)) {  // Crossed end-of-year boundary
                        calDueDate.add(Calendar.YEAR, -11); // Previous year
                    }
                } else {
                    Log.e(TAG, "getReminders: Invalid date for periodic reminder start date " +
                                    cursor.getString(6));
                }
                reminderDate = calReminderDate.getTime();
            } else {
                // Non periodic date
                reminderDate = nonPeriodicDateFormat.parse(cursor.getString(3)); // START_DATE
                dueDate = nonPeriodicDateFormat.parse(cursor.getString(6)); // DATA4
                dueDate = dueDate !=null ? dueDate : reminderDate;
            }
        } catch (Exception e) { // NullPointer and/or ParseFormat
            dueDate = null;
            reminderDate = null;
        }

        // Comment
        comment = cursor.getString(7); // DATA5
    }

    // endregion Constructor

    // region Getter/Setter
    public Uri getUri() {
        Uri uri;

        uri = null;
        if (id != -1) {
            uri = ContentUris.withAppendedId(CONTENT_URI, id);
        }

        return uri;
    }

    public ConContact getContact() {
        return contact;
    }

    public Date getReminderDate() {
        return reminderDate;
    }

    public Date getDueDate() {
        return dueDate;
    }

    public ReminderType getReminderType() {
        return type;
    }

    public String getComment() {
        return comment;
    }

    public boolean isPeriodic() {
        return type.isPeriodic();
    }

    public void setContact(ConContact contact) {
        this.contact = contact;
    }

    public void setReminderDate(Date reminderDate) {
        this.reminderDate = reminderDate;
    }

    public void setDueDate(Date dueDate) {
        this.dueDate = dueDate;
    }

    public void setType(ReminderType type) {
        this.type = type;
    }

    public void setComment(String comment) {
        this.comment = comment;
    }

// endregion Getter/Setter

    // region Commands

    public boolean save() {
        boolean ret;
        ContentValues values;
        Uri savedUri;

        ret = false;

        values = new ContentValues();
        values.put(ContactsContract.CommonDataKinds.Event.MIMETYPE,
                        ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE);
        //values.put(ContactsContract.CommonDataKinds.Event.CONTACT_ID, contact.getContactId());
        values.put(ContactsContract.CommonDataKinds.Event.RAW_CONTACT_ID, contact.getRawContactId());
        if (type.isPeriodic()) {
            values.put(ContactsContract.CommonDataKinds.Event.START_DATE,
                            fullPeriodicDateFormat.format(reminderDate));
            values.put(ContactsContract.CommonDataKinds.Event.DATA4,
                            fullPeriodicDateFormat.format(dueDate));
        } else {
            values.put(ContactsContract.CommonDataKinds.Event.START_DATE,
                            nonPeriodicDateFormat.format(reminderDate));
            values.put(ContactsContract.CommonDataKinds.Event.DATA4,
                            nonPeriodicDateFormat.format(dueDate));
        }
        values.put(ContactsContract.CommonDataKinds.Event.TYPE,
                        Integer.toString(type.getContactEventType()));
        values.put(ContactsContract.CommonDataKinds.Event.LABEL, type.getLabel());
        values.put(ContactsContract.CommonDataKinds.Event.DATA5, comment);
        if (id == -1L) {
            // Create new
            savedUri = App.getContentResolverInstance().insert(ContactsContract.Data.CONTENT_URI, values);
            id = ContentUris.parseId(savedUri);
            ret = true;
            Log.d(TAG, "save: Inserted uri=" + savedUri + " id=" + id);
        } else {
            // update existing id
//          if (0<App.getContentResolverInstance().update(ContactsContract.Data.CONTENT_URI, values,
//                          ContactsContract.Data._ID + "='" + Long.toString(id) + "'", null)){
            if (0 < App.getContentResolverInstance().update(
                            ContentUris.withAppendedId(ContactsContract.Data.CONTENT_URI, id),
                            values,
                            null,
                            null)) {

                ret = true;
                Log.d(TAG, "save: Updated id=" + id);
            } else {
                Log.e(TAG, "save: Error updating id=" + id);
            }

        }
        contact.resetReminders();

        return ret;
    }

    public boolean delete() {
        boolean ret = false;
        if (id != -1) {
            if (0 < App.getContentResolverInstance().delete(
                            ContentUris.withAppendedId(ContactsContract.Data.CONTENT_URI, id),
                            null,
                            null)) {
                Log.d(TAG, "delete: Deleted id=" + id);
                ret = true;
            } else {
                Log.w(TAG, "delete: Not found id=" + id);
            }
        }
        return ret;
    }

// endregion Commands

    @Override
    public int compareTo(@NonNull Reminder o) {
        if (reminderDate == null) {
            return 1; // null always at end
        } else {
            if (o.reminderDate == null) {
                return -1;
            } else {
                return reminderDate.compareTo(o.reminderDate);
            }
        }
    }

    /**
     * Get reminders for all contact, in a specific date (including periodics)
     *
     * @param date exact to search for (reminder date NOT due date)
     * @return List of reminders
     */
    public static List<Reminder> getReminders(Date date) {
        Cursor cursor;
        ArrayList<Reminder> retList;
        String where;
        String[] selectionArgs;
        Date dueDate;
        String dateSelection;
        String[] strSplitDate;
        Calendar calDueDate;

        retList = null;
        Uri uri = ContactsContract.Data.CONTENT_URI;

        String sortOrder = null;

        // Execute one query per reminder type
        for (ReminderType type : ReminderType.values()) {

            dateSelection = type.isPeriodic() ?
                            "%" + periodicDateFormat.format(date) :
                            "%" + nonPeriodicDateFormat.format(date);

            if (type.getContactEventType() == ContactsContract.CommonDataKinds.Event.TYPE_CUSTOM) {
                // Check event label too
                where = ContactsContract.CommonDataKinds.Event.MIMETYPE + "= ? AND " +
                                ContactsContract.CommonDataKinds.Event.START_DATE + " LIKE ? AND " +
                                ContactsContract.CommonDataKinds.Event.LABEL + "= ? AND " +
                                ContactsContract.CommonDataKinds.Event.TYPE + "=" + type.getContactEventType();

                selectionArgs = new String[]{
                                ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE,
                                dateSelection,
                                type.getLabel()
                };
            } else {
                // Non custom reminder. Don't check label
                where = ContactsContract.CommonDataKinds.Event.MIMETYPE + "= ? AND " +
                                ContactsContract.CommonDataKinds.Event.START_DATE + " LIKE ? AND " +
                                ContactsContract.CommonDataKinds.Event.TYPE + "=" + type.getContactEventType();
                selectionArgs = new String[]{
                                ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE,
                                dateSelection
                };
            } // where & selection per event type

            cursor = App.getContentResolverInstance().query(uri, PROJECTION,
                            where, selectionArgs, sortOrder);
            if (cursor != null) {
                retList = new ArrayList<Reminder>();
                while (cursor.moveToNext()) {
                    retList.add(new Reminder(cursor));
                }

                cursor.close();
            } // if cursor
        } // for per every type
        return retList;
    }

    /**
     * Get reminders for a specific contact, from a date forward
     *
     * @param contact Contact for search for
     * @param date    current or future date to search (including periodic)
     * @return List of reminders for current or future search
     */
    public static List<Reminder> getReminders(ConContact contact, Date date) {
        Uri uri;
        Cursor cursor;
        ArrayList<Reminder> retList = null;
        String where;
        String[] selectionArgs;
        String sortOrder;

        int contactEventType;
        String label;
        Calendar calReminderDate, calDueDate;
        String strReminderDate, strDueDate;

        ReminderType type;
        Date reminderDate;
        Date dueDate;
        String strDescription;
        String[] strSplitDate;

        uri = ContactsContract.Data.CONTENT_URI;

        sortOrder = null;

        // Check event label too
        where = ContactsContract.CommonDataKinds.Event.MIMETYPE + " = ? AND " +
                        ContactsContract.CommonDataKinds.Event.CONTACT_ID + " = ? AND " +
                        "(" +
                        ContactsContract.CommonDataKinds.Event.START_DATE + " LIKE '--%' OR " +
                        ContactsContract.CommonDataKinds.Event.START_DATE + " >= ?" +
                        ")";

        selectionArgs = new String[]{
                        ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE,
                        Long.toString(contact.getContactId()),
                        nonPeriodicDateFormat.format(date)
        };

        sortOrder = ContactsContract.CommonDataKinds.Event.START_DATE;

        cursor = App.getContentResolverInstance().query(uri, PROJECTION,
                        where, selectionArgs, sortOrder);
        if (cursor != null) {
            retList = new ArrayList<Reminder>();
            while (cursor.moveToNext()) {
                retList.add(new Reminder(contact, cursor));
            }
            Collections.sort(retList);
            cursor.close();
        } // if cursor
        // for per every type
        return retList;
    }

} // end Reminder class