Cannot create calendar event in android N

1.1k views Asked by At

I'm facing a weird issue in my full calendar app starting in Android N. I would like to know if I'm missing something or if there's something wrong with the calendar provider on Android N.

I'm using the calendar content provider to manage the calendar. It was possible to open, edit, delete and create events (from Android 2.1 to Marshmallow) but now on N it's not possible to create new events. When trying to create a new one, the event briefly appears on the event list and then disappears. When looking at the logs, that's what really happens:

EventHandler::InsertEvent: Forbidden Entity[ id=2172 calendar_id=1 calendar_sync_id=-899202677 ] 

com.google.api.client.googleapis.json.GoogleJsonResponseException: 403 Forbidden {
  "errors": [
  {
    "reason": "forbidden",
    "domain": "global",
    "message": "Forbidden"
  }],
  "message": "Forbidden",
  "code": 403
}

All that I can find with this error is related with Calendar API calls and not with the content provider (My guess is that internally is using the Calendar API and something is failing for a lack of authentication¿?). But, I've looked into the provider documentation https://developer.android.com/guide/topics/providers/calendar-provider.html and could not find any requirement of authentication.

I've looked for changes in the current calendar app from the AOSP but I could not find anything relevant. They are operating as a sync adapter that gives you the possibility to write on some special columns that I'm not using so I don't think this is the cause of the issue.

I also was thinking on using the Calendar API, but this wouldn't be desired as won't work offline and as the Calendar API documentation itself states: " Note: The purpose of this quickstart is to demonstrate the use of the Calendar API in an Android application. However, production calendar apps will strongly benefit by making use of the Calendar Provider content provider, which works offline, receives pushes, is a good match for the platform. "

I've been working long time on this with no luck, playing with the Google play client/services libraries, trying using Google Sign In, manifest permissions and several samples and well after all I don't know where else to go now and I would really appreciate if any of you could let me know what could be wrong or what's the expected way to go when writing a calendar event on Android N.

Thank you so much

Here the code:

public Uri AddCalendarEntry(CalendarEventObject eventObject) {
    ContentValues event = new ContentValues();

    if (eventObject.getCalendar() != null) {
        event.put(CalendarContract.Events.CALENDAR_ID, Integer.parseInt(eventObject.getCalendar().getId()));
    } else { 
        Log.e (DEBUG_TAG,"calendar id not available ");
        return null;
    }

    event.put(CalendarContract.Events.TITLE, eventObject.getSubject());
    event.put(CalendarContract.Events.DESCRIPTION, eventObject.getDescription());
    event.put(CalendarContract.Events.EVENT_LOCATION, eventObject.getLocation());
    event.put(CalendarContract.Events.STATUS, CalendarContract.Events.STATUS_CONFIRMED);
    event.put(CalendarContract.Events.HAS_ALARM, 1);  // 0 for false, 1 for true

    // All day events must use timezone UTC, others can use default
    if (eventObject.isAllDay()) {
        event.put(CalendarContract.Events.ALL_DAY,  1);  // 1 for true
        event.put(CalendarContract.Events.EVENT_TIMEZONE, Time.TIMEZONE_UTC);
    } else {
        event.put(CalendarContract.Events.ALL_DAY, 0);  // 0 for false
        event.put(CalendarContract.Events.EVENT_TIMEZONE, TimeZone.getDefault().toString());
    }

    // Recurrent events must use "duration", normal events should use "dtend"
    event.put(CalendarContract.Events.DTSTART, eventObject.getStartDateTime());
    String recurrence = eventObject.getRecurrence();
    if (recurrence != null && !TextUtils.isEmpty(recurrence)) {
        String duration = datesToDuration(eventObject.getStartDateTime(), eventObject.getEndDateTime());
        event.put(CalendarContract.Events.DURATION, duration);
        event.put(CalendarContract.Events.RRULE, recurrence);
    } else {
        event.put(CalendarContract.Events.DTEND, eventObject.getEndDateTime());
    }

    Uri eventsUri = CalendarContract.Events.CONTENT_URI;
    Uri insertedUri = null;
    try {
        insertedUri = mainActivity.getContentResolver().insert(eventsUri, event);
    } catch(Exception e) {
        Log.e(DEBUG_TAG, "Error creating event " + e);
    }
    return insertedUri;
}

Here the full trace:

at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:113)
at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:40)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest$1.interceptResponse(AbstractGoogleClientRequest.java:321)
at 
com.google.api.client.http.HttpRequest.execute(HttpRequest.java:1065)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:419)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:352)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:469)
at com.google.android.syncadapters.calendar.CalendarRequestExecutor.executeInternal(CalendarRequestExecutor.java:146)
at com.google.android.syncadapters.calendar.CalendarRequestExecutor.execute(CalendarRequestExecutor.java:120)
at com.google.android.syncadapters.calendar.EventHandler.sendEntityToServer(EventHandler.java:485)
at com.google.android.syncadapters.calendar.CalendarSyncAdapterApiary.sendEntityToServer(CalendarSyncAdapterApiary.java:4084)
at com.google.android.syncadapters.calendar.CalendarSyncAdapterApiary.processLocalChanges(CalendarSyncAdapterApiary.java:4031)
at com.google.android.syncadapters.calendar.CalendarSyncAdapterApiary.processLocalChangesForHandler(CalendarSyncAdapterApiary.java:3970)
at com.google.android.syncadapters.calendar.CalendarSyncAdapterApiary.performUpsync(CalendarSyncAdapterApiary.java:791)
at com.google.android.syncadapters.calendar.CalendarSyncAdapterApiary.performSync(CalendarSyncAdapterApiary.java:691)
at com.google.android.syncadapters.calendar.CalendarSyncAdapterApiary.onPerformLoggedSync(CalendarSyncAdapterApiary.java:504)
at com.android.emailcommon.syncadapter.LoggingThreadedSyncAdapter.onPerformSync(LoggingThreadedSyncAdapter.java:50)
at android.content.AbstractThreadedSyncAdapter$SyncThread.run(AbstractThreadedSyncAdapter.java:272)
1

There are 1 answers

0
Darklord5 On BEST ANSWER

I'm posting this information, not a real solution, but my conclusions, in case I can help someone that is in the same situation.

Apparently, Google forgot to grant calendar permission in some scenarios. So my guess is that support for calendar has been removed unintentionally in:

Old apps (with target below 23, Android M)

AND

New devices (using Android N, 24 and up)

According to documentation, if you target below 23 you don't need to worry about runtime permissions, but it seems that using the calendar API has some requirements that weren't solved properly. After the (difficult) migration in my app to Android Studio and targeting to newer APIs, 23-24, my calendar can finally save events.