@ins1der1211

Проблема с CAlendar API?

Здравствуйте. У меня возникла проблема с API календаря. Проблема следующая: Я создаю новый календарь в таблице CalendarContract.Calendars, используя CALLER_IS_SYNC_ADAPTER. Календарь создается без проблем

public void createCalendarWithId(int userId) {
Uri createUri = asSyncAdapter(CalendarContract.Calendars.CONTENT_URI);
ContentValues cv = new ContentValues();
cv.put(CalendarContract.Calendars.NAME, "Calendar " + userId);
cv.put(CalendarContract.Calendars.CALENDAR_COLOR, "");
cv.put(CalendarContract.Calendars.CALENDAR_ACCESS_LEVEL, CalendarContract.Calendars.CAL_ACCESS_OWNER);
cv.put(CalendarContract.Calendars.OWNER_ACCOUNT, "Owner");
cv.put(CalendarContract.Calendars.ACCOUNT_NAME, mContext.getString(R.string.app_name));
cv.put(CalendarContract.Calendars.ACCOUNT_TYPE, CalendarContract.ACCOUNT_TYPE_LOCAL);
mContext.getContentResolver().insert(createUri, cv);
}

private Uri asSyncAdapter(Uri uri) {
return uri.buildUpon()
.appendQueryParameter(android.provider.CalendarContract.CALLER_IS_SYNCADAPTER,"true")
.appendQueryParameter(CalendarContract.Calendars.ACCOUNT_NAME, mContext.getString(R.string.app_name))
.appendQueryParameter(CalendarContract.Calendars.ACCOUNT_TYPE, CalendarContract.ACCOUNT_TYPE_LOCAL).build();
}


После этого я добавляю новые события в этот календарь. Делаю это так:

public void createEventForCalendarWithId(String calendarId, long startTime, String description, String title) {
ContentValues cv = new ContentValues();
cv.put(CalendarContract.Events.DTSTART, startTime);
cv.put(CalendarContract.Events.DTEND, startTime);
cv.put(CalendarContract.Events.TITLE, title);
cv.put(CalendarContract.Events.DESCRIPTION, description);
cv.put(CalendarContract.Events.CALENDAR_ID, calendarId);
cv.put(CalendarContract.Events.EVENT_TIMEZONE, String.valueOf(TimeZone.getDefault()));
Uri insertedEvent = mContext.getContentResolver().insert(CalendarContract.Events.CONTENT_URI, cv);
String eventId = String.valueOf(ContentUris.parseId(insertedEvent));
createReminderForEventWithId(eventId, 2);
}


Создаю напоминания для данных событий:

public void createReminderForEventWithId(String eventId, int minutes) {
ContentValues cv = new ContentValues();
cv.put(CalendarContract.Reminders.MINUTES, minutes);
cv.put(CalendarContract.Reminders.EVENT_ID, eventId);
cv.put(CalendarContract.Reminders.METHOD, CalendarContract.Reminders.METHOD_ALERT);
mContext.getContentResolver().insert(CalendarContract.Reminders.CONTENT_URI, cv);
}


События добавляются и в календарь моего приложения и во встроенный календарь. напоминания также добавляются. Но когда я пытаюсь открыть событие в стандартном календаре, этот самый стандартный календарь падает. При переходе на событие из напоминания выходит то же самое.

Единственное отличие, которое есть между событием, созданным из моего календаря и событием созданным из стандартного календаря - отсутствие ACCOUNT_NAME (это я понял, пытаясь отредактировать событие, на некоторых устройствах это можно сделать не заходя в само событие). Но при этом, для созданного мной календаря я указал и ACCOUNT_NAME и ACCOUNT_TYPE. В Events почему-то эта информация не подтягивается. Я попытался записать ACCOUNT_NAME и ACCOUNT_TYPE по аналогии с созданием нового календаря (через CALLER_AS_SYNC_ADAPTER). В итоге: тот же результат.

public void createEventForCalendarWithId(String calendarId, long startTime, String description, String title) {
		Uri anyChanges = asSyncAdapterForEvents(CalendarContract.Events.CONTENT_URI);
				ContentValues cv = new ContentValues();
				cv.put(CalendarContract.Events.DTSTART, startTime);
				cv.put(CalendarContract.Events.DTEND, startTime);
				cv.put(CalendarContract.Events.TITLE, title);
				cv.put(CalendarContract.Events.DESCRIPTION, description);
				cv.put(CalendarContract.Events.CALENDAR_ID, calendarId);
				cv.put(CalendarContract.Events.EVENT_TIMEZONE, String.valueOf(TimeZone.getDefault()));
				Uri insertedEvent = mContext.getContentResolver().insert(anyChanges, cv);
				String eventId = String.valueOf(ContentUris.parseId(insertedEvent));
				createReminderForEventWithId(eventId, 2);
		}

private Uri asSyncAdapterForEvents(Uri uri) {
return uri.buildUpon()
.appendQueryParameter(android.provider.CalendarContract.CALLER_IS_SYNCADAPTER,"true")
.appendQueryParameter(CalendarContract.Events.ACCOUNT_NAME, mContext.getString(R.string.app_name))
.appendQueryParameter(CalendarContract.Events.ACCOUNT_TYPE, CalendarContract.ACCOUNT_TYPE_LOCAL).build();
}


Тогда я попытался записать эти поля "напрямую". Но приложение крашиться при попытке записи этих значений в БД. Причем stack trace сообщает что-то странное.
public void createEventForCalendarWithId(String calendarId, long startTime, String description, String title) {
ContentValues cv = new ContentValues();
cv.put(CalendarContract.Events.ACCOUNT_NAME, mContext.getString(R.string.app_name));
cv.put(CalendarContract.Events.ACCOUNT_TYPE, CalendarContract.ACCOUNT_TYPE_LOCAL);
cv.put(CalendarContract.Events.DTSTART, startTime);
cv.put(CalendarContract.Events.DTEND, startTime);
cv.put(CalendarContract.Events.TITLE, title);
cv.put(CalendarContract.Events.DESCRIPTION, description);
cv.put(CalendarContract.Events.CALENDAR_ID, calendarId);
cv.put(CalendarContract.Events.EVENT_TIMEZONE, String.valueOf(TimeZone.getDefault()));
Uri insertedEvent = mContext.getContentResolver().insert(CalendarContract.Events.CONTENT_URI, cv);
String eventId = String.valueOf(ContentUris.parseId(insertedEvent));
createReminderForEventWithId(eventId, 2);
}


Fatal Exception: java.lang.RuntimeException: An error occured while executing doInBackground()
at android.os.AsyncTask$3.done(AsyncTask.java:304)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355)
at java.util.concurrent.FutureTask.setException(FutureTask.java:222)
at java.util.concurrent.FutureTask.run(FutureTask.java:242)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:818)
Caused by java.lang.IllegalArgumentException: Only the provider may write to account_name
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:172)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:142)
at android.content.ContentProviderProxy.insert(ContentProviderNative.java:476)
at android.content.ContentResolver.insert(ContentResolver.java:1265)
at com.bnsf.jobfinder.jobfinder.calendar.CalendarManager.createEventForCalendarWithId(CalendarManager.java:156)
at com.bnsf.jobfinder.jobfinder.calendar.AddEventActivity$CreateEventAsyncTask.doInBackground(AddEventActivity.java:169)
at com.bnsf.jobfinder.jobfinder.calendar.AddEventActivity$CreateEventAsyncTask.doInBackground(AddEventActivity.java:156)
at android.os.AsyncTask$2.call(AsyncTask.java:292)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:818)

Я пытаюсь записать значения в ACCOUNT_NAME и ACCOUNT_TYPE через ContentResolver, который в свою очередь вызывает ContentProvider, поэтому сообщение о том что только ContentProvider может записывать в эти колонки кажется странным.

Пример проекта на github: https://github.com/ins1der1211/CalendarTrouble.git

Есть какие-либо идеи?
  • Вопрос задан
  • 422 просмотра
Пригласить эксперта
Ответы на вопрос 1
@glebas1986
Встретил такую же проблему. Всё правильно делаете, но забыли указать при создании календаря поле CalendarContract.Calendars.CALENDAR_DISPLAY_NAME
Календарь при открытии события показывает какому календарю оно принадлежит, для этого берёт calendar_displayName, но если не находит, просто падает. Добавьте и всё заработает
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы