Jason
Jason

Reputation: 2605

How do you get a list of accounts on Android that have calendars?

I am writing an app for Android that creates a series of events in a calendar for the user where normal recurrence just can't handle it. I want to allow the user to choose which account they want to use for the calendar (primarily for sharing the calendar later).

Using AccountManager, I can get all accounts, but not all of them have calendars. Google accounts would technically work, but I want to use Exchange accounts or others that also have calendars.

All of the AccountManager API docs I've read say that features to search for are authenticator specific (and can change). Keeping a list of those would be near impossible. Limiting to a list of known account types is more limiting than I really want.

Are there any other options I missed?

Upvotes: 2

Views: 2101

Answers (2)

Ahmed Hegazy
Ahmed Hegazy

Reputation: 12605

Models

import java.util.ArrayList;
import java.util.List;

public class CalendarAccount {

    private String name;
    private List<Calendar> calendars;

    public CalendarAccount(String name) {
        this(name, new ArrayList<Calendar>());
    }

    public CalendarAccount(String name, List<Calendar> calendars) {
        this.name = name;
        this.calendars = calendars;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Calendar> getCalendars() {
        return calendars;
    }

    public void setCalendars(List<Calendar> calendars) {
        this.calendars = calendars;
    }
}

import android.support.annotation.ColorInt;

public class Calendar {

    private long id;
    private String displayName;
    @ColorInt private int color;
    private boolean userActivated;

    public Calendar(long id, String displayName, @ColorInt int color) {
        this(id, displayName, color, false);
    }

    public Calendar(long id, String displayName, int color, boolean userActivated) {
        this.id = id;
        this.displayName = displayName;
        this.color = color;
        this.userActivated = userActivated;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getDisplayName() {
        return displayName;
    }

    public void setDisplayName(String displayName) {
        this.displayName = displayName;
    }

    public int getColor() {
        return color;
    }

    public void setColor(int color) {
        this.color = color;
    }

    public boolean isUserActivated() {
        return userActivated;
    }

    public void setUserActivated(boolean userActivated) {
        this.userActivated = userActivated;
    }
}

Actual code using the Calendar Provider

public static final String[] EVENT_PROJECTION = new String[] {
        Calendars._ID,                           // 0
        Calendars.ACCOUNT_NAME,                  // 1
        Calendars.CALENDAR_DISPLAY_NAME,         // 2
        Calendars.OWNER_ACCOUNT,                 // 3
        Calendars.CALENDAR_COLOR                 // 4
};

// 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;
private static final int PROJECTION_COLOR_INDEX = 4;


private List<CalendarAccount> getAccountCalendars() {
    // Run query
    Cursor cur;
    ContentResolver cr = context.getContentResolver();
    Uri uri = Calendars.CONTENT_URI;
    String selection = "(" + Calendars.VISIBLE + " = ?)";
    String[] selectionArgs = new String[] {"1"};
    // Submit the query and get a Cursor object back.
    cur = cr.query(uri, EVENT_PROJECTION, selection, selectionArgs, null);
    List<CalendarAccount> calendarAccounts = new ArrayList<>();
    // Use the cursor to step through the returned records
    while (cur.moveToNext()) {
        long calID = 0;
        String displayName = null;
        String accountName = null;
        String ownerName = null;
        @ColorInt int color = 0;

        // Get the field values
        calID = cur.getLong(PROJECTION_ID_INDEX);
        displayName = cur.getString(PROJECTION_DISPLAY_NAME_INDEX);
        accountName = cur.getString(PROJECTION_ACCOUNT_NAME_INDEX);
        ownerName = cur.getString(PROJECTION_OWNER_ACCOUNT_INDEX);
        color = cur.getInt(PROJECTION_COLOR_INDEX);

        Calendar calendar = new Calendar(calID, displayName, color);
        CalendarAccount calendarAccount = new CalendarAccount(accountName);
        int index = calendarAccounts.indexOf(calendarAccount);
        if (index != -1) {
            calendarAccount = calendarAccounts.get(index);
        } else {
            calendarAccounts.add(calendarAccount);
        }
        calendarAccount.getCalendars().add(calendar);
    }
    return calendarAccounts;
}

Upvotes: 1

Jason
Jason

Reputation: 2605

Contrary to my initial beliefs, you do not actually need to touch the account list at all. This was helpful in simplifying the process, and in reducing the number of required permissions.

Using the information from this page (under Querying a Calendar), you can write a query to the Calendars table and get a full list of Calendars and the Account they belong to.

In the end, this approach is going to be much more useful to me. Basically, the EVENT_PROJECTION is changed as below from what the example shows.

public static final String[] EVENT_PROJECTION = new String[] {
    Calendars._ID,
    Calendars.ACCOUNT_NAME,
    Calendars.CALENDAR_DISPLAY_NAME,
    Calendars.ACCOUNT_TYPE,
    Calendars.CALENDAR_ACCESS_LEVEL,
    Calendars.IS_PRIMARY
};

ACCOUNT_TYPE, CALENDAR_ACCESS_LEVEL and IS_PRIMARY were added so that we know a) which calendar is primary for the account for sorting later, and b) whether or not the user can actually write to it, because we don't want it if it's read-only.

Also, in the actual query of the calendars table, I made the following changes.

// Run query
Cursor cur = null;
ContentResolver cr = getContentResolver();
Uri uri = Calendars.CONTENT_URI;
// Submit the query and get a Cursor object back.
cur = cr.query(uri, EVENT_PROJECTION, "", null, null);

Since we want all the calendars to start off, the selection and selectionArgs variables in the example are useless.

Up until we get the final result of the query, basically everything else in the example remains the same.

Once we have the data, it can be parsed and put into a List, HashMap, etc. sorted and filtered as needed.

Upvotes: 2

Related Questions