CSharp:Android App - Tips
From Aino Wiki
Codice BackEnd
Thread
Come impostare una attesa
//asynchronously: await Task.Delay(10000); //synchronously: Task.Delay(10000).Wait();
Variabili di ambiente
Nome del dispositivo (doc developer.android.com, Microsoft):
SharedSetupPar.AppStatus.SO_Type = DeviceInfo.Platform.ToString(); // <-- Android SharedSetupPar.AppStatus.DeviceName = Android.OS.Build.Model; // <-- HUAWEI VNS-L31
Verifica e richiesta permessi
#region Gestione Permessi int sdkApi_info = (int)Build.VERSION.SdkInt; // >= 23 bool isAccessDenied = false; int nrAttempt4Access = 0; do { strErrorMsg = string.Empty; if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.ReadExternalStorage) != (int)Permission.Granted) { strErrorMsg += "\r\nNON Si dispone dei permessi di LETTURA per l'archivio esterno"; isAccessDenied = true; } if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.WriteExternalStorage) != (int)Permission.Granted) { strErrorMsg += "\r\nNON Si dispone dei permessi di SCRITTURA per l'archivio esterno"; isAccessDenied = true; } if (isAccessDenied) { var permissions = new string[] { Manifest.Permission.ReadExternalStorage, Manifest.Permission.WriteExternalStorage }; ActivityCompat.RequestPermissions(this, permissions, 9); // RequestCode REQUEST_FOLDER_PERMISSION = 9? Task.Delay(1000).Wait(); } nrAttempt4Access++; isAccessDenied = ContextCompat.CheckSelfPermission(this, Manifest.Permission.ReadExternalStorage) != (int)Permission.Granted || ContextCompat.CheckSelfPermission(this, Manifest.Permission.WriteExternalStorage) != (int)Permission.Granted; } while (isAccessDenied && nrAttempt4Access < 3); if (isAccessDenied && nrAttempt4Access == 3) { throw new Exception("Permissions are mandatory!\r\n" + strErrorMsg); } #endregion
Oggetti UI
ProgressBar
Quest'oggetto è utilizzabile in due versioni: Hourgrass/Clessidra o Progress bar. Doc ProgressDialog.
Stile clessidra
Segue esempio minimale rifatto ma basato su questa fonte forums.xamarin.com.
Si implementa la modalità "Indeterminate Progress
". Si usa un Timer che sostanzialmente fa vedere la clessidra per 10".
<ProgressBar android:id="@+id/PrgBarIndet" android:layout_width="match_parent" android:layout_height="match_parent" />
Altro esempio:
<ProgressBar android:id="@+id/progressBar_cyclic" android:layout_width="wrap_content" android:layout_height="wrap_content" android:indeterminate="true" android:minHeight="50dp" android:minWidth="50dp" android:layout_centerVertical="true" android:layout_centerHorizontal="true" />
Lato BackEnd:
//.. ProgressBar PrgBarIndet; //.. System.Timers.Timer m_TimerUI; private static int m_ProgressBarStatus = 0; object m_lockRsr = new object(); protected override void OnCreate(Bundle savedInstanceState) { //.. dopo il codice di inizializzazione standard PrgBarIndet = FindViewById<ProgressBar>(Resource.Id.PrgBarIndet); } public void MetodoDaClessidra() { StartTimer4PrgBar_Test(); //Azioni lunghe a seguire } private void StartTimer4PrgBar_Test() { m_ProgressBarStatus = 0; PrgBarIndet.Progress = m_ProgressBarStatus; PrgBarIndet.Max = 100; m_TimerUI = new System.Timers.Timer(); m_TimerUI.Enabled = true; m_TimerUI.Interval = 1000; // 1 secondo m_TimerUI.Elapsed += TimerUI_Elapsed_test; } private void TimerUI_Elapsed_test(object sender, ElapsedEventArgs e) { RunOnUiThread(() => { Log.Info(SharedSetupParams.Dbg_AndroidTAG + "TimerUI_Elapsed" , string.Format("m_Prg: {0}, PrgBar: {1}" , m_ProgressBarStatus, PrgBarIndet.Progress)); //PrgBar.IncrementProgressBy(10); //Non influenza la proprietà: PrgBar.Progress m_ProgressBarStatus += 10; PrgBarIndet.Progress = m_ProgressBarStatus; //La proprietà non è influenzata avrà sempre 0! CheckProgress_test(); }); } public void CheckProgress_test() { lock (m_lockRsr) { Log.Info(SharedSetupParams.Dbg_AndroidTAG + "CheckProgress" , string.Format("m_Prg: {0}, PrgBar: {1}, Visible? {2}." , m_ProgressBarStatus , PrgBarIndet.Progress , PrgBarIndet.Visibility == ViewStates.Visible)); if (m_ProgressBarStatus >= 100) // FA DURARE LA CLESSIDRA 10 SECONDI !!!!!!!!!!!!!!!! { Log.Info(SharedSetupParams.Dbg_AndroidTAG + "CheckProgress" , string.Format("Reset! m_Prg: {0}, PrgBar: {1}" , m_ProgressBarStatus, PrgBarIndet.Progress)); m_ProgressBarStatus = 0; PrgBarIndet.Visibility = ViewStates.Gone; m_TimerUI.Dispose(); } else { PrgBarIndet.Visibility = ViewStates.Visible; } } }
Stile ProgressBar
Si implementa la modalità "Determinate Progress
". Per indicare la progressione si usa lo stile style="@android:style/Widget.ProgressBar.Horizontal"
ed il valore della progressione usando l'attibuto "Progress" (nel seguente esempio si parte da 25%).
<ProgressBar android:id="@+id/PrgBarDet" style="@android:style/Widget.ProgressBar.Horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:visibility="gone" android:layout_gravity="center|left" android:layout_marginLeft="30dp" android:layout_marginRight="30dp" android:progressTint="@android:color/holo_green_light" android:indeterminate="false" android:max="100" android:progress="20" android:contentDescription="xxxx" />
Altro esempio
<ProgressBar android:id="@+id/progressBar" style="?android:attr/progressBarStyleHorizontal" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:layout_marginLeft="23dp" android:layout_marginTop="20dp" android:indeterminate="false" android:max="100" android:minHeight="50dp" android:minWidth="200dp" android:progress="1" />
BackEnd. E' necessario che la percentuale di avanzamento sia impostata in un metodo asincrono:
ImageButton
Esempio di inserimento di un pulsante con icona del copia in Clipboard:
<ImageButton android:id="@+id/ImgBtnPw4Clipboard" android:src="?android:attr/actionModeCopyDrawable" android:visibility="gone" android:layout_width="40dp" android:layout_height="40dp" android:layout_row="3" android:layout_column="2" android:layout_marginRight="0.0dp" android:layout_gravity="right" android:contentDescription="@string/LblHnt_ImgBtnPw4Clipboard" />
L'evento da intercettare è Touch
ImageButton ImgBtnPw4Clipboard; //.. ImgBtnPw4Clipboard = FindViewById<ImageButton>(Resource.Id.ImgBtnPw4Clipboard); ImgBtnPw4Clipboard.Touch += ImgBtnPw4Clipboard_Touch; //.. private async void ImgBtnPw4Clipboard_Touch(object sender, View.TouchEventArgs e) { await Clipboard.SetTextAsync(TxtPw4Clipboard.Text); }
Varie
Cambiare il Focus
Empiricamente è difficile evitare il focus su TextBox se è questo che si vuole per evitare la visualizzazione della tastiera virtuale spostare il focus ad esempio su una ListView come segue:
ListView LVMatchItems = null; //Cambia il Focus per sistemare la situazione odiosa di apertura della tastiera virtuale LVMatchItems.RequestFocus();
Intercettare pulsante Indietro e chiudere l'App
ATTENZIONE l'handler OnKeyUp()
intercetta TUTTI i tasti digitati non solo il tasto "<" per tornare ""indietro.
private static bool m_backExitBtnPressed = false; //<-- usato per evitare uscite involontarie dall'App //... public override bool OnKeyUp(Keycode keyCode, KeyEvent e) { try { //Torna alla PAGINA PRINCIPALE if (keyCode == Keycode.Back && !m_isMainView) { SwitchViewMainEdit(true); PrepareForNewSearch(); //Aggiorna la lista e rieseguire l'ordinamento! UpdateListView(TxtItemToSearch_FileName.Text); return true; } //Warining per evitare che per errore si esca dall'App! else if (keyCode == Keycode.Back && !m_backExitBtnPressed) { m_backExitBtnPressed = true; //Sarà ripristinato dopo l'evento: OnPostResume() Toast.MakeText(this, "Sure? Try again to EXIT...", ToastLength.Long).Show(); return true; } //Mette in Pausa l'App passando dagli eventi: OnPause() ed infine OnStop() else if (keyCode == Keycode.Back) { Log.Warn(SharedSetupParams.Dbg_AndroidTAG + "OnKeyUp" , string.Format("KeyCode='{0}'. Messa in PAUSA dell'App!", keyCode)); this.Finish(); } } catch (Exception ex) { Log.Error(SharedSetupParams.Dbg_AndroidTAG + "OnKeyUp", ex.Message); } return true; } protected override void OnPostResume() { if (m_backExitBtnPressed) { m_backExitBtnPressed = false; } base.OnPostResume(); }
Condivisione di documenti
Se si vuol condividere ad esempio una immagine o un file si usa facilmente il popup di Android per la condivisione dopo avergli fornito il percorso del file. La libreria da includere è la Xamarin.Essentials
Per documentazione microsoft.com
Sostanzialmente dal seguente esempio si riesce a creare al volo un file di testo ed aprire il popup di invio in condivisione.
using Xamarin.Essentials; //... Button BtnShareFile; protected override void OnCreate(Bundle savedInstanceState) { //... BtnShareFile.Click += BtnShareFile_Click; } private void BtnShareFile_Click(object sender, EventArgs e) { try { ShareFile(); } catch (Exception ex) { AManager.MessageBox(this, "BtnShareFile_Click", ex.Message); } } public async Task ShareFile() { var fn = "Attachment.txt"; var file = Path.Combine(FileSystem.CacheDirectory, fn); File.WriteAllText(file, "Hello World"); await Share.RequestAsync(new ShareFileRequest { Title = Title, File = new ShareFile(file) }); }
Google Drive
Da approfondire qui [1]
Link interno Android App - Soluzioni avanzate
Autorizzazioni
KeyTool
The MD5 or SHA1 signature of a Xamarin.Android app depends on the .keystore file that was used to sign the APK. Typically, a debug build will use a different .keystore file than a release build. Locate the Xamarin debug.keystore file that is used to sign the app. By default, the keystore that is used to sign debug versions of a Xamarin.Android application can be found at the following location:
C:\Users\USERNAME\AppData\Local\Xamarin\Mono for Android\debug.keystore
Information about a keystore is obtained by running the keytool.exe command from the JDK. This tool is typically found in the following location:
C:\Program Files (x86)\Java\jdkVERSION\bin\keytool.exe
DOC come trovare la propria Keystore Signature docs.microsoft.com
Perchè serve SHA1 fingerprint support.google.com
Per la chiave SH1 di debug ecco come ottenerla:
keytool -list -v \ -alias androiddebugkey -keystore %USERPROFILE%\.android\debug.keystor oppure keytool.exe -list -v -keystore "%LocalAppData%\Xamarin\Mono for Android\debug.keystore" -alias androiddebugkey -storepass android -keypass android
Debug
Log
Per verificare cosa succede ad es in fase di installazione dell'APK usare il tool "Logcat" (developer.android.com).
Da Visual Studio cliccare sull'icona in toolbar 'Adb' ed apertasi la finestra terminale digitare:
adb logcat
Dal forum: forums.xamarin.com
Installazione e debug App via USB
Occorre prima abilitare il telefono alla modalità "Developper", successivamente, dal nuovo menu Developper abilitare il Debug via USB.
A questo punto il dispositivo dovrebbe essere visibile ma se non lo fosse si può provare a cambiare la modalità di connessione da MTP (Media Transfer Protocol) a PTP (Picture Transfer Protocol) o RNDIS(USB Ethernet). Per cambiare questa modalità cercare "Select USB Configutation" PTP / MTP / RNDIS
Abilitare anche "Check apps installed via ADB/ADT for harmful behavior"
APK
E' un file archivio contenente il necessario per installare "manualmente" una App Android senza seguire il classico modo attraverso Google Play, con questa modalità occorrerà prima abilitare lo Smartphone all'installazione da "Unknown Sources" (Fonti sconosciute).
Creazione
Il processo corretto per la creazione del file di installazione APK (Android Application Package) è il seguente:
- Cambiare la modalità di compilazione da 'Debug' a 'Release'.
- Tasto dx sulla Solution (dalla finestra Solution Explorer), e effettuare un "Clean";
- Compre prima ma scegliere l'opzione "Rebuild";
- Tasto dx sul progetto Android e cliccare su "Archive";
- After successful archive click on "Distribute" and click on "Ad-hoc";
- Create keystore file;
- After finishing click on Open Distribution;
Il file APK è disponibile.
Mappa e Link
C# | Android App | Android Asynchronous programming
Visual Studio | Windows Form | MS SQL | Dizionario
Parole chiave: