Android Tutorial Working with Calendars using ContentResolver (Query, Insert, Update, Delete)

Discussion in 'Tutorials & Examples' started by DonManfred, Dec 9, 2018.

  1. DonManfred

    DonManfred Expert Licensed User

    Hello,

    i´m playing around with the Calendars on my Device and want to Query them. I found out how it works using ContentResolver querying the ContentProvider available in Android.

    You can find it documented here.

    I started using java code and a java-librarywrapper for this but then i realized that it is probably better to use the B4A ContentResolver instead of building a new one just for the Calendars. I then decided to do the same with the ContentResolver.
    As the CalendarProvider is using different Tables internally there are a lot of Constants used (for each table a Content-Uti is needed, and Constants for the Fields in the Table). All of them can be created in B4A easily too but i decided to have them in a small Library (just a small wrapper just for the Constants).

    - From now i will use "CR" for a short version of ContentResolver.

    The constants make it easy to Build a Query for CR, get the right ContentUri.

    Ok, let´s start with the basics to use Android CalendarProvider.

    - It depends on the Permission "android.permission.READ_CALENDAR" and "android.permission.WRITE_CALENDAR". As both of them are dangerous permissions you need to use Runtimepermission to request Permission for them and they need to be defined in the manifest.

    Code:
    AddManifestText(
    <uses-permission android:name=
    "android.permission.READ_CALENDAR"/>
    <uses-permission android:name=
    "android.permission.WRITE_CALENDAR"/>
    )
    .
    Code:
    Sub Process_Globals
        
    'These global variables will be declared once when the application starts.
        'These variables can be accessed from all modules.
        Dim ccon As CalendarConstants
        
    Dim econ As EventConstants
    End Sub
    Sub Globals
        
    'These global variables will be redeclared each time the activity is created.
        'These variables can only be accessed from this module.
        Dim gcal As GoogleCalendar
        
    Private cr As ContentResolver
        
    Private canAccessCal As Boolean
    End Sub
    Sub Activity_Create(FirstTime As Boolean)
        
    'Do not forget to load the layout file created with the visual designer. For example:
        canAccessCal = False
        
    Activity.LoadLayout("Layout1")
        Starter.rp.CheckAndRequest(
    "android.permission.READ_CALENDAR")
        
    wait for Activity_PermissionResult (Permission As String, Result As Boolean)
        
    If Result = False Then
            
    Log("NO Permission READ Calendar")
        
    Else
            Starter.rp.CheckAndRequest(
    "android.permission.WRITE_CALENDAR")
            
    wait for Activity_PermissionResult (Permission As String, Result As Boolean)
            
    If Result Then
                canAccessCal = 
    True
                cr.Initialize(
    "CR")
            
    End If
        
    End If
        
    If canAccessCal Then
                   
    ' We are working from here now...
            END IF
    END sub
    If you want to Query something your need to define a "Projection". In fact ou are defining the Fields you want to query.

    We want to get a list of ID, Name, DisplayNAme, AccountType and the OwnerAccount from the Calendars.
    Code:
    Dim projection() As String = Array As String(ccon.ID,ccon.NAME,ccon.CALENDAR_DISPLAY_NAME,ccon.ACCOUNT_NAME,ccon.ACCOUNT_TYPE,ccon.OWNER_ACCOUNT)
    The ID you get is the ID from Androids Calendar.
    as we are requesting Calendars we need to use the ContentUri from the CalendarsConstants object when Querying with CR.

    ccon, econ are two Objects which only contains the Constant. Each of them do have a CONTENT_URI and some other Values. ccon have Constants for Calendars. econ have Constants for CalendarEvents.

    We set the "Selection" to match all visible Calendars.
    We order them by ID Ascending.
    Code:
    cr.QueryAsync(ccon.CONTENT_URI,projection, ccon.VISIBLE&"=1",Null,ccon.ID&" ASC")
            
    wait for CR_QueryCompleted(Success As Boolean, Crsr As Cursor)
            
    Log($"QueryCompleted(${Success})"$)
            
    If Crsr.IsInitialized Then
                
    If Crsr.RowCount > 0 Then
                    
    Dim Cursor As Cursor
                    
    Cursor = Crsr
                    
    For i = 0 To Cursor.RowCount - 1
                        
    Cursor.Position = i
                        
    'calcon.ACCOUNT_TYPE,calcon.OWNER_ACCOUNT)
                        Log($"CR --------------------------------------"$)
                        
    Log(Cursor.GetString(ccon.ID)) ' You can use the constants you used for the projection. But remember: only the ones you defined in the projection are available in the Result.
                        Log(Cursor.GetString(ccon.NAME))
                        
    Log(Cursor.GetString(ccon.CALENDAR_DISPLAY_NAME))
                        
    Log(Cursor.GetString(ccon.ACCOUNT_NAME))
                        
    Log(Cursor.GetString(ccon.ACCOUNT_TYPE))
                        
    Log(Cursor.GetString(ccon.OWNER_ACCOUNT))
                    
    Next
                    
    Cursor.Close
                
    Else
                    
    ' No rows
                End If
            
    Else
                
    '
            End If
    The constants used in the Example here can be found in the Attached small Library.

    I´ll add more Examples to this Thread. As of now i´m ivestigating/play around with the Calendars. I do not know how to delete or edit a Event but i´ll find out and Update here.

    I just want to share my findings and i think they may be useful for you too.

    Related Examples:
    - Reading Events from a Calendar
    - More Advanced reading from a Calendar
    - Adding an Event in a Calendar
    - Some notes about Calendars AccountType.
    - Deleting an Event.
    - Updating an Event.
    - Adding an Attendee.
    - Get all Reminders for an specific Event.
     

    Attached Files:

    Last edited: Jan 26, 2019
    KMatle, AnandGupta, JordiCP and 12 others like this.
  2. DonManfred

    DonManfred Expert Licensed User

    Reading Events from a Calendar.

    Event constants can be found in the
    Code:
    Dim econ As EventConstants
    Here we are reading all visible Events which blongs to the Calendar ID 1 (we got the ID in the Step above).

    Code:
    Dim projection() As String = Array As String(econ.ID,econ.ALL_DAY,econ.AVAILABILITY,econ.CALENDAR_ACCESS_LEVEL,econ.CALENDAR_ID,econ.DESCRIPTION,econ.DTEND,econ.DTSTART,econ.DURATION,econ.LOCATION,econ.ORGANIZER,econ.TITLE)
        cr.QueryAsync(econ.CONTENT_URI,
    projection, econ.VISIBLE&"=1 AND "&econ.CALENDAR_ID=1",Null,econ.ID&" ASC")
        wait for CR_QueryCompleted(Success As Boolean, Crsr As Cursor)
        
    Log($"QueryCompleted(${Success})"$)
        
    If Crsr.IsInitialized Then
            
    If Crsr.RowCount > 0 Then
                
    Dim Cursor As Cursor
                
    Cursor = Crsr
                
    For i = 0 To Cursor.RowCount - 1
                    
    Cursor.Position = i
                    
    Log($"CR --------------------------------------"$)
                   
    Log($"${Cursor.GetString(econ.ID)}: ${Cursor.GetString(econ.TITLE)} / ${Cursor.GetString(econ.LOCATION)} / ${Cursor.GetString(econ.DESCRIPTION)}"$)
                
    Next
                
    Cursor.Close
            
    Else
                
    ' No rows
            End If
        
    Else
            
    '
        End If
     
  3. DonManfred

    DonManfred Expert Licensed User

    More advanced query for Events from a given Calendar

    Code:
    Dim projection() As String = Array As String(econ.ID,econ.ALL_DAY,econ.AVAILABILITY,econ.CALENDAR_ACCESS_LEVEL,econ.CALENDAR_ID,econ.DESCRIPTION,econ.DTEND,econ.DTSTART,econ.DURATION,econ.LOCATION,econ.ORGANIZER,econ.TITLE)
        
    Dim selection As String = $"${econ.VISIBLE}=? AND ${econ.CALENDAR_ID}=?"$
        selection = 
    $"((${econ.CALENDAR_ID} = ?) AND (((${econ.DTSTART}>= ?) AND (${econ.DTSTART}<= ?) AND (${econ.ALL_DAY}= ?) ) OR ((${econ.DTSTART}= ?) AND (${econ.ALL_DAY}= ?))))"$

        
    Dim selectionArgs() As String = Array As String(1,FromTime,EndTime,0,FromTime,1)
        cr.QueryAsync(econ.CONTENT_URI,
    projection, selection,selectionArgs,econ.ID&" ASC")
        
    wait for CR_QueryCompleted(Success As Boolean, Crsr As Cursor)
        
    Log($"QueryCompleted(${Success})"$)
        
    If Crsr.IsInitialized Then
            
    If Crsr.RowCount > 0 Then
                
    Dim Cursor As Cursor
                
    Cursor = Crsr
                
    For i = 0 To Cursor.RowCount - 1
                    
    Cursor.Position = i
                    
    'calcon.ACCOUNT_TYPE,calcon.OWNER_ACCOUNT)
                    Log($"CR --------------------------------------"$)
                    
    ',econ.ALL_DAY,econ.AVAILABILITY,econ.CALENDAR_ACCESS_LEVEL,econ.CALENDAR_ID,econ.DESCRIPTION,econ.DTEND,econ.DTSTART,econ.DURATION,econ.LOCATION,econ.ORGANIZER
                    Log($"${Cursor.GetString(econ.ID)}: $date{Cursor.GetString(econ.DTSTART)} $time{Cursor.GetString(econ.DTSTART)}  ${Cursor.GetString(econ.TITLE)} / ${Cursor.GetString(econ.LOCATION)} / ${Cursor.GetString(econ.DESCRIPTION)}"$)
                
    Next
                
    Cursor.Close
            
    Else
                
    ' No rows
            End If
        
    Else
            
    '
        End If
     
    Last edited: Dec 9, 2018
  4. DonManfred

    DonManfred Expert Licensed User

    Creating an Event in a specific Calendar:
    Please note that the Calendar used here has an AccountType of "com.google".

    Code:
    Dim FromTime As Long = DateTime.DateTimeParse("12/19/2018","08:00:00")
        
    Dim EndTime As Long = DateTime.DateTimeParse("12/19/2018","09:30:00")

        
    Dim val As ContentValues
        val.Initialize
        val.PutLong(econ.DTSTART,FromTime)
        val.PutLong(econ.DTEND,EndTime)
        val.PutString(econ.TITLE,
    "SomeTitle")
        val.PutString(econ.LOCATION,
    "Düren")
        val.PutLong(econ.CALENDAR_ID,
    18)
        val.PutString(econ.EVENT_TIMEZONE,
    "Europe/Berlin")
        val.PutString(econ.DESCRIPTION,
    "Some description")
        
    Dim resUri As Uri = cr.Insert(econ.CONTENT_URI,val)
        
    Dim eventID As Int = resUri.ParseId
        
    Log($"EventID of created Event: ${eventID}"$)
    Here a screenshot from the Google Calendar Website (the event is automatically synched with google servers. The Calendar must be subscribed in the Calendar app and enabled for Sync. And the Calendar must have a AccountType of com.google!
    [​IMG]
     
    Last edited: Dec 12, 2018
    asales, Erel, BillMeyer and 2 others like this.
  5. Peter Simpson

    Peter Simpson Expert Licensed User

    DonManfred and BillMeyer like this.
  6. BillMeyer

    BillMeyer Well-Known Member Licensed User

    Keep going Manfred. I like, I like, I like....
     
    DonManfred likes this.
  7. DonManfred

    DonManfred Expert Licensed User

    Some notes about Calendars.

    Each Calendar - we have access to - has an Property AccountType. The AccountType gives infos about where does the Calendar belongs to.

    AccountType(s) i found so far on my Device:
    - LOCAL
    Changes in a Local Calendar only affects the Devices Calendar. Events in this Calendar are not Synced.

    - COM.GOOGLE
    Account is linked to Google. Changes in Calendars of this Type are Synced with Google. The Calendar i used above is a com.google Calendar.
    Note that the Calendar must be defined and activated for Sync in the Google Calendar App.

    - COM.SAMSUNG.ANDROID.EXCHANGE
    Account is linked to Samsung Account (and Samsung Calendar). It is probably syned with Samsung (don´t know).
    I can not say anything about this CalendarType. I do own a Samsung Account (and probably a Calendar) but i do not use it. I only use Google Calendars.
     
    Last edited: Dec 12, 2018
  8. DonManfred

    DonManfred Expert Licensed User

    Deleting an Event.

    Code:
    dim Value as int = 1234 ' EventID
        Dim selectionArgs() As String = Array As String(Value)
        
    Dim deleted As Int = cr.Delete(econ.CONTENT_URI,econ.ID&"=?",selectionArgs)
        
    Log($"${deleted} Events were deleted"$)
     
    Last edited: Dec 11, 2018
  9. DonManfred

    DonManfred Expert Licensed User

    Updating an Event.
    Using the EventID you just can fill a ContentValues with all Values you want to change.

    Code:
    dim Value as int = 1334 ' EventID
        Dim selectionArgs() As String = Array As String(Value)
        
    Dim val As ContentValues
        val.Initialize
        val.PutString(econ.TITLE,
    "Some new Title")
        val.PutString(econ.LOCATION,
    "New Location")
        
    Dim updated As Int =cr.Update(econ.CONTENT_URI,val,econ.ID&"=?",selectionArgs)
        
    Log($"${updated} Events were updated"$)
     
    Last edited: Dec 11, 2018
  10. DonManfred

    DonManfred Expert Licensed User

    Adding an Attendee:

    Adding a Attendee is using the Attendeeconstants.
    Code:
    Dim acon As AttendeeConstants

    Code:
    Dim val As ContentValues
        
    Dim event As Int = 1234 ' EventID
        val.Initialize
        val.PutString(acon.EVENT_ID, 
    event)
        val.PutInteger(acon.ATTENDEE_TYPE, acon.TYPE_REQUIRED)
        val.PutString(acon.ATTENDEE_NAME, 
    "Zaphod Beeblebrox")
        val.PutString(acon.ATTENDEE_EMAIL, 
    "d.adams@zaphod-b.com")
        
    Dim attendee As Uri =cr.Insert(acon.CONTENT_URI,val)
        
    Dim attendeeID As Int = attendee.ParseId
        
    Log($"attendeeID: ${attendeeID}"$)
    Note that Google send out a Invitationemail automatically to the given Attendee in which he can confirm the Event.

    Also note that this only applies to Calendars which AccountType is of com.google.
    It may not happen in a Local or a Samsung Calendar.
     
    Last edited: Dec 12, 2018
    Erel likes this.
  11. DonManfred

    DonManfred Expert Licensed User

    Getting all Reminders for a specific Event.

    Code:
    Dim rcon As RemindersConstants ' Library from the Tutorial needed
        Dim event As Int = 12345 ' Eventid of which you want to get the Reminders
     
        
    Dim projection() As String = Array As String(rcon.ID, rcon.EVENT_ID ,rcon.METHOD, rcon.MINUTES)
        
    Dim selection As String = $"${rcon.EVENT_ID}=?"$
        
    Dim selectionArgs() As String = Array As String(event)
        cr.QueryAsync(rcon.CONTENT_URI,
    projection, selection,selectionArgs,rcon.ID&" ASC")
        
    wait for CR_QueryCompleted(Success As Boolean, Crsr As Cursor)
        
    Log($"QueryCompleted(${Success})"$)
        
    If Success = False Then
            
    Log(LastException)
        
    End If
        
    If Crsr.IsInitialized Then
            
    If Crsr.RowCount > 0 Then
                
    Dim crsrreminder As Cursor
                crsrreminder = Crsr
                
    For i = 0 To crsrreminder.RowCount - 1
                    crsrreminder.Position = i
                    
    Dim methodid As Int = crsrreminder.GetInt(rcon.METHOD)
                    
    Dim method As String = "unknown"
                    
    Select methodid
                        
    Case 1
                            method = 
    "Notify"
                        
    Case 2
                            method = 
    "eMail"
                    
    End Select
                    
    Log($"ID: ${crsrreminder.GetString(rcon.ID)} / Method: ${method} / Minutes: ${crsrreminder.GetString(rcon.MINUTES)}"$)
                
    Next
                crsrreminder.Close
            
    Else
                
    ' No rows
            End If
        
    Else
            
    '
        End If
     
    jimmyF and Erel like this.
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice