I think the answer is YES but I'm not sure how to do it. I've been using the API to get events from calendars stored on the device. e.g.
public static ArrayList<MINCalendarEvent> queryEvents(long startMillis, long endMillis) throws MINPermissionException
{
if ( ContextCompat.checkSelfPermission(MINMainActivity.getSharedInstance(), Manifest.permission.WRITE_CALENDAR) != PackageManager.PERMISSION_GRANTED )
{
throw new MINPermissionException(NO_PERMISSION);
}
else
{
ArrayList<MINCalendarEvent> eventArray = new ArrayList<MINCalendarEvent>();
Cursor cur = CalendarContract.Instances.query(MINMainActivity.getSharedInstance().getContentResolver(), EVENT_PROJECTION, startMillis, endMillis);
int numItems = cur.getCount();
Log.d("MINCalendarUtils.queryEvents", "Number of events retrieved: " + numItems);
while (cur.moveToNext())
{
MINCalendarEvent event = new MINCalendarEvent();
event.calendarID = cur.getLong(EVENT_PROJECTION_CALENDAR_ID);
event.organizer = cur.getString(EVENT_PROJECTION_ORGANIZER);
event.title = cur.getString(EVENT_PROJECTION_TITLE);
event.eventLocation = cur.getString(EVENT_PROJECTION_EVENT_LOCATION);
event.description = cur.getString(EVENT_PROJECTION_DESCRIPTION);
event.dtStart = cur.getLong(EVENT_PROJECTION_DTSTART);
event.dtEnd = cur.getLong(EVENT_PROJECTION_DTEND);
event.eventTimeZone = cur.getString(EVENT_PROJECTION_EVENT_TIMEZONE);
event.eventEndTimeZone = cur.getString(EVENT_PROJECTION_EVENT_END_TIMEZONE);
event.duration = cur.getString(EVENT_PROJECTION_DURATION);
event.allDay = (cur.getInt(EVENT_PROJECTION_ALL_DAY) != 0);
event.rRule = cur.getString(EVENT_PROJECTION_RRULE);
event.rDate = cur.getString(EVENT_PROJECTION_RDATE);
event.availability = cur.getInt(EVENT_PROJECTION_AVAILABILITY);
event.guestsCanModify = (cur.getInt(EVENT_PROJECTION_GUESTS_CAN_MODIFY) != 0);
event.guestsCanInviteOthers = (cur.getInt(EVENT_PROJECTION_GUESTS_CAN_INVITE_OTHERS) != 0);
event.guestsCanSeeGuests = (cur.getInt(EVENT_PROJECTION_GUESTS_CAN_SEE_GUESTS) != 0);
eventArray.add(event);
}
return eventArray;
}
}
This works great. The problem is that I need to query a shared calendar stored on the server which the user DOES NOT have rights too. My app creates a LOCAL calendar. I need to access a shared that the user WILL NOT have rights too and synchronize the events stored in the shared calendar with the LOCAL calendar (non-synchronized). I'm assuming that I can use a service account to access the shared calendar.
I've successfully created a service account and added the account to the shared calendar. Now what????
There seems to be several ways of accessing calendar events using a service account but I'm totally confused. Obviously, I'd like to use the API I've been using but I think it only works with calendars that are synchronized with the device.
I've investigated using "GoogleCredentials" but I need some sample source code to make this work. 1st of all, when I created the service account, I exported it using JSON and not p12. I don't see how to use it. The API seems to require p12. I'm also totally confused on how to use the credentials once I have them. Here's the sample source I started with:
//String emailAddress = "123456789000-abc123def456@developer.gserviceaccount.com";
GoogleCredential credential = null;
JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
HttpTransport httpTransport = null;
try
{
httpTransport = GoogleNetHttpTransport.newTrustedTransport();
credential = new GoogleCredential.Builder()
.setTransport(httpTransport)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId(emailAddress)
.setServiceAccountPrivateKeyFromP12File(new File("MyProject.p12"))
.setServiceAccountScopes(Collections.singleton(SQLAdminScopes.SQLSERVICE_ADMIN))
.build();
}
catch (GeneralSecurityException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
There are several problems. 1) I can't event get this to compile. I can't seem to get the imports to work:
import com.google.api.services.sqladmin.SQLAdminScopes;
Assuming I can get past that, now what. I'm not sure how to use the credentials to access the remote calendar. What I need is to get a list of events from the shared calendar.
I've also been looking through the source code at the following link for directions, but it doesn't use a service account: https://developers.google.com/google-apps/calendar/quickstart/android
Also, is there a way to hook changes to the server based shared calendar so that I get pinged when changes occur to the shared calendar?
Any help????
EDIT - Working Code
Based on the answer from Andres, I've put together the following code:
public static void calendarAuthenticate()
{
Thread thread = new Thread(new Runnable()
{
@Override
public void run()
{
try
{
MINAppConfiguration appConfig = MINAppConfiguration.getSharedInstance();
// Application Name
appName = appConfig.getCurrentAppInfo().appName;
// Directory to store user credentials for this application;
//dataStoreDir = new File(appConfig.appDirectoryOnDevice);
// Instance of the JSON factory
jsonFactory = JacksonFactory.getDefaultInstance();
// Instance of the scopes required
scopes = new ArrayList<String>();
scopes.add(CalendarScopes.CALENDAR_READONLY);
// Http transport creation
httpTransport = AndroidHttp.newCompatibleTransport();
java.io.File licenseFile = getSecretFile();
GoogleCredential credential = new GoogleCredential.Builder()
.setTransport(httpTransport)
.setJsonFactory(jsonFactory)
.setServiceAccountId("account-1@handy-contact-762.iam.gserviceaccount.com")
.setServiceAccountScopes(scopes)
.setServiceAccountPrivateKeyFromP12File(licenseFile)
.build();
com.google.api.services.calendar.Calendar.Builder builder = new com.google.api.services.calendar.Calendar.Builder(httpTransport, jsonFactory, credential);
builder.setApplicationName(appName);
com.google.api.services.calendar.Calendar client = builder.build();
// List the next 10 events from the target calendar.
DateTime now = new DateTime(System.currentTimeMillis());
com.google.api.services.calendar.Calendar.Events.List list = client.events().list("mobilityinitiative.com_qfvbdrk368f9la06hacb4bduos@group.calendar.google.com");
list.setMaxAttendees(10);
list.setTimeMin(now);
list.setOrderBy("startTime");
list.setSingleEvents(true);
Events events = list.execute();
List<Event> items = events.getItems();
if (items.size() == 0)
{
Log.d(TAG, "No upcoming events found.");
}
else
{
Log.d(TAG, "Upcoming events");
for (Event event : items) {
DateTime start = event.getStart().getDateTime();
if (start == null) {
start = event.getStart().getDate();
}
Log.d(TAG, event.getSummary() + " (" + start + ")\n");
}
}
}
catch (GeneralSecurityException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
}
});
thread.start();
}
public static java.io.File getSecretFile()
{
File f = new File(MINMainActivity.getSharedInstance().getCacheDir()+ "/" +"google_calendar_secret.p12");
if (f.exists())
{
f.delete();
}
try
{
InputStream is = MINMainActivity.getSharedInstance().getAssets().open("google_calendar_secret.p12");
int size = is.available();
byte[] buffer = new byte[size];
is.read(buffer);
is.close();
FileOutputStream fos = new FileOutputStream(f);
fos.write(buffer);
fos.close();
}
catch (Exception e)
{
throw new RuntimeException(e);
}
return f;
}
A couple of notes:
- Make sure you create the service account correctly and that it has the appropriate rights: https://developers.google.com/identity/protocols/OAuth2ServiceAccount#delegatingauthority
- Make sure you're making the call in a background thread. If you try to make the call using the UI thread, it will fail.
- Make sure you add items to the calendar so you know it's working :)