I dispositivi Android gestiscono molti dati come contatti, file archiviati su disco, eventi del Calendario. Tutte queste informazioni possono essere lette e modificate dalle nostre applicazioni mediante ContentProvider. Il carattere particolarmente “personale” di questi dati segnala quanto la programmazione di un dispositivo mobile a volte si allontani dall'astrazione dell'informatica e si intrecci fortemente con la vita reale dell'utente.
Nel capitolo precedente abbiamo visto le caratteristiche dei ContentProvider, funzionamento e validità architetturale. Qui apprezzeremo molto la standardizzazione dei meccanismi di accesso che mettono a disposizione, agevolando la consultazione di basi di dati spesso molto diverse tra loro.
Aspetti cui si dovrà porgere particolare attenzione:
- gli URI non sono più inventati dal programmatore ma questa volta sono parte delle API di accesso al ContentProvider. Il framework cercherà tuttavia di renderne semplice la conoscenza mediante apposite classi che prendono il nome di classi Contract. Essenzialmente si tratta di classi che contengono un gran numero di costanti, generalmente suddivise in sottoclassi, che forniscono i nomi delle tabelle dei dati, dei campi ed altro ancora sulla struttura della sorgente dati;
- solitamente sarà necessario aggiungere delle permission all'interno del file AndroidManifest.xml. Saranno probabilmente più di una, relative a lettura dei dati e alla scrittura.
Per quanto riguarda l'accesso, si userà ugualmente il ContentResolver ed i suoi metodi che permetteranno di mettere in pratica le quattro operazioni CRUD: lettura, inserimento, modifica e cancellazione.
Alcuni ContentProvider di sistema
ContentProvider molto noti del sistema opearativo sono:
- Contacts: include tutte le informazioni sui contatti dell'utente: rubrica telefonica, email, etc. Non se ne parlerà in questo capitolo. Troverà spazio in un contesto più adeguato come l'esplorazione del rapporto tra Android e telefonia;
- MediaStore: gestisce dati relativi a file multimediali contenuti nel sistema tra cui file audio, immagini e video;
- UserDictionary: si occupa delle parole aggiunte dall'utente al dizionario di default;
- Calendar: serve a gestire appuntamenti ed eventi sul calendario del dispositivo eventualmente sincronizzato con l'account Google. Verrà illustrato nel prossimo paragrafo.
Un esempio: gestire il Calendario
Le API Calendar possono essere davvero utili per fare in modo che le nostre applicazioni possano aiutare gli utenti a gestire i propri impegni.
Per utilizzare questo provider è necessario innanzitutto aggiungere al manifest le permission richieste, dipendentemente dal tipo di operazioni che si vogliono svolgere (lettura e/o scrittura):
<uses-permission
android:name="android.permission.READ_CALENDAR"/>
<uses-permission
android:name="android.permission.WRITE_CALENDAR"/>
Ricordiamo che, durante la sua storia, Android ha modificato l'approccio alle permission, soprattutto per quelle più delicate
a livello di protezione dei dati personali dell'utente. Pertanto, anche per l'impiego di quelle relative al Calendar
si rimanda alle indicazioni fornite nell'apposita lezione.
Tutto il sistema ad oggetti del calendario verterà intorno alla classe Contract di competenza, ovviamente CalendarContract. Questa permetterà l'accesso ad una serie di tabelle, ognuna dedicata ad un aspetto. Ecco le più comuni:
CalendarContract.Calendars
: contiene le informazioni su ogni Calendario. Nel sistema infatti possono essere disponibili più calendari, alcuni di carattere locale quindi utilizzabili solo sul dispositivo, altri legati ad un account Google. Quest'ultimo caso permetterà una sincronizzazione tra i dati presenti nel telefono e quelli remoti;CalendarContract.Events
: è una delle tabelle più importanti ed elenca gli eventi memorizzati nei calendari. L'id del singolo evento servirà per stabilire un collegamento tra questa tabella e altre che ne specificano alcune sfaccettature come Reminders e Attendees;CalendarContract.Attendes
: è collegata a Events e memorizza i partecipanti all'evento specificandone, tra l'altro, il nome e l'account;CalendarContract.Reminders
: collegata anch'essa a Events, ogni sua riga simboleggia un alert o una notifica impostata per un evento. Ogni evento può avere anche più notifiche.
Vediamo ora alcuni esempi di codice per interagire con il nostro calendario.
Recuperare i calendari disponibili nel sistema:
String[] projection =
new String[] {
CalendarContract.Calendars._ID,
CalendarContract.Calendars.NAME,
CalendarContract.Calendars.ACCOUNT_NAME,
CalendarContract.Calendars.ACCOUNT_TYPE
};
Cursor cursor = getContentResolver().
query(CalendarContract.Calendars.CONTENT_URI,
projection,
CalendarContract.Calendars.VISIBLE + & quot; = 1 & quot;,
null,
null);
Per vedere tutti i calendari che possono essere trovati nel sistema, come presumibile, è necessario inviare una query mediante ContentResolver. Si faccia caso che l'URI da utilizzare sarà fornito direttamente dalla classe Calendars e così anche per quanto riguarda i campi. Per il resto non c'è niente di nuovo. Il risultato sarà un normale Cursor che si potrà gestire nelle modalità consuete, lettura diretta o CursorAdapter per citarne alcuni.
Inserire un evento in un determinato calendario:
Una volta utilizzato il codice precedente abbiamo a disposizione tutti i calendari registrati nel dispositivo. Come si può vedere osservando l'array projection, tra i campi recuperati dalla query c'è l'ID. A questo si potrà inserire un nuovo evento nel sistema collegandolo ad un calendario fornendone l'ID.
Calendar cal = new GregorianCalendar(2021, 4, 20);
cal.setTimeZone(TimeZone.getDefault());
cal.set(Calendar.HOUR, 15);
cal.set(Calendar.MINUTE, 30);
long dtstart = cal.getTimeInMillis();
ContentValues values = new ContentValues();
values.put(CalendarContract.Events.DTSTART, dtstart);
values.put(CalendarContract.Events.DTEND, dtstart+3*3600*1000); // durata di tre ore
values.put(CalendarContract.Events.TITLE, "Riunione con il capo");
values.put(CalendarContract.Events.CALENDAR_ID, id);
values.put(CalendarContract.Events.EVENT_TIMEZONE, TimeZone.getDefault().getDisplayName());
Uri uri =
getContentResolver().
insert(CalendarContract.Events.CONTENT_URI, values);
Nell'esempio abbiamo salvato un promemoria per la riunione con il capo per il 20 maggio. Abbiamo annotato anche che l'impegno durerà tre ore a partire dalle 15:30.
Il salvataggio effettuato si preoccuperà di inserire solo i dati minimi indispensabili ossia quelli assolutamente obbligatori per un evento non ripetitivo. Tra gli altri vediamo che nel ContentValues
preparato per l'inserimento è stato collocato anche l'ID del calendario, informazione che può essere recuperata mediante il primo esempio di codice.
Aprendo successivamente l'applicazione Calendar sul dispositivo Android vedremo che effettivamente il promemoria per la riunione è stato salvato ed è visibile nell'immagine seguente.