Android Calendar API


    This article will talk about using the undocumented API to work with the standard Android calendar. I did not find articles in Russian on this topic, and in English there are only a few posts with outdated data. Who is interested - under cat.

    What for!?


    I faced such a problem when I wrote an application for compiling my university schedule. It is convenient to have your schedule separately, and even the standard calendar does not support repeating events after one week, which is necessary for a two-week (odd / even) schedule.
    The idea of ​​the fix was an application function that allows you to “fill in” the Android calendar with the schedule you entered. The advantages are obvious: synchronization with Google Calendar from google (sorry for the tautology), built-in calendar widgets (this widget is very good from HTC Sense) and a mountain of widgets from third-party manufacturers that will at least show the next event, at least a busy week, etc. Here and needed to work with the Android calendar.

    Using an undocumented API is BAD! Pnatnenko?



    Is it really necessary to use an undocumented API to solve this obvious problem? The answer is no. The most correct method is to use the Google Calendar API , which I advise you to do in your development.

    But the “correct” method imposes a number of limitations:
    • It cannot be used in the absence of an Internet connection;
    • Need synchronization after filling out the calendar;
    • Data (and it’s not a lot when filling out the whole year) goes to the server and then goes back during synchronization, which, obviously, doubles the traffic.

    In my opinion, it is much more convenient to use the offline version of the calendar, which at any opportunity can be synchronized with the Google calendar. Unfortunately google did not take care of Android developers and did not publish the official API, but to our joy Android source codes are open and smart people have long found magic URI content providers.

    We ask the user for permission to work with the calendar



    Paradox. There is no API, but there is permission in the documentation. You only need to add them to your AndroidManifest.xml .


    Unified Resource Identifier or in Russian - URI


    To use the Android calendar, we must contact its content provider at a specific URI. To our chagrin, this URI differs in different versions of the OS and may very well change in the following (costs of using an undocumented API). But the problem is solvable.
    For Froyo [2.2] and with a high probability for the next versions - content: //com.android.calendar/calendars .
    For versions <2.2 - content: // calendar / calendars .
    As you can see, everything is not so bad. You can determine the version of the system and use the appropriate URI, but I went the other way. I see if there is anything by URI for <2.2 and if there is nobody there, then I use the rest.

    // Calendar URI before 2.2
    Uri calendars = Uri.parse("content://calendar/calendars");
    Cursor managedCursor = this.managedQuery(calendars, new String[] { "_id", "name" }, null, null, null);
    if(managedCursor == null || managedCursor.getCount() == 0)
    {
    	// 2.2 (Froyo)
    }
    else
    {
    	// < 2.2
    }

    We assume that the URI received.

    Get a list of calendars


    For Google Calendar users, you don’t need to explain, but for the rest I’ll explain. A shared calendar consists of one or more smaller calendars, which may vary in color with the markers. Usually this is something like “personal”, “work”, “study”, etc. You can create such calendars only on the Google Calendar website, in the android itself you can’t do this with standard tools.
    To add events, we need to know the calendar ID. To accomplish this task, we use the construction from the previous paragraph.

    String[] projection = new String[] { "_id", "name" };
    Cursor managedCursor = this.managedQuery(URI, projection, null, null, null);
    if (managedCursor != null && managedCursor.moveToFirst()) 
            {
            	String calName;
            	String calID;        	
            	int nameColumn = managedCursor.getColumnIndex("name");
            	int idColumn = managedCursor.getColumnIndex("_id");
            	do 
            	{
            		calName = managedCursor.getString(nameColumn);
            		calID = managedCursor.getString(idColumn);
            		if (calName != null) // … UI     			 
               	} while (managedCursor.moveToNext());
                managedCursor.close();
            }
    

    Now the resulting set of names can be displayed on the interface and allow the user to select.

    Add event


    Here, work with the calendar occurs, as with any other content provider. This can be found in the documentation .

    ContentValues event = new ContentValues();
    event.put("calendar_id", calID);  // ID календаря мы получили ранее
    event.put("title", title); // Название события
    event.put("description", desc); // Описание события
    event.put("eventLocation", loc); // Место проведения
    event.put("dtstart", start); // время начала
    event.put("dtend", end); // время окончания
    eventsURI=Uri.parse(URI + “/events"); 
    this.getContentResolver().insert(eventsURI, event);
    

    Let's talk about the event fields. I recommend using the description field to indicate an event. Using this field, then it will be possible to delete all events created by your program in bulk. Fields of the name, description and venue are string. Separately, it is worth mentioning about the time fields. These fields are of type long, and the values ​​of these fields can be generated, for example, by the following function.

    public static long pickDate(int year, int month, int day, int hour, int minute) 
    {
        	Calendar rightNow = Calendar.getInstance();
        	int timeShift = rightNow.get(Calendar.ZONE_OFFSET);    	
        	return Date.UTC(year - 1900, month, day, hour - (timeShift / 3600000), minute, 0);
    }
    

    I think you’ll figure it out for yourself, I’ll only note that it takes into account the current time zone.

    Delete event


    To delete a message by ID, you must add it to the URI.

    //eventsURI = URI + events/
    uri = ContentUris.withAppendedId(eventsURI, EVENT_ID);
    this.getContentResolver().delete(uri, null, null);	
    

    To delete by other fields, you can use the cursor to pull out all events, as we did with calendars, and then selectively delete by ID.

    Conclusion


    In conclusion, I remind you once again that you use this at your own peril and risk. During the development, I wrote the URI once incorrectly and every time during synchronization my phone started to generate an error (corrected by erasing the information from the calendar at all).
    Another nuisance - there is no calendar in the emulator, therefore, it will not be possible to repeat all of the above.
    And in conclusion. I can’t help but take advantage of the opportunity to promote my application, which was discussed in the article. You can find it in a market called uTimetable .




    And in conclusion, I would like to ask habrastudents: What format do you have at your university (one / two weeks) and what are these weeks called if they are two - even / odd or different? Also interested in foreign universities.

    Also popular now: