Con l'arrivo di Windows 8 è arrivato anche il Windows Store, un marketplace di applicazioni di "terze parti", dove chiunque può vendere le proprie applicazioni per WinRT. Innanzitutto, per "terze parti" si intendono tutti gli ISV e professionisti, fino ad arrivare ai programmatori amatoriali, esattamente come ciò che già avviene con i vari store di Microsoft, Apple, Google, Intel, etc.
Inoltre, con la definizione "applicazioni per WinRT" intendiamo una serie di applicazioni particolari, chiamate anche appunto applicazioni Windows Store, che non hanno la stessa UI delle applicazioni desktop ma che, soprattutto, non ne condividono il runtime.
Il runtime delle applicazioni Windows Store è infatti diverso dal runtime delle applicazioni tradizionali desktop. Sebbene infatti una applicazione Windows Store ed una applicazione Desktop condividano il kernel di Windows, la prima poggia su una infrastruttura che si chiama Windows Runtime o, in breve, WinRT, mentre le applicazioni tradizionali poggiano su .NET o su Win32.
Trascurando i dettagli dell'ambiente WinRT, che si merita di certo una trattazione a parte, è necessario sapere, per poter continuare a leggere, almeno alcuni punti fondamentali per la comprensione di questo nuovo modello di sviluppo:
- Il contesto di ogni applicazione è isolato, esattamente come avviene su Windows Phone.
- Le applicazioni scritte per il Windows Store devono essere "touch-ready" in quanto Windows RT è distribuito sui tablet.
- Una applicazione WinRT può essere distribuita sui dispositivi attraverso il Windows Store (a pagamento o gratuitamente) e non può essere copiata-incollata tra dispositivi.
- Lo sviluppo può avvenire in diverse modalità tra, cui, di maggiore rilevanza:
- Codice (VB, C#, C++) + XAML
- HTML5 + JS + CSS3
- C++ nativo e/o DirectX
Passando oltre alla doverosa introduzione, si intuisce la potenzialità di questo nuovo ecosistema che, approfittando dei milioni di dispositivi Windows presenti sul mercato, può generare dei profitti notevoli per i produttori di applicazioni.
Infine, coniugando questo fattore al mondo Ultrabook, le potenzialità sono ancora di più, considerando che un Ultrabook, a differenza di un laptop "normale", spesso ha:
- I sensori di movimento, accelerazione, etc
- Una antenna NFC
- Un monitor touch screen
Che lo rendono quindi fruibile sia come laptop che come tablet, come una esperienza integrata percepita dall'utente. Sebbene infatti già di per sé il touch screen renda "l'esperienza Windows 8" più fluida per l'utente, l'accesso ai dispositivi permette una copertura completa dei due mondi Tablet+Desktop con un solo dispositivo.
Relativamente all'ambiente di sviluppo, per sviluppare applicazioni Windows Store è necessario avere installati sull'Ultrabook Windows 8 e Visual Studio 2012 ed attivare la licenza per sviluppatori, all'interno di Visual Studio stesso.
Accesso ai sensori
Benchè dalle applicazioni Windows Store noi sviluppatori non abbiamo l'accesso e il controllo completo del sistema, sono state fornite delle API molto complete relativamente all'interoperabilità con i dispositivi hardware, come ad esempio quelli di connettività (WiFi, BT, NFC) e quelli sensoriali (Accelerometro, Giroscopio, etc).
Accelerometro
Ricordiamo che l'accelerometro fornisce le tre componenti di accelerazione istantanea del dispositivo lungo le tre assi X, Y e Z. Per ottenere l'accelerometro di default dell'Ultrabook:
// Ottenere un istanza dell'accelerometro
var accelerometer = Accelerometer.GetDefault();
Sensore di luce ambientale
Il sensore di luminosità fornisce un valore, espresso in LuX, corrispondente all'intensità luminosa percepita dalla finestra del sensore (di solito posizionata in prossimità della webcam). Per ottenere il sensore di default dell'Ultrabook:
// Ottenere un istanza dell'accelerometro
var ambientLight = LightSensor.GetDefault();
Bussola digitale
Il sensore di forza magnetica fornisce un valore poco significativo per le persone comuni, motivo per cui esso è wrappato in un valore angolare che indica, appunto, l'angolo compreso tra la "punta" dell'Ultrabook e il nord rilevato1. Per ottenere il sensore di default dell'Ultrabook:
// Ottenere un istanza dell'accelerometro
var compass = Compass.GetDefault();
Giroscopio
Il sensore fornisce, per ogni asse di rotazione, un valore istantaneo di velocità angolare, per determinare l'intensità del moto rotante del dispositivo. Per ottenere il sensore di default dell'Ultrabook:
// Ottenere un istanza dell'accelerometro
var gyrometer = Gyrometer.GetDefault();
API dei sensori
Come si può notare, tutti e quattro i sensori sono accessibili tramite la stessa API e condividono inoltre alcuni comportamenti tipici. Ad esempio, se l'Ultrabook non dovesse supportare il dispositivo appena richiesto, la variabile avrà valore null
, motivo per cui ogni qualvolta si chieda un sensore al sistema operativo è necessario testarne l'effettiva valorizzazione, prima di utilizzarlo, al fine di evitare spiacevoli NullReferenceException
.
Valore corrente
Un altro fattore comune a ogni sensore è la presenza di una chiamata API comune per la lettura singola ed istantanea del valore del sensore:
AccelerometerReading result1 = accelerometer.GetCurrentReading();
LightSensorReading result2 = ambientLight.GetCurrentReading();
CompassReading result3 = compass.GetCurrentReading();
GyrometerReading result4 = gyrometer.GetCurrentReading();
Dove chiaramente cambierà il valore di ritorno, essendo fortemente tipizzato rispetto al tipo di dato passato dal sensore.
AccelerometerReading
conterrà:
Proprietà | Accesso | Descrizione |
---|---|---|
AccelerationX |
Sola-lettura | accelerazione normalizzata a G, lungo l'asse X |
AccelerationY |
Sola-lettura | accelerazione normalizzata a G, lungo l'asse Y |
AccelerationZ |
Sola-lettura | accelerazione normalizzata a G, lungo l'asse Z |
Timestamp |
Sola-lettura | momento in cui è stato effettuato il campionamento |
LightSensorReading
conterrà:
Proprietà | Accesso | Descrizione |
---|---|---|
IlluminanceInLux |
Sola-lettura | il livello di luminosità in lux |
Timestamp |
Sola-lettura | istante in cui è stato effettuato il campionamento |
CompassReading
conterrà:
Proprietà | Accesso | Descrizione |
---|---|---|
HeadingMagneticNorth |
Sola-lettura | angolo in gradi rispetto al nord magnetico |
HeadingTrueNorth |
Sola-lettura | angolo in gradi rispetto al nord |
Timestamp |
Sola-lettura | istante in cui è stato effettuato il campionamento |
GyrometerReading
conterrà:
Proprietà | Accesso | Descrizione |
---|---|---|
AngularVelocityX |
Sola-lettura | velocità angolare, in gradi al secondo, lungo l'asse X |
AngularVelocityY |
Sola-lettura | velocità angolare, in gradi al secondo, lungo l'asse Y |
AngularVelocityZ |
Sola-lettura | velocità angolare, in gradi al secondo, lungo l'asse Z |
Timestamp |
Sola-lettura | istante in cui è stato effettuato il campionamento |
Meccanismo di notifica
Tutti i sensori descritti (ed anche gli altri non menzionati), aderiscono ad un pattern che permette ad un cliente di registrarsi alle successive notifiche di nuove letture, invece che obbligarlo a fare polling sul sensore. Il pattern prevede la registrazione di un evento ReadingChanged a cui si possono attaccare le seguenti signatures:
void gyrometer_ReadingChanged(Gyrometer sender, GyrometerReadingChangedEventArgs args)
{
throw new NotImplementedException();
}
void compass_ReadingChanged(Compass sender, CompassReadingChangedEventArgs args)
{
throw new NotImplementedException();
}
void ambientLight_ReadingChanged(LightSensor sender, LightSensorReadingChangedEventArgs args)
{
throw new NotImplementedException();
}
void accelerometer_ReadingChanged(Accelerometer sender, AccelerometerReadingChangedEventArgs args)
{
throw new NotImplementedException();
}
Dove, in ognuno degli argomenti args è esposta la proprietà Reading che contiene il valore della lettura, wrappato dai tipi specifici sopra citati.
Punti di attenzione
Utilizzare i sensori è semplice ma può portare a problemi di eccessivo consumo di batteria. Al fine di evitare spiacevoli sorprese è meglio impostare sul sensore un valore abbastanza alto della proprietà ReportInterval, in modo da indicare al sistema operativo di notificarci solo in occasione dello scadere del periodo selezionato. Questa opzione è presente su tutti i sensori ed è il caso di impostarla prima della valorizzazione del delegate di notifica:
accelerometer.ReportInterval = 1000;
accelerometer.ReadingChanged += accelerometer_ReadingChanged;
ambientLight.ReportInterval = 1000;
ambientLight.ReadingChanged += ambientLight_ReadingChanged;
compass.ReportInterval = 1000;
compass.ReadingChanged += compass_ReadingChanged;
gyrometer.ReportInterval = 1000;
gyrometer.ReadingChanged += gyrometer_ReadingChanged;
Per valore "abbastanza alto" si intende il minimo valore che rende l'applicazione valida, ovvero quell'intervallo minimo per cui ha senso campionare quel particolare dato.
Prossimità e NFC
Gli Ultrabook rappresentano un vero crossover tra mondo desktop e mondo tablet/touch, a tal punto che alcuni vendor hanno introdotto nella loro gamma Ultrabook convertibili. Quindi, proprio nella qualità di device "dei due mondi", l'Ultrabook introduce nel mondo desktop alcune funzionalità che fino ad oggi abbiamo visto solo nel mondo mobile: la radio NFC.
NFC sta per Near Field Communication, ovvero Comunicazione in prossimità, motivo per cui quando si parla di applicazioni proximity-aware oggi, si intendono applicazioni che fanno uso della radio NFC. Una radio NFC è un banale ricevitore/trasmettitore di segnale ad alta frequenza e a bassa velocità (13,56MHz con 424 Kbit/s) con alcune interessanti caratteristiche. La prima è che prende spunto dalle tecnologie RFId per l'identificazione dei dispositivi passivi su corte distanze: questo significa che, ponendo per esempio un dispositivo passivo in prossimità della radio NFC, essa imprimerà una corrente al dispositivo passivo in modo da instaurare una comunicazione dei dati presenti nel dispositivo.
La seconda cosa interessante (che lo differenzia dall'RFiD) è la possibilità di instaurare un canale di comunicazione bidirezionale, permettendo alla radio di leggere e scrivere sul dispositivo remoto, il tutto contact-less.
Proximity API
Windows 8 raccoglie tutta l'API dell'accesso ai dispositivi NFC all'interno del namespace Windows.Networking.Proximity
. La classe fondamentale di accesso all'NFC è la ProximityDevice, che espone un metodo statico per ottenere il dispositivo di default (analogamente a quanto visto per i sensori).
// Ottiene una istanza per la radio NFC
var nfc = ProximityDevice.GetDefault();
Una volta ottenuto il riferimento (e testato che non sia null) potremo compiere, fondamentalmente, due operazioni: una scrittura o una lettura. Il principio della lettura è mettersi in ascolto di un messaggio fino a quando un dispositivo non passerà in prossimità della radio; la scrittura è analoga e nell'istante della prossimità stabilità verranno inviati dei dati al dispositivo remoto.
Considerate queste due funzioni base, possono delinearsi tre modalità di utilizzo tipico di un NFC:
- Emulazione di carte di pagamento: la recente funzione Wallet di Windows Phone 8 mostra come, con l'NFC, il telefono possa essere usato come strumento di pagamento presso molti esercenti americani già in possesso di opportuni lettori di NFC. In questo caso il dispositivo radio si "comporta" da smart-card.
- Comunicazione peer-to-peer: due dispositivi NFC possono scambiare dati in half-duplex se messi a contatto, per cui si possono implementare soluzioni applicative di condivisione informazioni tra device fisicamente vicini.
- Lettore/Scrittore: questa modalità prevede che un dispositivo attivo legga/scriva un dispositivo passivo (Tag).
Ora vedremo come implementare il terzo caso.
Lettore/Scrittore di Tag
Un Tag NFC è un qualsiasi oggetto (di solito un adesivo, una carta plastificata) che contiene un microchip che possa contenere una modesta quantità di dati (da pochi bytes ad alcuni KBytes). All'interno del Tag NFC possono essere salvati dati binari, con opportune limitazioni di spazio: usualmente si memorizzano URL, numeri di telefono e/o comunque informazioni "brevi".
Il formato di salvataggio del dato all'interno di un Tag NFC è l'NDEF (NFC Data Exchange Format), standard de facto che permette ai vendor di implementare il software di lettura/scrittura in modo analogo su vari dispositivi.
NDEF
Una volta compreso il formato NDEF è svelato interamente il processo di lettura/scrittura di un Tag. Il formato NDEF prevede un singolo messaggio che al suo interno contenga un numero arbitrario di NDEF Record, i quali a loro volta sono particolari strutture dati composte da payload di dimensioni variabili, i cui metadati sono forniti in un apposito header in testa al payload stesso (come si può notare in figura).
La composizione di un messaggio NDEF
Analizzare il contenuto esadecimale di un record NDEF è il punto di partenza per leggere un Tag NFC. Tuttavia, molte delle API di alto livello a nostra disposizione permettono una lettura/scrittura "più semplice", tramite opportuni wrapper.
Lettura di un Tag
Per leggere un tag è necessario un codice di questo tipo:
private void InizioAttesaContenutoURI()
{
// Ottiene una istanza per la radio NFC
ProximityDevice proximityDevice = ProximityDevice.GetDefault();
// Prosegue solo se la radio esiste ed è disponibile
if(proximityDevice != null) {
// iscrizione agli eventi di lettura del tipo WindowsUri
proximityDevice.SubscribeForMessage("WindowsUri",
// registra una Lambda alla notifica avvenuta
(sender, message) =>
{
// Prende la stringa dal contenuto binario
String uri = CryptographicBuffer.ConvertBinaryToString(BinaryStringEncoding.Utf16LE, message.Data);
}
);
}
}
Questo codice permette tuttavia la registrazione delle notifiche solo in caso di ricezione di un contenuto di tipo WindowsUri, mentre i tipi oggi in circolazione sono molti. Nel caso in cui volessimo registrarci alle notifiche di un qualsiasi contenuto del Tag, dovremo specificare il Type "NDEF", nel metodo SubscribeForMessage, in modo da essere notificati in qualsiasi caso.
Scrittura di un Tag
La scrittura di un Tag da una applicazione Windows Store è analoga all'operazione di lettura, forse addirittura più semplice:
private void InizioAttesaContenutoURI()
{
// Ottiene una istanza per la radio NFC
ProximityDevice proximityDevice = ProximityDevice.GetDefault();
// Prosegue solo se la radio esiste ed è disponibile
if(proximityDevice != null) {
// Pubblica il messaggio (la chiamata è bloccante)
var tagWritingMessageId = proximityDevice.PublishBinaryMessage("WindowsUri:WriteTag",
CryptographicBuffer.ConvertStringToBinary(uri, BinaryStringEncoding.Utf16LE,
(sender, message) =>
{
// Viene norificata l'avvenuta trasmissione e scrittura del tag
}
);
}
}
In questo modo ho correttamente letto e scritto un URI all'interno di un Tag NFC.
Conclusioni
In questo articolo abbiamo introdotto lo sviluppo di applicazioni Windows Store, con particolare riferimento alle applicazioni sensors-based, più un approfondimento dedicato sulla tecnologia NFC e su come interagire dall'Ultrabook, con i Tag oggi in commercio.
Riferimenti