Google Calendar è probabilmente uno dei servizi di Mountain View più utilizzati al mondo, non solo dall’utenza Android. Si tratta di uno dei primi strumenti che ha permesso a chiunque – anche se dotato di capacità informatiche praticamente nulle – di memorizzare in maniera dettagliata i propri impegni, esperienze e attività in una banca dati remota, sicura e utilizzabile in qualunque luogo.
Non a caso, una delle curiosità che anima i programmatori Android, ad ogni livello di esperienza, è proprio apprendere come integrare una semplice app Android con questo servizio in modo da renderlo un vero e proprio backend finalizzato allo scopo.
In questo articolo vedremo come farlo, ne capiremo gli strumenti fondamentali e ne otterremo una dimostrazione con un semplice esempio.
Primo passo: impostiamo un progetto Google
Molti concetti che incontreremo nel corso del tutorial rappresentano i fondamenti comuni per l’accesso a tutti i servizi Google. Uno di questi è la configurazione di un “progetto”, la controparte remota con cui la nostra app dialogherà. Questo deve essere fatto all’interno di un pannello web chiamato Google Developers Console cui potremo accedere con un normalissimo account Gmail che probabilmente già stiamo usando da tempo per la posta elettronica.
L’interfaccia che ci ospiterà serve a gestire l’insieme dei progetti Google che fanno da backend alle nostre applicazioni e se saremo al nostro primo accesso alla console probabilmente sarà vuoto. Creeremo il progetto tramite il pulsanse Create Project e la finestra di dialogo che si aprirà non chiederà altro che il nome da assegnargli. Tale identificativo è esclusivamente per uso “interno” alla console pertanto possiamo sceglierlo liberamente purchè abbia per noi un significato preciso.
Fatto ciò avremo accesso direttamente ad un pannello in cui potremo gestire le risorse che fanno parte del singolo progetto. Abbiamo bisogno di fare due cose qui:
- abilitare le API per Google Calendar;
- definire una chiave di accesso che permetterà alla nostra app il dialogo.
Per abilitare le API, apriremo una sezione denominata API Manager e cercheremo, come mostrato in figura, le Calendar API. Una volta trovate sarà sufficiente cliccarvi sopra e poi il tasto Enable API che apparirà immediatamente.
Per creare le chiavi di accesso apriremo il pannello Credentials dal menu laterale sinistro e chiederemo di avere un OAuth Client ID che fornirà un tipo di autenticazione in grado di concedere l’accesso agli account Google.
Osservando la figura vediamo che i dati da fornire sono:
- il tipo di client ID che desideriamo: ovviamente, nel nostro caso sarà di tipo Android;
- una chiave SHA1 che come spiegato nel pannello stesso potrà essere richiesta con il comando keytool fornendo come password android. L’output del programma conterrà una chiave etichettata con il nome SHA1 e quello sarà il valore da copiare nel campo di testo:

- il package Java che useremo nella nostra app Android.
A questo punto potrà essere creato il nostro client ID. Fondamentale ricordare che qualora il package realmente usato nel codice Java dell’app fosse diverso da quello inserito nella definizione delle credenziali Google, sarà necessario cambiarlo o creare un altro client ID OAuth2.
Configurazione del progetto Android
La configurazione del nostro progetto in Android Studio passerà invece per una corretta definizione del file build.gradle. Creando un nuovo progetto andrà benissimo quello fornito dall’IDE ma il blocco delle dipendenze dovrà richiedere il corretto set di librerie per la connessione ai servizi Google:
...
...
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:23.0.1'
compile 'com.google.android.gms:play-services-identity:7.8.0'
compile('com.google.api-client:google-api-client-android:1.20.0') {
exclude group: 'org.apache.httpcomponents'
}
compile('com.google.apis:google-api-services-calendar:v3-rev125-1.20.0') {
exclude group: 'org.apache.httpcomponents'
}
}
...
...
Anche il file AndroidManifest.xml sarà piuttosto semplice ma dovremo fare attenzione a rispettare due regole:
- al di fuori del nodo <application> predisporremo le giuste permission per l’accesso a Internet, la consultazione dello stato di Rete e la gestione di credenziali e account;
- all’interno del nodo <application> inseriremo un blocco di tipo <meta-data> in cui sarà specificata, tramite una costante, la versione dei Google Play Services che stiamo utilizzando.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="it.devapp.googlecalendarintegration" >
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme" >
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<activity android:name=".MainActivity" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Struttura dell’esempio
Lo scopo del nostro esempio è vedere le fasi di accesso al Google Calendar da app Android. Lo faremo inserendo degli eventi in un calendario manualmente, ad esempio tramite il nostro smartphone, e recuperando dall’app gli eventi di un dato periodo e mostrandoli in una normale ListView. Non mostreremo nell’esempio le fasi dell’inserimento tramite codice ma, come accenneremo, useranno gli stessi strumenti della lettura.
Queste le fasi dell’esperimento:
- nel metodo onCreate inizializzeremo Adapter e ListView che ci serviranno per mostrare i risultati dell’elaborazione e, successivamente, invocheremo la classe GoogleAccountCredential per l’accesso tramite OAuth2;
- in onResume verificheremo la disponibilità dei Google Play Services nel dispositivo ed in caso affermativo invocheremo un Intent che mostrerà all’utente una finestra di dialogo per la scelta dell’account che possiede il calendario da gestire. Ovviamente se l’account non fosse ancora configurato sul dispositivo verrà richiesto all’utente di inserire le corrette credenziali;
- effettuato l’accesso potremo svolgere la vera interrogazione a Google Calendar e lo faremo in background creando una derivazione della classe AsyncTask.
La query che effettueremo sul calendario restituirà una lista di oggetti Event ognuno dei quali contenente i soliti dati che siamo abituati ad inserire registrando a mano i nostri impegni.
Il nostro Adapter
Per visualizzare gli eventi, creeremo un nostro adapter che estende la classe ArrayAdapter. Di ogni evento verranno visualizzati solo due campi: il summary e la data di inizio dell’evento. Ovviamente sulla falsa riga di quanto faremo qui insieme l’esempio può essere espanso a volontà.
Questa la classe EventAdapter:
public class EventAdapter extends ArrayAdapter<Event>
{
private List<Event> events;
private Context context;
private int layoutId;
private SimpleDateFormat simple=new SimpleDateFormat("dd/MM/yyyy");
public EventAdapter(Context context, int resource) {
super(context, resource);
this.context=context;
layoutId=resource;
}
@Override
public void addAll(Collection<? extends Event> collection) {
clear();
super.addAll(collection);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Event e=getItem(position);
View v= LayoutInflater.from(context).inflate(layoutId, null);
TextView txt_event= (TextView) v.findViewById(R.id.event);
TextView txt_date= (TextView) v.findViewById(R.id.date);
Calendar cal=Calendar.getInstance();
DateTime dt=e.getStart().getDateTime();
if (dt==null)
dt=e.getStart().getDate();
cal.setTimeInMillis(dt.getValue());
txt_event.setText(e.getSummary());
txt_date.setText(simple.format(cal.getTime()));
return v;
}
}
Il metodo getView presente in ogni Adapter è stato sottoposto a override per personalizzare la visualizzazione. Si noti che si controlla se il metodo getDateTime ha restituito un valore null in quanto ciò si verifica quando l’evento copre l’intera giornata e non solo alcune ore.
Il layout che da forma alle singole righe della ListView è molto semplice – due TextView in un LinearLayout – e verrà posto nel file di risorse /res/layout/row.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="15dp"
android:background="#48B1F7"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FFFFFF"
android:textSize="20dp"
android:textStyle="bold"
android:id="@+id/event"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FFFFFF"
android:textSize="15dp"
android:id="@+id/date"/>
</LinearLayout>
L’Activity
Infine arriviamo alla nostra Activity:
public class MainActivity extends ListActivity {
static final int REQUEST_ACCOUNT_PICKER = 101;
static final int REQUEST_GOOGLE_PLAY_SERVICES = 102;
GoogleAccountCredential login;
private static final String[] SCOPES = { CalendarScopes.CALENDAR_READONLY };
private EventAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
adapter=new EventAdapter(this, R.layout.row);
setListAdapter(adapter);
getListView().setDividerHeight(10);
login = GoogleAccountCredential.usingOAuth2(
getApplicationContext(), Arrays.asList(SCOPES))
.setBackOff(new ExponentialBackOff())
.setSelectedAccountName(null);
}
@Override
protected void onResume() {
super.onResume();
if (isGooglePlayServicesAvailable()) {
if (login.getSelectedAccountName() == null) {
startActivityForResult(login.newChooseAccountIntent(), REQUEST_ACCOUNT_PICKER);
} else {
if (isConnected()) {
new CalendarQueryTask(login).execute();
}
}
}
}
@Override
protected void onActivityResult(
int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch(requestCode) {
case REQUEST_GOOGLE_PLAY_SERVICES:
if (resultCode != RESULT_OK) {
isGooglePlayServicesAvailable();
}
break;
case REQUEST_ACCOUNT_PICKER:
if (resultCode == RESULT_OK && data != null &&
data.getExtras() != null) {
String accountName = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
if (accountName != null) {
login.setSelectedAccountName(accountName);
}
}
}
super.onActivityResult(requestCode, resultCode, data);
}
private boolean isConnected() {
ConnectivityManager connMgr =
(ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
return (networkInfo != null && networkInfo.isConnected());
}
private boolean isGooglePlayServicesAvailable() {
final int connectionStatusCode =
GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if (GooglePlayServicesUtil.isUserRecoverableError(connectionStatusCode)) {
GooglePlayServicesUtil.getErrorDialog(
connectionStatusCode,
this,
REQUEST_GOOGLE_PLAY_SERVICES).show();
return false;
} else if (connectionStatusCode != ConnectionResult.SUCCESS ) {
return false;
}
return true;
}
private class CalendarQueryTask extends AsyncTask<Void, Void, List<Event>> {
private com.google.api.services.calendar.Calendar mService = null;
private ProgressDialog progress=null;
public CalendarQueryTask(GoogleAccountCredential credential) {
HttpTransport client = AndroidHttp.newCompatibleTransport();
JsonFactory json = JacksonFactory.getDefaultInstance();
mService = new com.google.api.services.calendar.Calendar.Builder(
client, json, credential)
.build();
}
@Override
protected List<Event> doInBackground(Void... params) {
try {
DateTime now = new DateTime(System.currentTimeMillis());
Events events = mService.events()
.list("primary")
.setMaxResults(10).setTimeMin(now).setOrderBy("startTime")
.setSingleEvents(true)
.execute();
return events.getItems();
} catch (Exception e) {
// gestione dell'errore
return null;
}
}
@Override
protected void onPreExecute() {
progress=ProgressDialog.show(MainActivity.this,"Google Calendar", "Collegamento in corso...",true);
progress.show();
}
@Override
protected void onPostExecute(List<Event> output) {
if (progress!=null)
progress.dismiss();
adapter.addAll(output);
}
}
}
Buona parte del funzionamento gira attorno al metodo onResume. In esso si controlla se è già stato associato un nome di account all’oggetto di classe GoogleAccountCredential che si occuperà del login presso il servizio. Se è ancora nullo si farà apparire una finestra con cui Android chiederà di selezionare un account presente nel sistema o di registrarne uno nuovo. Pronte le credenziali si eseguirà il task in background rappresentato dalla classe CalendarQueryTask. Questo richiederà i dati nel metodo doInBackground che agisce su un thread secondario mentre i metodi onPreExecute e onPostExecute si occuperanno, rispettivamente, di far apparire la finestra con la barra di progresso e di farla scomparire con contestuale aggiornamento dell’adapter.
Da notare che all’interno del costruttore di CalendarQueryTask viene allocato un oggetto Calendar che costituirà il nostro collegamento al servizio. Tramite questo potremo fare varie operazioni. Noi effettueremo una query con il metodo list invocato sulla collezione di eventi events e nello specifico chiederemo dieci eventi a partire dalla data di oggi.
I passi indicati nel codice come l’accesso tramite credenziali e la verifica della disponibilità dei Google Play Services nel terminale (metodo isGooglePlayServicesAvailable) incontrati nell’esempio sono utili sia in altri tipi di interazione con Calendar API sia nel dialogo con altri servizi di Big G.
Per effettuare la vera sperimentazione, potremo aprire l’app Calendar nel nostro dispositivo Android ed inserire manualmente un paio di eventi.
Uno per il 31 dicembre:
e l’altro per l’8 gennaio:
La nostra app mostrerà il seguente layout:
Conclusioni
Quello che abbiamo visto in questo tutorial ha una sua utilità ma non è che l’inizio. Consultando la documentazione delle API si potrà vedere come aggiungere tante altre funzionalità partendo comunque dagli stessi fondamenti: creazione di un progetto Google, gestione delle credenziali, collegamento al servizio ed infine invocazione dei metodi necessari.

















2 Responses to “Come integrare Google Calendar nelle applicazioni Android”
24 Gennaio 2017
NicolaCiao, la guida è veramente ottima…ma ho un problema! Quando avvio l’applicazione mi solleva questo errore net.apispark.webapi.googlecalendarapiexample.EventAdapter.addAll(EventAdapter.java:37)
Sapresti darmi una mano?
Saluti
28 Gennaio 2017
Giuseppe MaggiCiao, grazie dell’apprezzamento. Sull’errore sapresti darci qualche indicazione in più, magari riportando una porzione maggiore del testo restituito da Android Studio così da avere più informazioni sull’eccezione? Grazie