====== Calendari e contatti su Android, senza Google ======
Su uno smartphone Android è possibile avere la rubrica dei contatti e dei calendari con gli eventi senza passare per i servizi Google? Sembra di sì, anche se è tutt'altro che semplice!
In pratica è necessario installare un server che fornisca il servizio tramite i protocolli DAV, nel nostro caso abbiamo utilizzato **DAViCal** su Debian. Sul terminale Android si deve installare un client opportuno, la nostra scelta è caduta su **DAVdroid**. Infine è necessario adottare diversi accorgimenti per poter **esportare** i calendari e i contatti esistenti e verificare che la **sincronizzazione** col server remoto avvenga correttamente.
===== Installazione del server DAViCal =====
In Debian 9 Stretch esiste il pacchetto **DAViCal** versione 1.1.5, per convenienza esiste anche il **[[https://packages.debian.org/stretch-backports/|backport]]** della **versione 1.1.7**, che abbiamo preferito. Per soddisfare le dipendenze è necessario prendere dai backports anche il pacchetto **libawl-php** versione **0.59**.
È necessario un **database di supporto**, nel nostro caso abbiamo utilizzato **PostgreSQL**. La procedura di installazione è abbastanza //sui generis//, in quanto richiede che venga predisposto l'accesso al database senza password (per fortuna è possibile revocarlo al termine dell'installazione). Vedere gli appunti nel paragrafo **[[https://www.rigacci.org/wiki/doku.php/doc/appunti/linux/sa/davical?s[]=caldav#installazione_del_server_davical|Installazione del server DAViCal]]**.
In **Apache 2.4** abbiamo definito un **VirtualHost** solo su HTTPS. Chi fa accesso usando HTTP viene rediretto per evitare che trasmetta password in chiaro sulla rete. Questa la configurazione da aggiungere:
DirectoryIndex index.php
AllowOverride None
Require all granted
ServerName dav.server.org
DocumentRoot /var/www/html/default
# Redirect everything to https, except /.well-known/ directory.
RedirectMatch permanent ^/((?!\.well-known).*)$ https://dav.server.org/$1
SSLEngine on
ServerName dav.server.org
SSLCertificateFile /etc/letsencrypt/live/dav.server.org/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/dav.server.org/privkey.pem
DocumentRoot /var/www/html/default
ServerAdmin webmaster@server.org
ErrorLog ${APACHE_LOG_DIR}/dav.server.org/error.log
CustomLog ${APACHE_LOG_DIR}/dav.server.org/access.log combined
Alias /dav /usr/share/davical/htdocs
RewriteEngine On
# PT is important if you are using an alias, it implies L
# Redirect /.well-known URLs
RewriteRule ^/\.well-known/(.*)$ /dav/caldav.php/.well-known/$1 [NC,PT]
# Optionally: redirect /principals/users/ as well
RewriteRule ^/principals/users/(.*)$ /dav/caldav.php/$1 [NC,PT]
RewriteRule ^/principals/resources/(.*)$ /dav/caldav.php/$1 [NC,PT]
RewriteRule ^/calendars/__uids__/(.*)$ /dav/caldav.php/$1 [NC,PT]
RewriteRule ^/addressbooks/__uids__/(.*)$ /dav/caldav.php/$1 [NC,PT]
# Redirect / requests to web login page
RedirectMatch permanent ^/$ https://dav.server.org/dav/
L'URL base del server sarà **%%https://dav.server.org/dav/%%** e sarà accessibile sia con il protocollo WebDAV che con il protocollo HTTPS.
L'accesso in HTTPS presenta un'interfaccia minimale di amministrazione web: sarà possibile creare utenti (//principals//), creare address book e calendar (//collections//), ma per le normali operazioni di gestione contatti ed eventi si dovrà utilizzare un client dedicato. Nel nostro caso il client è rappresentato dalla app **DAVdroid** installata su uno smartphone Android, che darà accesso ai contatti e agli eventi di calendario a tutte le app relative.
===== Creazione di un utente con address book e calendar =====
L'amministratore del server DAViCal si collega all'URL di gestione **%%https://dav.server.org/dav/%%**, quindi accede alla pagina **User Functions** => **Create Principal**. I parametri da inserire sono:
^ Username | È prassi abbastanza diffusa utilizzare l'indirizzo di posta elettronica come nome utente. |
^ Password | Immettere una password. |
^ Fullname | Normalmente si inserisce nome e cognome. Gli altri utenti del server DAViCal potranno vedere questa informazione. |
^ Email Address | Informazione di base richiesta per la creazione di un account, di solito coincide con lo Username. Anche questa informazione sarà pubblica per tutti gli utenti del server DAViCal. |
^ Principal Type | Selezionare **Person** per la creazione di un utente. |
Al termine dell'operazione sarà stato attivato un utente DAViCal; automaticamente sarà stato creato anche un **address book** (CardDAV) e un **calendario** (CalDAV) a lui assegnati. Se il nome di login è //%%user@server.org%%//, gli URL dei rispettivi oggetti saranno del tipo:
^ Address book | %%https://dav.server.org/dav/caldav.php/user@server.org/addresses/%% |
^ Calendar | %%https://dav.server.org/dav/caldav.php/user@server.org/calendar/%% |
In generale non sarà necessario ricordare il percorso completo dei due oggetti, con l'**autodiscover** dovrebbe essere sempre sufficiente indicare solo l'**URL base del server** e gli oggetti disponibili dovrebbero essere automaticamente mostrati.
===== Installazione di DAVx5 =====
Esistono diverse app per Android per la **sincronizzazione** di **contatti** ed **eventi** calendario verso server **DAV**. Purtroppo si tratta spesso di app che offrono solo una delle due funzionalità e spesso le versioni free hanno limitazioni o problemi (ad esempio continui alert se la connessione con il server fallisce). Pare invece che l'app **[[https://play.google.com/store/apps/details?id=at.bitfire.davdroid|DAVx5]]** (prima conosciuto come **DAVdroid**) abbia tutte le carte in regola per essere scelta: si tratta di **software open source** disponibile anche su **[[https://f-droid.org/en/packages/at.bitfire.davdroid/|F-Droid]]** e sembra sufficientemente stabile e maturo.
{{ .:davdroid:davdroid-account.png?direct&180|DAVx5 Account}}
Una volta scaricata e installata l'app, si deve **creare un nuovo account** sullo smartphone. Nel nostro caso si procede con l'opzione **Login with URL and user name**. Con l'installazione di DAViCal vista in precedenza si inserisce qualcosa del genere:
^ Base URL | %%https://dav.server.org/dav/%% |
^ User name | %%email@server.org%% |
^ Password | %%******%% |
Lasciare attiva l'opzione predefinita **Groups are separate VCards**. Il server DAViCal support il formato [[wpit>VCard|vCard]] v.3.0 e quindi DAVx5 gestirà i gruppi come vCard separate (i gruppi sono usati ad esempio per creare le etichette da assegnare ai contatti). Vedere il paragrafo //Contact group method// nella [[https://www.davdroid.com/manual/accounts/|documentazione sugli Account]].
DAVx5 si collega al server e tramite l'**autodiscovery** sul percorso **%%/.well-known/%%** si accorge che esistono già due //collection// collegate all'account: un **addressbook** e un **calendar**. Per impostazione predefinita infatti DAViCal crea queste due collection per ogni //principal// di tipo //person// (cioè per ogni account) che viene creato. Nella schermata che segue (vedi figura) bisogna attivare la sincronizzazione per queste due //collection//.
Questo è ciò che compare nello smartphone:
* Strumenti => **Utenti e account**
* Compare un nuovo account di tipo **DAVx5**, identificato con l'indirizzo email. Da questo account dipendono tutti i calendari e le rubriche che verranno sincronizzate col server.
* Compare anche un nuovo account di tipo **Rubrica DAVx5**, identificato con il //Displayname// definito da DAViCal e l'indirizzo email più due lettere casuali. Questo account //figlio// è necessario per una **limitazione di Android**; la rubrica contatti infatti deve essere direttamente collegata ad uno specifico account, quindi l'account **DAVx5** (padre) deve esporre un account **Rubrica DAVx5** (figlio) per ogni rubrica contatti disponibile sul server. Questo non accade per i calendari: il singolo account padre è sufficiente per accederli tutti.
Maggiori dettagli nella pagina **[[https://www.davdroid.com/manual/accounts/|Accounts del manuale di DAVdroid]]**.
==== Problema di sincronizzazione ====
Il sistema Android usa **tecniche aggressive per il risparmi di batteria**, questo affligge soprattutto le app non di sistema tipo DAVdroid. In pratica accade che **viene inibita la sincronizzazione** con il server: quando l'app è in background non viene mai svegliata.
Avviando l'app DAVx5 si viene avvisati di questo potenziale problema da tre pop-up:
* Battery ... ?
* Automatic synchronization
* OpenTasks non installata
Ci si può accorgere che qualcosa non va dal menu **Strumenti** => //Utenti e account// => //DAVdroid// => **Sincronizzazione account**. Sebbene l'opzione sincronizzazione sia attiva e si scelga l'azione **Sincronizza ora** dal menu in alto a destra, **l'orario di sincronizzazione non si aggiorna**!
Nel nostro caso Android 8.1.0 Oreo su Xiaomi Mi A1, è stato necessario ... FIXME
===== Creazione di un calendario aggiuntivo =====
{{ .:davdroid:davdroid-multi-calendar.png?direct&200|DAVdroid: calendari}}
In genere la via più comoda per aggiungere un calendario è utilizzare il client CalDAV preferito. Anche DAVdroid ha questa funzione agendo dalla app **DAVdroid**, //Account// => //CalDAV// => //Hamburger menu// => **Crea nuovo calendario**.
Il calendario può essere di tipo **Calendario** oppure **Elenco attività** o meglio ancora la combinazione dei due. Tale impostazione viene registrata nel database nella colonna **property_value** della tabella **property**. Le attività (**task** in inglese), a differenza degli eventi di calendario, di solito sono associati ad una data senza l'orario e sono pensati per potersi sovrapporre gli uni con gli altri (molte attività vengono portate avanti in parallelo. L'app calendario stock di Android non consente di visualizzare i task.
In alternativa l'utente (anche non amministratore) può effettuare login alla **pagina di amministrazione web** di DAViCal, quindi sceglie l'opzione //User Functions// => //View My Details// => //Principal Collections// => **Create Collection**. Nella pagina di creazione si deve attivare l'opzione **Is a Calendar**. Il calendario creato in questo modo sarà di tipo combinato //Calendario// ed //Elenco attività//.
Per attivare il nuovo calendario nello smartphone si accede alla app DAVdroid, dopo aver selezionato l'account opportuno, si effettua tap sull'Hamburger menu di CalDAV, quindi si sceglie l'azione **Aggiorna lista calendari**.
Per poter disporre del nuovo calendario sullo smartphone è necessario:
* Dalla app **DAVdroid** si sceglie l'account relativo, quidni dall'hamburger menu si sceglie l'azione **Aggiorna lista calendari**.
* Dalla app **Sistema** => //Utenti e account// => //Account DAVdroid// => //Sincronizzazione account// => //Menu// => **Sincronizza ora**.
* Dalla app **Calendario** si inserisce un **nuovo evento**, che generalmente provoca la **sincronizzazione** con il server.
===== Esportazione dei contatti da Google =====
Per fortuna è possibile esportare in formato aperto tutti i contatti che abbiamo associato al nostro account Google. È sufficiente autenticarsi su Gmail all'url [[https://mail.google.com/]], quindi aprire l'url **[[https://contacts.google.com/]]**. Aprendo il menù **Di più** è possibile scegliere l'opzione **Esporta**. Nel dialog-box è possibile indicare **Esporta come vCard**. Al termine sul PC avremo il file **contacts.vcf** che deve essere trasferito sullo smartphone, ad esempio con il tool **adb push** e il cavetto USB.
L'importazione va fatta dall'app **Contatti**, noi abbiamo usato quella stock di Android Oreo 8.1.0. Anzitutto dal menu **Impostazioni** abbiamo indicato come **Account predefinito per i nuovi contatti** quello aggiunto con DAVdroid, quindi, sempre da //Impostazioni//, si è scelto la voce **Importa** => **File .vcf**.
**Non ha funzionato** l'import delle **foto associate** ai contatti, infatti Google le include nel formato vCard come link ad un indirizzo web esterno (eventualmente da scaricare a parte), non come allegato JPEG.
L'app Contatti può mostrare i contatti provenienti dall'account Google, dall'account DAVdroid, oppure da tutti e due; dall'hamburger menu vi è un menu a tendina per la scelta. **Fare attenzione** a quando si rimuove un account, perché **non è chiaro se lo stiamo rimuovendo** anche dall'account Google e/o DAVdroid. Per sicurezza conviene rimuovere gli account Google facendo accesso con il browser all'url [[https://contacts.google.com/]].
===== Esportazione degli eventi calendario da Google =====
Per l'esportazione è necessario fare accesso con un PC e un browser alla pagina **[[https://calendar.google.com/]]**, ovviamente dopo aver effettuato il login ad esempio alla casella [[https://mail.google.com/|Gmail]]. Dall'icona **ingranaggio** si sceglie **Impostazioni** => //Importazione ed esportazione// => **Esporta**. Viene fatto scaricare un file zip che contiene un **file .ics** per ogni calendario esistente nell'account Google.
Nei file .ics esportati è opportuno modificare il tag **X-WR-CALNAME**, impostando la descrizione breve che identificherà il calendario nell'interfaccia di DAVdroid.
È possibile caricare ogni singolo file .ics in un calendario DAViCal dall'interfaccia web. Dopo aver fatto login come utente proprietario del calendario, si clicca **View My Details** => //Principal Collections// => //ID// => **Load From File**. L'opzione //Append// consente di aggiungere gli eventi a quelli già esistenti, altrimenti il file caricato sostituisce del tutto il contenuto esistente.
Per altri dettagli vedere la pagina [[https://wiki.davical.org/index.php/How_can_I_migrate_to_DAViCal_using_already_generated_.ics_files%3F|How can I migrate to DAViCal using already generated .ics files?]].
===== Impostazione del colore predefinito di un calendario =====
Dall'interfaccia di amministrazione web di DAViCal non è possibile impostare il colore predefinito di un calendario. Né la app calendario di AOSP stock (Android Open Source Project) né DAVdroid consentono di impostare il colore di un calendario esistente. È possibile tuttavia **agire direttamente sul database** di DAViCal per impostare il colore predefinito.
Anzitutto verificate nella **app DAVdroid**, aprendo l'account interessato, che nelle **Impostazioni** (icona ingranaggio) l'opzione **Cambia il colore del calendario** sia attiva; in questo modo DAVdroid gestirà i colori dei calendari forniti dal server. Anche l'opzione **Supporto colore dell'evento** può essere attivata; in questo modo ogni singolo evento potrà essere colorato in modo diverso rispetto al calendario di appartenenza (funzione [[https://www.davdroid.com/faq/setting-event-colors-crash/|con alcuni problemi]]: non funziona con la app Calendario stock, manda in crash l'app calendario **Etar**, ecc.).
Dopo essersi **collegati al database** come amministratore, si cerca l'**utente proprietario** del calendario:
davical=# SELECT principal_id, user_no, displayname FROM principal;
principal_id | user_no | displayname
--------------+---------+-----------------------
1 | 1 | DAViCal Administrator
1001 | 1001 | Niccolo Rigacci
quindi si cerca il **dav_name** del calendario desiderato:
davical=# SELECT user_no, dav_name, is_calendar FROM collection WHERE user_no = 1001;
user_no | dav_name | is_calendar
---------+---------------------------------+-------------
1001 | /niccolo@rigacci.org/addresses/ | f
1001 | /niccolo@rigacci.org/calendar/ | t
1001 | /niccolo@rigacci.org/famiglia/ | t
Infine si aggiunge una riga alla **tabella property**:
INSERT INTO property
(dav_name, property_name, property_value, changed_by) VALUES
('/niccolo@rigacci.org/famiglia/', 'http://apple.com/ns/ical/:calendar-color', '#F8BE14', 1);
Sullo smartphone è necessaria una sincronizzazione per vedere l'effetto:
* Dalla app **DAVx5** => Tap su account => CalDAV => [[wp>Hamburger button]] => **Refresh calendar list**.
* **Chiudere** l'app **Calendario**.
* Da **Impostazioni** => //Utenti e account// => //DAVx5// => //Sincronizzazione account// => **Sincronizza ora**.
===== Disattivazione App Calendario Google =====
A margine di questa esperienza vorrei segnalare un comportamento **poco evidente e rischioso** della **app Calendario di Google**. Su uno smartphone con CyanogenMod v.12.1 (basato on Android 5.1 Lollipop) era stata installata la app **[[https://f-droid.org/en/packages/ws.xsoh.etar/|Etar]]**; poiché il **Calendario di Google** non veniva più utilizzato, la app era stata **disattivata** dal menu //Impostazioni// => //App//.
La app Etar e le altre app di tipo calendario continuavano a mostrare gli eventi di calendario come associati all'account Google, permettendo anche di inserirne di nuovi. In realtà la sincronizzazione con l'account Google non funzionava più: tutti gli eventi erano salvati solo in locale sul device Android e non erano presenti sui server Google. Il tutto accadeva in modo silente, senza alcun messaggio di errore o di warning. Due circostanze potevano evidenziare il problema:
* Gli eventi aggiunti dalla app Android **non comparivano nel calendario web** [[https://calendar.google.com/]] e viceversa.
* Dal menu //Impostazioni// => //Account// => //Google// => **Sincronizzazione** era sparita la voce Calendario.
Per effettuare la corretta esportazione dei calendari è stato necessario riattivare la App Google e forzare la sincronizzazione.
===== Creazione di un calendario di gruppo =====
Scenario: si vuole creare un calendario e condividerlo con due o più utenti.
=== Creazione di un Principal di tipo Group ===
Il //principal// di tipo //group// avrà il suo login e la sua password, ma le credenziali non verranno condivise con gli utenti che avranno accesso al calendario. Il calendario (in effetti una //collection//) sarà creato come appartenente a questo gruppo.
* User Functions => Create Principal
* Username: caldavtest@domain.tld
* Password: %%******%%
* Fullname: Gruppo per calendario condiviso
* Email Address: caldavtest@domain.tld
* Principal Type: Group
=== Aggiunta degli User al Group ===
FIXME Serve davvero? Oppure è sufficiente creare il Ticket (vedi avanti)?
* User Functions => List Groups => (click su ID)
* Group Members => (select the user) => Add
* Group Members => (select the user) => Add
=== Creazione di un Calendar Collection nel Group ===
* User Functions => List Groups => (click su ID)
* Principal Collections => Create Collection
* DAV Path: /dav/caldav.php/caldavtest@domain.tld/calendar/
* Displayname: Calendario condiviso
* Is a Calendar: [x]
* Default Privileges: [x]
* alendar Timezone: Europe/Rome
* Collection Grants:
* (select the group): ALL (FIXME Serve davvero? O basta il ticket?)
* (select the user): ALL (FIXME Serve davvero? O basta il ticket?)
* (select the user): ALL (FIXME Serve davvero? O basta il ticket?)
L'URL completo del calendario è **%%https://dav.domain.tld/dav/caldav.php/caldavtest@domain.tld/calendar/%%**, ma non si userà questo per l'accesso. La risorsa verrà ricollocata (**bind**) come risorsa di ciascun utente.
=== Creazione di un Access Ticket per la Collection ===
* User Functions => List Groups => (click su ID)
* Access Tickets
* Ticket ID: Nfe5wq0Y
* Target: /caldavtest@domain.tld/calendar/
* Expires: 2029-12-29
* Privileges: ALL
=== Binding della Calendar Collection allo User ===
Con questa operazione, il calendario condiviso (che ha URL **%%/caldavtest@domain.tld/calendar/%%**) verrà mappato come risorsa dell'utente (con URL **%%/user1@domain.tld/boundcalendar/%%**):
* User Functions => List Users => (click su ID)
* Bindings to other collections
* ID Bounds As: /user1@domain.tld/boundcalendar/
* Display Name: Calendario condiviso
* To Collection: /caldavtest@domain.tld/calendar/
* Ticket ID: Nfe5wq0Y
L'URL completo del calendario diventa quindi **%%https://dav.domain.tld/dav/caldav.php/user1@domain.tld/boundcalendar/%%**. In generale non si dovrà indicare l'URL per esteso, perché il client (es DAVx5 su Android) elencherà il nuovo calendario nell'elenco di quelli disponibili, con l'etichetta impostata in //Displayname//.
=== Utilizzo dei ticket ===
**%%https://dav.domain.tld/dav/caldav.php/user1@domain.tld/calendar/?ticket=7edu4Cpt%%**
===== Aggiornamento del database di backend =====
Dopo l'aggiornamento da Debian 9 Stretch a **Debian 10 Buster**, quando ci si collega all'interfaccia web di Davical (nella nostra installazione è qualcosa del tipo **%%https://dav.host.tld/dav/%%**), si legge il seguente warning:
Database schema needs upgrading. Current: 1.3.2, Desired: 1.3.3
Si è deciso di agire da riga di comando. Anzitutto si crea un file **/etc/davical/administration.yml** con le credenziali per accedere al database (proteggere il file con **mode 600**):
admin_db_user: davical_dba
admin_db_pass: MySecret
admin_db_host: 127.0.0.1
Quindi si esegue lo script **/usr/share/davical/dba/update-davical-database** che provvede ad applicare tutte le patch incrementali allo schema del database. Ovviamente si consiglia di fare un backup del database **prima** di eseguire la procedura.