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 = "[email protected]";
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????
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("[email protected]")
.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:
Share you calendarIn the Google Developers console, go to IAM & Admin > Service Accounts and copy your Service Account ID email. Afterwards, go to your Google Calendar, and select Calendar Settings for the calendar you want to have access to. Then go to the Share this Calendar tab.
Background. A service account is a special type of Google account intended to represent a non-human user that needs to authenticate and be authorized to access data in Google APIs. Typically, service accounts are used in scenarios such as: Running workloads on virtual machines (VMs).
Create your service accountSign in to the Google API Console. Open the Credentials page. If prompted, select the project that has the Android Management API enabled. Click Create credentials > Service account key.
Once the other Google account is added, open the “Google Play” app. button located at the upper-left corner of he screen. Select the small arrow next to your google account, then choose the other account listed. > “My apps” to download and install the apps that are purchased under the other account.
I'm not an Android expert, but either of the following approach should work for your use case.
Your approach: Using Service Account, will avoid having the user authenticating to your application. You may want to read about 'Delegating domain-wide authority', this will allow your application to make API calls as the user in your domain (also known as 'impersonate' users). I also found this this SO to be helpful.
Another approach: Using the Acl.Insert resource. This will require user authentication every time they log-in to your application. Here is an example on how to implement this.
From your sample source above, set your scopes to Calendar scope instead of SQL Admin, looks something like this:
GoogleCredential credential = new GoogleCredential.Builder()
...
.setServiceAccountScopes(CalendarScopes.CALENDAR)
.setServiceAccountPrivateKeyFromP12File(xxxx)
.build();
Hope this helps and good luck!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With