So perfettamente che i contenuti di questo post sono un pò "fuori tempo massimo", ma sono altrettanto consapevole che quanto esporrò servirà a qualcun'altro che, come me, non sempre ha tutte le sue app compilate e testate con le ultime versioni di Android.

Premessa

Una delle app di cui curo lo sviluppo ha un funzionalità grazie alla quale alla pressione di un tasto viene eseguita un'elaborazione che al termine crea una mail bell'è pronta da spedire corredata di un allegato pdf che rappresenta i risultati.

Nulla di particolarmente strano: su come generare questo pdf ho già scritto su questa blog, e per creare la mail nonchè associargli il file ho usato lo straconosciuto e utilizzato Messaging di Xamarin.Plugins.

Tutto ha sempre funzionato in modo egregio e senza grossi problemi.... in modo sano.... per usare un gergo da ciovane......

Ora occorre dire che la app di cui sto parlando permette di inserire degli ordini di merce da parte degli agenti della rete vendita, che quindi vengono elaborati e riversati nel software gestionale del commettente: per tale motivo l'ambito di utilizzo è molto professionale e in tale contesto si è più interessati al fatto che la logica di funzionamento sia corretta e affidabile, un pò meno all'aspetto grafico e alle funzionalità supplettive.

Per questo motivo pur avendo lavorato molto su questo progetto per la  preparazione dei pacchetti di installazione per Android colpevolmente non avevo mai prestato eccessiva attenzione alle versioni da utilizzare dell'sdk per la compilazione e il target, e quindi in generale al valore assunto dai campi minSdkVersion, targetSdkVersion, compileSdkVersion in visual studio. La parte piu importante erano gli esiti degli unit test a corredo !

Devo confessare che pur avendo letto decine di articoli la differenza tra targetSdkVersion e compileSdkVersion non mi è mai stata particolarmente chiara: so questa questa app prima del fattaccio aveva le seguenti impostazioni.

minSdkVersion = Android 5.0 (API Level 21 - Lollipop)

targetSdkVersion = Android 6.0 (Api Level 23 - MarshMallow)

compileSdkVersion = Android 7.1 Nougat

La minSdkVersion è la versione minima di Android su cui la app può girare; siccome non sono interessato a rendere la app disponibile a versioni precedenti alla 5.0 il campo assume proprio questo valore.

La regola che uso per gli altri due campi è che la targetSdkVersion deve essere la stessa versione di Android che equipaggia il tablet che uso per i miei test, mentre il compileSdkVersion la pongo sempre uguale alla versione massima di sdk installata sulla macchina di sviluppo.

Che sia chiaro: non so se quanto faccio sia corretto al 100 %, ma so che così ha sempre funzionato tutto.

Qualche giorno fa ho pensato di riordinare e sistemare la mia scrivania, e, una volta terminata questa attività sull'onda dell'entusiasmo ho pensato bene di aggiornare anche il tablet che uso per lo sviluppo: contestualmente ho anche pensato di porre gli ultimi "gridi" (= versioni) dell'sdk Android sul mio pc lasciando sfogo agli aggiornamenti in canna dell' Android SDK Manager.

A chi si chiede come queste attività siano collegate (riordinare la scrivania ed eseguire gli update) rispondo che a me capitano delle giornate così, in cui sento la voglia irrefrenabile di sistemare e aggiornare tutto. E quella era proprio una di quelle giornate.

Dopo aver terminato, e volendo continuare a seguire la mia regola esposta sopra circa i campi min/target/compile SDK, ho sistemato i tre valori come nel seguito.

minSdkVersion = Android 5.0 (API Level 21 - Lollip)

targetSdkVersion = Android 7.0 (Api Level 24 - Nougat) - Il mio tablet si è aggiornato a tale versione

compileSdkVersion = Android 8.0 Oreo - ultima versione scaricata dall'Android SDK manager.

Dopo un upgrade è d'uopo fare un bel test: nel mezzo di questa attività mi sono reso conto che la maledetta mail con il maledetto pdf non mi veniva più maledettemanete creata.

Non solo: la mia app non solo non eseguiva quanto il suo padre-padrone ha pensato di farle fare, ma anche che il pezzo di codice che allegava alla mail il pdf creato restituiva il seguente errore.

{Java.Lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.XmlResourceParser android.content.pm.PackageItemInfo.loadXmlMetaData(android.content.pm.PackageManager, java.lang.String)' on a null object reference
  at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in :0 
  at Java.Interop.JniEnvironment+StaticMethods.CallStaticObjectMethod (Java.Interop.JniObjectReference type, Java.Interop.JniMethodInfo method, Java.Interop.JniArgumentValue* args) [0x00069] in :0 
  at Android.Runtime.JNIEnv.CallStaticObjectMethod (System.IntPtr jclass, System.IntPtr jmethod, Android.Runtime.JValue* parms) [0x0000e] in :0 
  at Android.Support.V4.Content.FileProvider.GetUriForFile (Android.Content.Context context, System.String authority, Java.IO.File file) [0x00078] in :0 
  at Plugin.Messaging.EmailTask.SendEmail (Plugin.Messaging.IEmailMessage email) [0x00176] in C:\Development\Home\Xamarin.Plugins\Messaging\Plugin.Messaging.Android\EmailTask.cs:89 

Dopo aver osservato che tutte le volte che si ha fretta scoppia il "bugnone", e pensato che.... beh... sì... tutto sommato le vecchie versioni di Android non andavano affatto male e chi caspita me l'ha fatto fare di eseguire gli aggiornamenti etc etc.......

Siccome indietro non si torna ho dovuto investigare il problema e la prima cosa che ho provato a fare è stata quella di aprire direttamente il file pdf senza allegarlo alla mail: questa volta l'errore ottenuto è stato il seguente.

Exception of type 'Java.Lang.NullPointerException' was thrown.
  at Android.Runtime.JNIEnv.CallStaticObjectMethod (IntPtr jclass, IntPtr jmethod, Android.Runtime.JValue[] parms) [0x00064] in /Users/builder/data/lanes/monodroid-mlion-monodroid-4.10.2-branch/4b53fbd0/source/monodroid/src/Mono.Android/src/Runtime/JNIEnv.g.cs:1160 

Bè.... ora si che si ragiona: dopo uno stack di errore cosi chiaro.......... Ma che cappero succede ??

Dopo un pò di ricerche finalmente trovo il bandolo della matassa: in Android 24 Nougat sono state cambiate le policy per l'accesso ai file e questo provoca gli errori sopra.

Allora non ho potuto fare a meno di pensare che.... è dura non fare una cippa dalla mattina alla sera come quelli di google, e quindi è giocoforza inventarsi qualcosa per movimentare la giornata: sai che noia altrimenti ?! ..... e cosa c'è di meglio che cambiare un pò le policy per accedere ai file in Android ???

Quindi più che arrabbiatura.... ci vuole comprensione......sono bravi ragazzi dopotutto....

Con tutta la calma che sono riuscito a raccimolare ho pensato che forse avevano le loro buone ragioni, che tra l'altro sono documentate nei link che Vi allego nel fondo... forse.. ma in ogni caso si rendeva necessario ridare alla mia app dignità sistemando il maledetto errore.

Ecco che quindi dalla lettura di un pò do documentazione ho evinto che è possibile sistemare la cosa in due modi.

Modo sbrigativo

Riporre i valori minSdkVersion, targetSdkVersion e compileSdkVersion al loro valore precedente. Fatto questo tutto riprende a funziona alla grandissima.

C'è il piccolo dettaglio che la app non potrà sfruttare le nuove api e funzionalità proposte nelle versioni successive dell'SDK, ma tutto sommato....... un chi se ne frega lo vogliamo mettere !?

Modo evoluto

L'altro metodo è quello di rendere la app compatibile con queste nuove policy. Per fare questo è necessario aggiungerein AndroidManifest.xml qualcosa come nel seguito.

..
<application android:label="Lamande.Order" android:theme="@style/ThemeOrderEntry">
<provider android:name="android.support.v4.content.FileProvider"
android:authorities="it.infpressapochista.appmeravigliosa.fileprovider"
android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths"></meta-data>
</provider>
...

Occorre sostituire a it.infpressapochista.appmeravigliosa il nome corretto del package name.

 Fatto questo occorre creare un file in Resources/xml/file_paths.xml aggiungendo un'altra manfrina come nel seguito.

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="." />
</paths>

Nella mia app il file da associare alla mail è creato al seguente percorso: Ve lo riporto per chiarezza poichè magari le impostazioni sopra per la vostra situazione devono essere leggermente variate al fine di permettere il corretto funzionamento.

Path.Combine(Forms.Context.ExternalCacheDir.ToString()......

Non vi tedio con la spiegascion circa queste righe: troverete in giro un sacco di documentazione a riguardo sicuramente molto più ben fatta di quanto avrei potuto fare io. Se volete approfondire l'argomento Vi ho riportato alcuni link notevoli che mi sono stati utili.

Eppoi in questo blog si bada al sodo.....

Inutile dire che alla fine ho usato il metodo evoluto, se non altro per evitare problemi futuri e tutto ha ripreso a funzionare a meraviglia.

Spero di esserVi stato utile.

Linkografia

Xamarin.Plugins

file:// scheme is now not allowed to be attached with Intent on targetSdkVersion 24 (Android Nougat). And here is the solution.

FileProvider throws exception on GetUriForFile

Stackoverflow: android.os.FileUriExposedException: file:///storage/emulated/0/test.txt exposed beyond app through Intent.getData()