È stato rilasciato il nuovo "gioiellino" di Microsoft: Windows Phone SDK 7.1, che ci permetterà di sviluppare applicazioni per Windows Phone 7.5 Mango.
Se avete sviluppato per Windows Mobile e non siete passati a Windows Phone 7 perché c'erano delle cose che mancavano non avete più scuse. Tutto quello che, dal mio punto di vista, non era presente in Windows Phone 7 è stato aggiunto a Mango. Se invece non avete mai sviluppato per Windows Phone dovete provarci: la curva di apprendimento è ottima, tutti i tool sono gratis, e la piattaforma è in piena espansione. È sempre meglio partire prima per aumentare le probabilità di successo.
In questo articolo esamineremo velocemente tutte le nuove funzionalità che vi consentiranno di mettere alla prova la vostra creatività.
Prima di andare avanti, ecco il link per:
- scaricare Windows Phone SDK
- assistere al Webcast su tutte le novità su Mango organizzato da Microsoft Italia! Assolutamente da non perdere
Nuovi tool di sviluppo
Partiamo dagli strumenti di sviluppo. Con la versione Beta 2 dei tool di sviluppo per Mango abbiamo tutto il necessario per iniziare:
- Visual Studio Express for Windows Phone
- Expression Blend for Windows Phone
- le librerie
- l'emulatore
Inoltre gli sviluppatori registrati possono aggiornare il telefono Windows Phone alla versione di Mango compatibile con l'ultimo aggiornamento dei tool.
Uno dei punti di forza di questa nuova release risiede nella la possibilità di sviluppare per tutte due le versioni (Windows Phone 7.0 e Windows Phone 7.1). In questo modo è possibile sviluppare, pubblicare, mantenere applicazioni per la versione attuale del Marketplace, ma anche sperimentare e sviluppare per la nuova.
Per il momento il Marketplace di Mango non è ancora aperto agli sviluppatori ma per essere tra i primi a sfruttare le nuove funzionalità bisogna iniziare subito.
Sistema operativo nuovo, emulatore nuovo
Il nuovo emulatore ci permette di testare sia progetti 7.0 che 7.1. La novità più "scenografica" che troviamo è la possibilità di simulare l'accelerometro e la localizzazione. L'interfaccia è semplice da usare e intuitiva.
Accelerometro e geolocalizzazione
Per l'accelerometro appare un rendering del dispositivo ed è possibile, con il mouse, muovere il dispositivo in uno spazio 3D. Esiste anche la possibilità di scatenare delle sequenze preregistrate per emulare dei gesti come ad esempio lo scuotere il dispositivo o la rotazione secondo un determinato percorso:
Per la parte di geolocalizzazione appare una mappa di Bing Maps e premendo nei vari punti di questa mappa si scatenano dei eventi della classe GeoCoordinateWatcher (vedremo un esempio più tardi). Volendo si possono simulare dei percorsi preregistrati.
Misurare le Performance
Oltre a queste funzionalità il nuovo ambiente di sviluppo permette di misurare ed "analizzare" le prestazioni delle applicazioni.
Sul Marketplace di Windows Phone 7 molte delle recensioni negative sono dovute alla lentezza delle applicazioni, blocchi, interfaccia utente non fluida, applicazioni che si chiudono all'improvviso. Questi problemi possono fare impazzire gli sviluppatori se non sanno da dove iniziare a risolverli.
Ora, grazie al Windows Phone Performance Analysis, possiamo vivisezionare le nostre applicazioni e trovare i colli di bottiglia.
In ogni caso, prima di pubblicare un'applicazione sul Marketplace, è sempre consigliabile effettuare qualche test con un dispositivo reale in quanto l'emulatore non è paragonabile, come hardware, ad un telefono.
Isolated Storage Explorer
Nella Beta 2 è stato aggiunto anche l'Isolated Storage Explorer. Usando questo tool si può interagire con il supporto dati della nostra applicazione, direttamente dal PC.
Risulta molto utile per scattare screenshot dell'applicazione che gira su un dispositivo e non nell'emulatore, in quanto basterà salvare la bitmap corrente alla pressione di un tasto (o ad esempio allo scuotimento del device) e recuperarla poi dal PC.
L'eseguibile da usare lo trovate nella cartella Programmi
del PC:
Program Files [(x86)]Microsoft SDKsWindows Phonev7.1ToolsIsolatedStorageExplorerTool
Le nuove classi
Le nuove funzionalità dell'emulatore e di Visual Studio saranno utili soprattutto al debugging, ma è con le nuove classi che possiamo scatenare la nostra creatività e la voglia di sviluppare con Mango. È difficile decidere l'ordine giusto per presentare quanto è stato aggiunto: basta guardare questa immagine per capire la quantità di nuove funzionalità:
Cercheremo di esaminarle nelle prossime pagine.
Mango e la gestione della fotocamera
Iniziamo ad esaminare le nuove classi partendo da quelle relative alla fotocamera, una delle funzionalità più importanti aggiunte in Mango.
Poter interagire, in tempo reale, con lo streaming della fotocamera permette sviluppare applicazioni di realtà aumentata, scansione di codici a barre, elaborazione foto. Ci sono due possibilità di lavorare con la fotocamera:
- la classe Webcam di Siliverlight 4
- la nuova classe PhotoCamera
La classe Webcam
Se avete già sviluppato su Silverlight 4 sicuramente conoscete la classe Webcam. La stessa è adesso disponibile in Mango e potete riutilizzare il vostro codice desktop sul telefono. Inoltre la classe permette la registrazione dello streaming video e audio in un file MP4 su IsolatedStorage
con anche il supporto per il formato Y (grayscale).
Ecco il codice per salvare il file MP4:
CaptureSource source;
FileSink sink;
private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
{
source = new CaptureSource();
sink = new FileSink();
sink.CaptureSource = source;
sink.IsolatedStorageFileName = @"CaptureVideo.mp4";
}
private void startCameraButton_Click(object sender, RoutedEventArgs e)
{
if (source != null)
{
source.Stop();
source.VideoCaptureDevice = CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice();
VideoBrush videoBrush = new VideoBrush();
videoBrush.Stretch = Stretch.Uniform;
videoBrush.SetSource(source);
rectVideo.Fill = videoBrush;
if (CaptureDeviceConfiguration.AllowedDeviceAccess || CaptureDeviceConfiguration.RequestDeviceAccess())
{
source.Start();
}
}
}
La classe PhotoCamera
La seconda modalità è usando la classe PhotoCamera
. I vantaggi di questa classe sono la possibilità di avere una qualità migliore delle photo, si può gestire il pulsante hardware della fotocamera e anche l'uso del flash e messa a fuoco dell'immagine.
Il seguente codice usa un ciclo continuo per cercare di individuare un codice a barre 2D:
public Camera()
{
InitializeComponent();
App.videoBrush = new VideoBrush();
}
private PhotoCamera cam;
private Thread workerThread;
private Reader reader;
private Reader qreader;
public static System.Collections.Generic.Dictionary zxingHints =
new System.Collections.Generic.Dictionary()
{ { DecodeHintType.TRY_HARDER, true } };
private BinaryBitmap binaryBitmap;
private RGBLuminanceSource luminance;
private HybridBinarizer binarizer;
private Result result;
volatile bool focusing = false;
volatile bool running = true;
private System.Diagnostics.Stopwatch focusStopwatch = new System.Diagnostics.Stopwatch();
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
cam = new PhotoCamera();
cam.Initialized += new EventHandler(cam_Initialized);
cam.AutoFocusCompleted += new EventHandler(cam_AutoFocusCompleted);
video.Fill = App.videoBrush;
App.videoBrush.SetSource(cam);
base.OnNavigatedTo(e);
}
protected override void OnNavigatingFrom(System.Windows.Navigation.NavigatingCancelEventArgs e)
{
cam.AutoFocusCompleted -= new EventHandler(cam_AutoFocusCompleted);
cam.Dispose();
running = false;
base.OnNavigatingFrom(e);
}
void cam_AutoFocusCompleted(object sender, CameraOperationCompletedEventArgs e)
{
focusing = false;
}
void cam_Initialized(object sender, CameraOperationCompletedEventArgs e)
{
cam.FlashMode = FlashMode.Off;
reader = new com.google.zxing.oned.MultiFormatOneDReader(zxingHints);
workerThread = new Thread(Worker);
workerThread.Start();
}
private string previousResult;
void Worker()
{
byte[] cameraPreviewBuffer = new byte[640 * 480];
focusStopwatch.Start();
focusCamera();
while (running)
{
cam.GetPreviewBufferY(cameraPreviewBuffer);
focusCamera();
try
{
luminance = new RGBLuminanceSource(cameraPreviewBuffer, 640, 480, true);
binarizer = new com.google.zxing.common.HybridBinarizer(luminance);
binaryBitmap = new BinaryBitmap(binarizer);
result = reader.decode(binaryBitmap, zxingHints);
}
catch { }
if ((result != null) && (previousResult != result.Text))
{
Dispatcher.BeginInvoke(() => {
VibrateController.Default.Start(TimeSpan.FromMilliseconds(200));
txtBarcode.Text = result.Text;
previousResult = result.Text;
result = null;
});
}
};
}
private void focusCamera()
{
if (focusing == true | focusStopwatch.ElapsedMilliseconds < 1500)
return;
focusStopwatch.Reset();
focusStopwatch.Start();
cam.Focus();
focusing = true;
}
La PhotoCamera non è una risorsa condivisa perciò è importante chiamare il metodo Dispose
quanto non si usa più la risorsa.
Motion e Compass: bussola, giroscopio e accelerometro con Mango
Un'area importante in cui aggiungere funzionalità mancanti è quella della gestione dei sensori. Nella versione precedente gli sviluppatori avevano accesso solo all'accelerometro anche se quasi tutti i dispositivi hanno anche la bussola digitale. Mango aggiunge supporto per la bussola digitale e per il giroscopio (hardware opzionale nei requisiti di Windows Phone).
Inoltre è stata introdotta la classe Motion in grado di interpolare le informazioni rilevate da tutti i sensori presenti sul dispositivo. È sempre consigliato usare la classe Motion in quanto meno sensibile alle interferenze.
I sensori sono delle risorse condivise che significa che più di una applicazione può usarli nello stesso tempo. È comunque consigliabile chiudere la comunicazione con i sensori quanto l'applicazione non li usa più.
Per la programmazione bisogna usare l'assembly Microsoft.Devices.Sensors
. Tutte le classi hanno la proprietà IsSupported che indica se il sensore è disponibile o no nel dispositivo usato. Ogni classe contiene l'evento CurrentValueChanged che indica che ci sono dei valori nuovi rilevati disponibili.
Le classi Compass e Motion contengono anche l'evento Calibrate
che gli sviluppatori devono sottoscrivere e presentare una interfaccia per la calibrazione quando questo evento viene scatenato.
Il seguente esempio presenta un semplice esempio per un programma di bussola digitale:
Compass compass;
bool compassCalibrating;
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
if (Compass.IsSupported)
{
compass = new Compass();
CompassSupported = "Compass non supportato";
compass.CurrentValueChanged += compass_CurrentValueChanged;
compass.Calibrate += compass_Calibrate;
compass.Start();
}
else
CompassSupported = "Compass supportato";
base.OnNavigatedTo(e);
}
protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
{
if (null != compass)
{
compass.CurrentValueChanged -= compass_CurrentValueChanged;
compass.Calibrate -= compass_Calibrate;
compass.Stop();
compass.Dispose();
compass = null;
}
base.OnNavigatedFrom(e);
}
void compass_Calibrate(object sender, CalibrationEventArgs e)
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
//fa vedere l'interfaccia per la calibrazione
stkCalibration.Visibility = Visibility.Visible;
imgRose.Visibility = Visibility.Collapsed;
ContentPanel.Background = new SolidColorBrush(Colors.White);
compassCalibrating = true;
});
}
void compass_CurrentValueChanged(object sender, SensorReadingEventArgs<CompassReading> e)
{
//Compass in calibrazione
if (compassCalibrating)
{
//Se il valore è abbastanza "pulito" il sensore
//non è più in calibrazione
if (e.SensorReading.HeadingAccuracy < 10)
{
compassCalibrating = false;
//nasconde l'interfacia di calibrazione
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
imgRose.Visibility = Visibility.Visible;
stkCalibration.Visibility = Visibility.Collapsed;
ContentPanel.Background = new SolidColorBrush(Colors.Transparent);
});
}
}
if (!compassCalibrating)
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
imageRotation.Angle = -e.SensorReading.TrueHeading;
});
}
Mango, la gestione del Multitasking
Il multitasking in Mango non è il solito concetto di multitasking. Per multitasking si intendono due cose:
- Fast App Switching
- Background agent
Fast App Switching
Per Fast App Switching si intende la possibilità di passare velocemente da un'applicazione all'altra.
Per fare questo è stato introdotto uno nuovo stato per le applicazioni dormant: quando non è più attiva la nostra applicazione "dorme" e non ha più il controllo del dispositivo finché non è l'utente a "svegliarla" portandola in foreground.
Nello stato dormant il processo della nostra applicazione rimane attivo ma vengono terminate le connessioni di rete e la comunicazione con i sensori, per questo il risveglio della applicazione è quasi istantaneo e ci da l'impressione di multitasking.
Il sistema operativo può comunque terminare la nostra applicazione (se ha bisogno delle risorse - memoria) attivando così lo stato Tombstoned. Per questo motivo è utile salvare lo stato delle applicazioni in fase di sospensione.
Le applicazioni nello stato Dormant non possono eseguire codice in background.
Background agent
Se l'applicazione ha bisogno di eseguire codice in background bisogna usare dei background agent. Sono servizi che possono eseguire codice in background.
Su ogni dispositivo è possibile avere un massimo di 18 agenti attivi nello stesso tempo. L'utente dispone di un'interfaccia per rimuovere un agent. La vita massima di ogni agent è di 14 giorni ma vengono rinnovati ad ogni avvio della nostra applicazione.
Gli agenti sono stati studiati per avere un impatto minimo sulla batteria e sulle risorse hardware, per questo non possono interagire con l'interfaccia utente, non si possono interfacciare con la fotocamera né con i sensori, non possono usare le librerie XNA o riprodurre audio in background senza l'ausilio della Api per il background audio.
È invece possibile aggiornare le tile, mostrare messaggi toast, usare le socket, la connettività e l'accesso all'Isolated Storage.
Esistono due tipi di background agent:
- PeriodicTask: viene eseguito ogni 30 minuti e ha 15 secondi per finire tutte le operazioni da eseguire
- OnIdleTask: è eseguito solo quando il dispositivo è in carica e in questo caso l'intervallo di tempo a disposizione è di 10 minuti
Per registrare un PeriodicTask bisogna prima creare un progetto di tipo "Windows Phone ScheduledTask Agent" che sarà il codice eseguito in background dalla nostra applicazione.
protected override void OnInvoke(ScheduledTask task)
{
//TODO: Add code to perform your task in background
ShellToast toast = new ShellToast
{
Title = "Questa è una notifica",
Content = "Sono un Background Agent "
};
toast.Show();
NotifyComplete();
}
Una volta creato un progetto di tipo "Windows Phone Application" e si aggiunge una referenza all'progetto dell' agent. Visual Studio genera, in modo automatico, il collegamento tra le due entità usando il file WMAppManifest.xml
.
<ExtendedTask Name="BackgroundTask">
<BackgroundServiceAgent Specifier="ScheduledTaskAgent"
Name="PeriodicTaskSample"
Source="PeriodicTaskSample"
Type="PeriodicTaskSample.ScheduledAgent" />
</ExtendedTask>
Il codice da aggiungere per registrare lo agent è:
private void btnScheduleTask_Click(object sender, RoutedEventArgs e)
{
PeriodicTask task = new PeriodicTask("PeriodicTaskSample")
{
Description = "Demo periodic task",
ExpirationTime = DateTime.Now.AddDays(7)
};
ScheduledActionService.Add(task);
}
Inoltre è possibile usare BackgroundNotification
per creare delle notifiche di tipo alarm direttamente dalla vostra applicazione, BackgroundAudioPlayer
per la riproduzione audio e BackgroundTransfer
per il trasferimento dei file.
BackgroundTransferRequest è una classe alternativa a WebClient e HttpWebRequest che permette di trasferire dati verso e dalla rete. Le richieste fatte con questa classe non vengono terminate quanto l'applicazione non è più attiva. Usando la proprietà TransferProperties
possiamo impostare le condizioni necessarie per eseguire il trasferimento, ad esempio il dispositivo deve collegato ad una rete WiFi, in ricarica.
Socket
Una delle aggiunte più importanti di Mango sono i socket. La classe Socket è la stessa di Silverlight 4 che significa che il vostro codice può essere migrato facilmente. Personalmente ho portato, senza fatica, una classe che usava i socket scritta per una applicazione WPF di domotica. Usando questa classe è possibile creare delle applicazioni di chat, giochi multiplayer, stampare direttamente sulle stampanti di rete o stampanti mobili, comunicazione Machine2Machine, applicazioni VoiP e Ftp.
// creazione endpoint
var ipAddress = IPAddress.Parse(host);
var endpoint = new IPEndPoint(ipAddress, port);
// conversione da test a byte[]
var message = string.Format("{0};{1}", Message.Text.Length, Message.Text);
var buffer = Encoding.UTF8.GetBytes(message);
// creazione event args
var args = new SocketAsyncEventArgs();
args.RemoteEndPoint = endpoint;
args.Completed += SocketAsyncEventArgs_Completed;
args.SetBuffer(buffer, 0, buffer.Length);
// creazione nuovo socket
var socket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp);
bool completesAsynchronously = socket.ConnectAsync(args);
SocketAsyncEventArgs_Completed(args.ConnectSocket, args);
DataBase
Ecco una delle funzionalità più importanti di Mango, il supporto per Sql Ce. Non è il solito concetto di database con istruzioni di tipo Create Table
e Select
. Si una invece LINQ2Sql. Per creare un database bisogna seguire i seguenti passi:
- Implementare le classi del database: tabelle, colonne, datacontext
- Creare un nuovo database (file .sdf) su Isolated Storage
- Popolare il database con dati
Le classi del database
Prima creiamo la classe che rappresenta la nostra tabella (nel progetto dovete aggiungere un riferimento a System.data.linq
). Ecco una semplice classe Persona:
[Table]
public class Persona
{
[Column(IsPrimaryKey = true)]
public string CodiceFiscale { get; set; }
[Column]
public string Nome { get; set; }
[Column]
public string Cognome { get; set; }
[Column]
public string Indirizzo { get; set; }
}
Dopo bisogna creare il DataContext che è una classe "proxy" tra il database e la nostra applicazione. Questa classe deve ereditare la classe System.Data.Linq.DataContext
.
public class PersoneDataContext : DataContext
{
public Table Persone;
public PersoneDataContext(string connection) : base(connection) { }
}
Creare il file del DB
Per creare il database bisogna chiamare la funzione CreateDatabase presente già nel dataContext:
PersoneDataContext db = new PersoneDataContext("isostore:/DB_Persone.sdf");
if (!db.DatabaseExists())
db.CreateDatabase();
Popolare il DB
Persona p1 = new Persona() { CodiceFiscale = "ESEMPIO", Cognome = "ROSSI", Nome = "MARIO" };
db.Persone.InsertOnSubmit(p1);
db.SubmitChanges();
Per leggere dei valori dal database bisogna usare la sintassi LINQ:
var q = from p in db.Persone
orderby p.Nome,p.Cognome
select p;
List<Persona> persone = q.ToList();
Conclusioni
Se questo piccolo assaggio di Mango vi ha incuriosito, potete scaricare scaricare i tool di sviluppo e iniziare a provare di persona. Queste sono solo alcune delle 1500 API introdotte della nuova versione torneremo a parlarne in altre occasioni.