Android Event contentResolver query synchronize problem - android

I am using following code to pull the data from the core event calendar application to show in my app.
ContentResolver contentResolver = this.getContentResolver();
final Cursor cursor = contentResolver.query(Uri.parse("content://calendar/calendars"),(new String[] { "_id", "displayName", "selected" }),null, null, null);
HashSet<String> calendarIds = new HashSet<String>();
while (cursor.moveToNext()) {
String _id = cursor.getString(0);
String displayName = cursor.getString(1);
Boolean selected = !cursor.getString(2).equals("0");
calendarIds.add(_id);
}
for (String id : calendarIds) {
Uri.Builder builder = Uri.parse(
"content://calendar/instances/when").buildUpon();
Calendar calendar = Calendar.getInstance();
long now = calendar.getTimeInMillis();
ContentUris.appendId(builder, now
- ((DateUtils.DAY_IN_MILLIS) * 24));
ContentUris.appendId(builder, now
+ ((DateUtils.DAY_IN_MILLIS) * 24));
Cursor eventCursor = contentResolver.query(builder.build(),
new String[] { "title", "begin", "end", "allDay",
"description", "eventLocation", "dtstart",
"dtend", "eventStatus", "visibility",
"transparency", "hasAlarm" },
"Calendars._id=" + id, null,
"startDay ASC, startMinute ASC");
while (eventCursor.moveToNext()) {
if (eventCursor != null) {
String title = eventCursor.getString(0);
}
}
The code is working fine. But sometime if I add a event in the calendar and come back to app, it is not showing the new event. If i exit the app and again come back or change the tab, the new event is added to the list. What have to do solve the synchronize?

When you come back to your app after the calendar add occurs, onResume() gets called. Re-query the cursor there to cause your UI to update.
Also, you should look at the ContentObserver interface, which sets up a callback interface for being notified of changes (Say they show up on the device after a network sync while you haven't left your app at all.)
Finally, using a CursorAdapter to drive a ListView from your Cursor will set up a ContentObserver automatically and take care of a lot of the glue code you had to write to get this far. It's a much more automatic way of doing it.

Related

How can I distinguish between Holidays and Birthdays and user created all day events

I'm am working on an app that gets all the user events and calendars to the app.
But I wish to distinguish between an user created all day event, and one that was automatically added to the calendar like a birthday or a holiday.
I get all the different calendars like this:
cursor = contentResolver.query(Uri.parse("content://com.android.calendar/calendars"),
(new String[]{Calendars._ID, Calendars.CALENDAR_DISPLAY_NAME, Calendars.CALENDAR_ACCESS_LEVEL}), null, null, null);
and the events like this:
Cursor eventCursor = contentResolver.query(builder.build(),
new String[]{"title", "begin", "end", "allDay"}, "(" + Events.CALENDAR_ID + " = ?)",
new String[]{String.valueOf(userCalendar.id)}, "startDay ASC, startMinute ASC");
In addition, can someone please direct me to the list of available google calendar and event fields?
for some reason I couldn't find them anywhere...
Thanks
well for now I'm just filtering out according to the access level
if (userCalendar.accessLevel != Calendars.CAL_ACCESS_READ)
You cant directly, distinguish the events as birthdays , holidays or user created Events.
But I did a workaround, its for my app LinkedOut Which organize contacts based on the events. So i want only the actual events not birthdays.
So I only took the events which have locations, because , the kind of events what i want will have locations.
If you only want birthdays, filter the events which contain "birthday" in the event name,
all the other event, which do not have location and do not have 'birthday' in their name will be holidays.
I know it may not be always correct , but it will work for now.
Code for getting events that has location,
public void getDataFromEventTable() {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_CALENDAR) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_CALENDAR}, 121);
}
Cursor cur = null;
ContentResolver cr = getContentResolver();
String[] mProjection =
{
"_id",
CalendarContract.Events.TITLE,
CalendarContract.Events.EVENT_LOCATION,
CalendarContract.Events.DTSTART,
CalendarContract.Events.DTEND,
};
Uri uri = CalendarContract.Events.CONTENT_URI;
cur = cr.query(uri, mProjection, null, null, null);
while (cur.moveToNext()) {
String title = cur.getString(cur.getColumnIndex(CalendarContract.Events.TITLE));
String loc = cur.getString(cur.getColumnIndex(CalendarContract.Events.EVENT_LOCATION));
if(loc.length()>0)
Log.e("event",title+" "+loc);
}
}

Get and filter events from Google Calendar

In my android app, I have the code to write, update and delete events from/to calendars.
Now I want to get some events from a calendar but I don't know the event IDs. I want to filter events by title, description, or any other value.
How can I do that?
My code:
public static void getEvents(Activity mContext) {
Uri.Builder builder = Uri.parse("content://com.android.calendar/instances/when").buildUpon();
long now = new Date().getTime();
ContentUris.appendId(builder, now - DateUtils.DAY_IN_MILLIS * 10000);
ContentUris.appendId(builder, now + DateUtils.DAY_IN_MILLIS * 10000);
String selection = "((calendar_id = ?) AND (description LIKE ?))";
String[] selectionArgs = new String[] {""+id, "'%abc%'"};
Cursor eventCursor = contentResolver.query(builder.build(),
new String[] { "title", "begin", "end", "allDay", "description"},
selection, selectionArgs,
"startDay ASC, startMinute ASC");
while (eventCursor.moveToNext()) {
String eventTitle = eventCursor.getString(0);
String description = eventCursor.getString(4);
}
}
When I filter by calendar id only - it works and I get all the events in this calendar. When I try to add the "AND description = ? " - I get 0 events although I know there are events with the string in their description.
Calendar API has Freebusy query where you conduct a filter between dates using timeMin and timeMax.
Check this SO thread for actual code sample.
If you wanted to do a query based on your preferences (title,etc), you'd have to make your own custom implementation. You can try to fetch all the events first then make your own custom query on the response.

Get events from all accounts of the device and only events for today

I need to get the number of events on the calendars for today. Only the number of events of all calendars, do not need anything more.
I found several threads about it, but I can not get the number of events for today.
using this code, the variable "events" gives me the number of all events in the first calendar.
int events=0;
public void read() {
Context context = getApplicationContext();
ContentResolver contentResolver = context.getContentResolver();
long ntime = System.currentTimeMillis();
Cursor cursr = contentResolver.query(Uri.parse("content://com.android.calendar/events"), new String[]{ "calendar_id", "title", "description", "dtstart", "dtend", "eventLocation" }, "calendar_id=1", null, null);
cursr.moveToFirst();
String[] CalNames = new String[cursr.getCount()];
int[] CalIds = new int[cursr.getCount()];
for (int i = 0; i < CalNames.length; i++) {
CalIds[i] = cursr.getInt(0);
CalNames[i] = "Event"+cursr.getInt(0)+": \nTitle: "+ cursr.getString(1)+"\nDescription: "+cursr.getString(2)+"\nStart Date: "+cursr.getLong(3)+"\nEnd Date : "+cursr.getLong(4)+"\nLocation : "+cursr.getString(5);
long StartTime = cursr.getLong(3);
long EndTime = cursr.getLong(4);
if ((StartTime<ntime)&&(ntime<EndTime)) {
System.out.println("In the middle of something");
break;
}
cursr.moveToNext();
events++;
}
cursr.close();
Log.i("control","vueltas:"+events);
events=0;
}
I need to modify the code to read events from all calendars of the device, and only events for today.
several days looking for how to do it, but the solutions I've found (including this page) do not know how to apply my code...
There is a very similar to my question, but has no answer https://stackoverflow.com/questions/18301870/get-todays-calendar-events
I appreciate your help.
What you need to do is query the calendars, and for each of them, query the events instances and just count them.
Something like that
// Projection array. Creating indices for this array instead of doing
// dynamic lookups improves performance.
private static final String[] CALENDAR_PROJECTION = new String[] { Calendars.ACCOUNT_NAME };
private static final String[] INSTANCE_PROJECTION = new String[] { Instances._ID };
// The indices for the projection array above.
private static final int PROJECTION_ACCOUNT_NAME_INDEX = 0;
// this is the method that returns the count of all events for all the calendars
public static int getAllEventsCount(final Context context)
{
int eventsCount = 0;
// queries the calendars
final Cursor calendarCursor = context.getContentResolver().query(Calendars.CONTENT_URI, CALENDAR_PROJECTION, null, null, null);
while (calendarCursor.moveToNext())
{
// gets the calendar name - you need that to query the instances for each calendar
final String accountName = calendarCursor.getString(PROJECTION_ACCOUNT_NAME_INDEX);
eventsCount = eventsCount + getCalendarEventsCount(context, accountName);
}
calendarCursor.close();
return eventsCount;
}
// this method gets a count of the events in a given calendar
private static int getCalendarEventsCount(final Context context, final String accountName)
{
final Calendar beginTime = Calendar.getInstance();
final Calendar endTime = Calendar.getInstance();
// get events from the start of the day until the same evening.
beginTime.set(beginTime.get(Calendar.YEAR), beginTime.get(Calendar.MONTH), beginTime.get(Calendar.DAY_OF_MONTH), 0, 0, 0);
endTime.set(beginTime.get(Calendar.YEAR), beginTime.get(Calendar.MONTH), beginTime.get(Calendar.DAY_OF_MONTH), 23, 59, 59);
final long startMillis = beginTime.getTimeInMillis();
final long endMillis = endTime.getTimeInMillis();
// Construct the query with the desired date range.
final Uri.Builder builder = Instances.CONTENT_URI.buildUpon();
ContentUris.appendId(builder, startMillis);
ContentUris.appendId(builder, endMillis);
final String selection = "(" + Calendars.ACCOUNT_NAME + " = ?)";
final String[] selectionArgs = new String[] { accountName };
final Cursor cursor = context.getContentResolver().query(builder.build(), INSTANCE_PROJECTION, selection, selectionArgs, Instances.BEGIN + " ASC");
int count = cursor.getCount();
cursor.close();
return count;
}
This has been tested and it works.
Hope it helps.
Did you look at Reading all of today's events using CalendarContract - Android 4.0+ ? But you would still need to query each calendar separately. You code is querying all events and post-processing whereas you should filter the start and end times in the query.
A content resolver is designed to return content, and is not a general purpose SQL that can return only a count. To make it more efficient, you would want to query just the id column and get the cursor row count.
Cursor.getCount()
Use Instances.CONTENT_BY_DAY_URI to get events of specific day. You can find an example of such query here - https://github.com/CyanogenMod/android_packages_apps_Calendar/blob/cm-12.0/src/com/android/calendar/Event.java#L307

Adding Events to Calendar without user confirmation

I'm trying to write a class to add events to a user's calendar in the background (not using intents). I need to ultimately be able to add as many as 7 events in a loop, so I can't do this with the approach where we turn control over to the calendar for the user to confirm.
Unfortunately, I'm not getting anywhere. The only feedback that I get from the logcat is a warning: Cursor finalized without prior close()
#TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public class AddToCalendar {
Context context;
// Projection array. Creating indices for this array instead of doing
// dynamic lookups improves performance.
public static final String[] EVENT_PROJECTION = new String[] {
Calendars._ID, // 0
Calendars.ACCOUNT_NAME, // 1
Calendars.CALENDAR_DISPLAY_NAME, // 2
Calendars.OWNER_ACCOUNT // 3
};
// The indices for the projection array above.
private static final int PROJECTION_ID_INDEX = 0;
private static final int PROJECTION_ACCOUNT_NAME_INDEX = 1;
private static final int PROJECTION_DISPLAY_NAME_INDEX = 2;
private static final int PROJECTION_OWNER_ACCOUNT_INDEX = 3;
public AddToCalendar (Context context) {
this.context = context;
}
public Cursor getCalendars() {
Cursor cursor = null;
ContentResolver cr = context.getContentResolver();
Uri uri = Calendars.CONTENT_URI;
String selection = "((" + Calendars.ACCOUNT_NAME + " = ?) AND ("
+ Calendars.ACCOUNT_TYPE + " = ?) AND ("
+ Calendars.OWNER_ACCOUNT + " = ?))";
String[] selectionArgs = new String[] {"sampleuser#gmail.com", "com.google",
"sampleuser#gmail.com"};
// Submit the query and get a Cursor object back.
cursor = cr.query(uri, EVENT_PROJECTION, selection, selectionArgs, null);
return cursor;
}
public void addEvent(Cursor cursor, String type, String location, String description, Date workout_date) {
System.err.println("adding event");
while (cursor.moveToNext()) {
String displayName = null;
String accountName = null;
String ownerName = null;
// Get the field values
long calID = 0;
calID = cursor.getLong(PROJECTION_ID_INDEX);
displayName = cursor.getString(PROJECTION_DISPLAY_NAME_INDEX);
accountName = cursor.getString(PROJECTION_ACCOUNT_NAME_INDEX);
ownerName = cursor.getString(PROJECTION_OWNER_ACCOUNT_INDEX);
System.err.printf("Calendar: %s\n", displayName);
long startMillis = 0;
long endMillis = 0;
Calendar beginTime = Calendar.getInstance();
beginTime.set(2014, 1, 26, 7, 30);
startMillis = beginTime.getTimeInMillis();
Calendar endTime = Calendar.getInstance();
endTime.set(2014, 1, 26, 8, 45);
endMillis = endTime.getTimeInMillis();
ContentResolver cr = context.getContentResolver();
ContentValues values = new ContentValues();
values.put(Events.DTSTART, startMillis);
values.put(Events.DTEND, endMillis);
values.put(Events.TITLE, type);
values.put(Events.EVENT_LOCATION, location);
values.put(Events.DESCRIPTION, description);
values.put(Events.CALENDAR_ID, calID);
values.put(Events.EVENT_TIMEZONE, "America/Los_Angeles");
cr.insert(Events.CONTENT_URI, values);
}
}
}
Most of this is pulled together from http://developer.android.com/guide/topics/providers/calendar-provider.html but it's just not working. Other than the warning that I mentioned, it all "appears" to work, except for the minor detail that nothing shows up on the calendar.
How do I write the getCalendars() query in a general way to get all of the calendars that might be on a user's device? Right now, you can see that I still have the language from the android site.
Any help is appreciated. Thank you.
EDIT: Andrew T pointed out that I have an off-by-one error since the months are zero-based, so I changed my hard coded date to be month 0. I also added a cursor.close() after my while loop. However, I still didn't have anything on my calendar.
One thing that I noticed was that I wasn't entering the while loop. So I took out the loop. When I hard-code the calID variable to 1, I finally get a calendar entry, but this concerns me. I thought that the point of getting the calendars was that a user might use multiple calendars on their device, and the cursor was a way of iterating over each of these calendars. Am I wrong about that?
If hardcoding calID to 1 will solve my problem, I'm fine with that, but I want to make sure that I understand this and am not relying on an ad hoc solution. Thanks again!
The warning is caused by not closing the Calendar Cursor after inserting the events. Call cursor.close() to close it.
Also, have you checked the events on February? I'm afraid you have off-by-1 issue with how month works in Java's Calendar. (Month starts with 0, which is Calendar.JANUARY)
EDIT :
It seems that there is a problem with getCalendars(), particularly on String selection = ... which returns empty Cursor. Also, with this approach, there is a possibility that the event will be added to multiple calendars, which might not be desired.
My idea is to create a ListActivity to list all calendars to let user chooses which calendar he wants to use. With this, you can also save the calendar ID to SharedPreference and use it when creating event conveniently.
I think you can use cursor = cr.query(uri, EVENT_PROJECTION, null, null, null); to list all calendars on the device.

Launching calendar to event ID from calendar contract event ID opens wrong event

I'm using the calendar contract to pull events from the DB.
First I get a list of the calendars and then I pull all the events in specific time for each calendar, it all works fine.
I use _id column name to get the event ID.
But when I try to get a pending event to open the calendar on that specific event, the calendar is opened on another event.
I've ran some tests to see if the ID is unique and it seems correct.
Here is the creating of the pendingIntent :
public static PendingIntent getEventPendingIntent(Context c, int eventID) {
Intent intent = new Intent(Intent.ACTION_VIEW);
Uri.Builder uri = Events.CONTENT_URI.buildUpon();
uri.appendPath(Long.toString(eventID));
intent.setData(uri.build());
return PendingIntent.getActivity(c, 0, intent, 0);
}
This is the method to get the events for a given CalendarID :
private static Cursor getEvents(String calendarID, ContentResolver contentResolver, long timeFromNow) {
// Create a builder to define the time span
Uri.Builder builder = Uri.parse("content://com.android.calendar/instances/when").buildUpon();
long now = new Date().getTime();
ContentUris.appendId(builder, now);
if (timeFromNow == -1) {
timeFromNow = DateUtils.DAY_IN_MILLIS * 10000;
}
ContentUris.appendId(builder, now + timeFromNow);
Cursor eventCursor = null;
// Create an event cursor to find all events in the calendar
String[] queryFields = {"title", "dtstart", "begin", "end", "allDay", "eventLocation","description", "_id" };
try {
eventCursor = contentResolver.query(builder.build(),
queryFields , "calendar_id=" + calendarID, null, "startDay ASC, startMinute ASC");
} catch (Exception e) {
Log.w(TAG, "ERROR: "+e.toString());
Log.w(TAG, "Didn't find calender for ID="+calendarID);
eventCursor = null;
}
return eventCursor;
}
The problem was with the ID columns, I used _id instead of event_id
After changing this, the correct events were shown.

Categories

Resources