Irgendwie bin ich mit Twitter immer noch nicht so ganz warm geworden. Aber jedes Mal, wenn ich etwas herumstöbere, finde ich etwas Interessantes. Heute ist es eine coole und anschauliche Infografik zu Codebeispielen für die Windows Phone 8 Entwicklung.
Matthias stellt in seinem Artikel noch 10 seiner Meinung nach besonders bemerkenswerte Codebeispiele vor. Da ich die meisten Themen bereits in der Serie 8 Tage Windows Phone 8 behandelt habe, gebe ich diese hier nicht wieder.
Das Foto stammt von Peter Juras, einem weiteren Mitglied der Gang und dem Entwickler von Lockslides.
Steve Behrendt von Avanade hat neulich nach einer Twitter-Konversation (siehe Bild unten) mit Derek, einem sehr aktiven Mitglied unserer Münchner App Mafia eine Münchner Windows Phone Developer Group ins Leben gerufen.
Ich selbst habe mich auch schon länger mit dem Gedanken getragen, den letzten Schritt aber nie gemacht. Umso mehr freue ich mich, dass so etwas plötzlich „von selbst“ passiert.
Steve hat für die Organisation der #MUCWPDEV Treffen auf Meetup.com eine Gruppe angelegt. Also fleißig anmelden:
Willkommen zum letzten Artikel der Serie 8 Tage Windows Phone 8, welche sich mit den neuen Features der Windows Phone 8 Plattform beschäftigt. Heute, am Tag 8, besprechen wir die Brieftasche und In-App Käufe.
Die Brieftaschen-Anwendung ist als Ort gedacht, in welchem alle möglichen Anwendungen Dinge wie Deals, Kreditkarteninformationen, Bonuspunkte und dergleichen speichern können. Mit Hilfe der Wallet API können Sie Einträge zur Brieftasche hinzufügen, diese lesen, verändern und löschen.
Als erstes werden wir in unserer Beispielanwendung einen Deal zur Brieftasche hinzufügen. In diesem Beispiel erstellen wir einen Deal für ein Freibier, welchen der Benutzer in seiner Lieblingsbar in Anspruch nehmen kann. Bevor wir mit Code anfangen, statten wir unser WMAppManifest.xml mit der Wallet Fähigkeit aus.
Danach erstellen wir einen Button in der MainPage.xaml, um den neuen Freibier-Deal zu erzeugen.
In dem Click Event fügen wir den Code hinzu, um den neuen Deal in der Brieftasche zu erstellen. Wir beginnen, indem wir ein neues Deal Objekt erstellen und diesem eine eindeutige ID übergeben. Auf dem Deal Objekt können Sie eine Reihe von Eigenschaften setzen, wie z.B. Name, Bilder und einen Barcode. In unserem Beispiel habe ich einige der einfachen Eigenschaften mit Werten belegt. Gerade die Barcode Eigenschaft kann aber sehr praktisch sein, etwa, wenn es daran geht, den Deal in einer Bar einzulösen. Nachdem wir alle Eigenschaften gesetzt haben, können wir die Methode SaveAsync aufrufen und der Deal wird gespeichert. Es ist gängige Praxis, danach die Brieftasche zu öffnen, so dass der Benutzer sehen kann, was dort gespeichert wurde. Es gibt keinen speziellen Launcher für die Brieftasche. Stattdessen öffnen Sie eine spezielle URL — beginnend mit wallet:// — um die Brieftasche zu öffnen.
Wenn wir die Anwendung ausführen und auf den Free Beer Button drücken, wird sich die Brieftasche öffnen und unser Deal wird sichtbar sein. Wenn wir auf diesen klicken, sehen wir seine Details. Wenn Sie einen Barcode hinzufügen, wird dieser ebenfalls hier sichtbar sein. Weiterhin gibt es eine Möglichkeit, die zum Deal gehörige Anwendung zu öffnen. Dabei wird der Wert der Eigenschaft NavigationURI verwendet, die wir auf dem Deal Objekt setzen können. Sie können die Deals hier auch als verbraucht markieren oder diese aus Ihrer Brieftasche löschen.
Neben Deals können Sie auch Zahlungsmittel hinterlegen. Diese Objekte sind Dinge wie z.B. Bonuspunkte, die man beim Einkaufen sammelt. Oft kann man diese Punkte einlösen gegen irgendeine Belohnung. Die Erstellung eines Zahlungsmittels läuft fast genau so ab wie die Erstellung eines Deals. Wir verwenden diesmal nur ein PaymentInstrument Objekt. Die Klasse PaymentInstrument hat eine Eigenschaft, mit welcher Sie den Betrag setzen können. Diesen können Sie auch nachträglich noch erhöhen oder verringern.
Um die Zahlungsmittel in der Brieftasche zu verwenden, müssen Sie dem WMAppManifest.xml eine weitere Fähigkeit mit dem Namen ID_CAP_WALLET_PAYMENTINSTRUMENTS hinzufügen.
Zu guter Letzt können Sie der Brieftasche generische Objekte hinzufügen. Das könnte etwa eine Kundenkarte mit einem Barcode oder einer Nummer sein, welche Sie in einem Laden als Stammkunden identifiziert. Generische Einträge in der Brieftasche können Sie mit Hilfe des WalletTransactionItem erstellen.
In-App Käufe
Ein wirklich wichtiges Feature von Windows Phone 8 ist die Möglichkeit, In-App Käufe zu nutzen. In Windows Phone 7 konnten Sie nur zwischen kostenlosen und zahlungspflichtigen Apps (evtl. mit Testversion) wählen. In-App Käufe eröffnen ganz neue Möglichkeiten, mit einer App Geld zu verdienen. Sie könnten z.B. ein kostenloses Spiel entwickeln, in welchem der Nutzer neue Levels für einen gewissen Betrag kaufen kann. Ein weiteres Beispiel wäre eine Sport-Anwendung, in welcher Ihre Benutzer die Push-Benachrichtigungen für eine Saison abonnieren können, anstatt diese für immer zu bekommen. Wie also machen wir das?
In-App Käufe müssen im Entwicklerportal erstellt werden. Loggen Sie sich mit Ihrem Entwicklerkonto auf dev.windowsphone.com ein und laden Sie eine neue Anwendung hoch oder navigieren Sie zu den Details einer existierenden Anwendung.
Klicken Sie auf den Reiter Products und dann auf „Add in-app product“. Damit erstellen Sie ein neues Produkt, welches aus Ihrer App heraus erworben werden kann.
Zunächst müssen wir die Eigenschaften des In-App Produkts ausfüllen. Diese bestehen aus einem Namen und einem Identifikator. Dieser Identifikator ist wichtig, da wir über diesen das Produkt in unserer App ansprechen können.
Weiterhin können Sie festlegen, ob es sich um ein dauerhaftes oder ein zu verbrauchendes Produkt handelt. Dauerhafte Produkte können einmal erworben werden und stehen dem Nutzer danach uneingeschränkt zur Verfügung. Zu verbrauchende Produkte werden einmal gekauft und verfallen nach Benutzung.
Zum Schluss wählen wir einen Preis und drücken auf Save. Danach geben wir eine Beschreibung für unser Produkt an.
Bei der Beschreibung müssen Sie einen Titel, einen ausführlichen Beschreibungstext und ein Bild angeben. Drücken Sie danach wieder Save.
Jetzt sind wir bereit, das In-App Produkt einzureichen. Wenn wir Submit drücken, wird unser Produkt gespeichert und wir können es nach der Zertifizierung in unserer Anwendung verwenden.
Da wir jetzt ein In-App Produkt definiert haben, können wir versuchen, dieses aus unserer App heraus zu kaufen.
Fügen wir einen Button hinzu, um unsere 30 neuen Levels zu kaufen.
<Button x:Name="BuyLevelsButton" Content="buy 30 more levels!" Click="BuyLevelsButton_Click_1" ></Button>
Im Click Event fügen wir den Code hinzu, um unser Produkt zu kaufen. Als erstes holen wir uns eine Liste aller verfügbaren Produkte. Diese bekommen wir vom CurrentApp Objekt, indem wir die Methode LoadListingInformationAsync aufrufen. Da wir wissen, dass es nur ein Produkt gibt, nehmen wir eine Abkürzung und greifen gleich auf den ersten Eintrag zu. Wenn es mehrere Produkte gäbe, könnten Sie das gewünschte Produkt mit Hilfe der ID auswählen. Sobald wir eine Referenz auf das Produkt haben, können wir die Methode RequestProductPurchaseAsync aufrufen, um den Store zu öffnen. Dort hat der Nutzer die Möglichkeit, zu entscheiden, ob er diesen Extrakauf tätigen möchte. Nachdem der Nutzer seine Entscheidung getroffen hat, kommen wir zurück in unseren Code, wo wir überprüfen können, ob das Produkt nun auf Active gesetzt ist. Wenn dieser Wert true ist, müssen wir die Methode ReportProductFulfillment rufen, um den Store wissen zu lassen, dass die App den Kauf registriert hat. Anschließend können wir irgendwo eine Eigenschaft speichern, welche anzeigt, dass das zum Produkt gehörige Feature in der Anwendung freigeschaltet wurde.
private async void BuyLevelsButton_Click_1(object sender, RoutedEventArgs e)
{
var products = await CurrentApp.LoadListingInformationAsync();
var product1 = products.ProductListings.FirstOrDefault();
var boughtProduct = await CurrentApp.RequestProductPurchaseAsync(product1.Value.ProductId, true);
if (CurrentApp.LicenseInformation.ProductLicenses[product1.Value.ProductId].IsActive)
{
CurrentApp.ReportProductFulfillment(product1.Value.ProductId);
var saveBoughtProductstate = true;
}
}
Beachten Sie, dass wir in einem Produkt keinen Extra-Code oder sonstiges hinzufügen. Es handelt sich um nichts weiter als einen Boole’schen Wert, welcher auf true gesetzt wurde. In dem Beispiel, in dem der Nutzer 30 Levels kaufen kann, müssen diese bereits im Paket vorhanden sein. Sie deaktivieren einfach die entsprechenden Bereiche der Benutzeroberfläche, so lange der Wert durch den Kauf noch nicht auf true gesetzt wurde.
Ich mag die Möglichkeiten der In-App Käufe und glaube, dass dadurch mehr Firmen, etwa Zeitungen, dazu motiviert werden, Apps für die Windows Phone Plattform zu entwickeln.
Das war’s Leute. Die Serie 8 Tage Windows Phone 8 ist zum Ende gekommen. Hoffentlich hat Ihnen das Lesen dieser Serie etwas Spaß gemacht und Sie haben dabei etwas gelernt.
Wenn Sie neue Features vermisst haben sollten, lassen Sie es mich wissen. Wenn möglich, kann ich noch einige weitere Artikel schreiben.
Willkommen zurück zu meiner Blog-Serie 8 Tage Windows Phone 8. Heute, im vorletzten Artikel, befassen wir uns mit den Nahfeldsensoren, namentlich Bluetooth und NFC. Wichtig bei diesem Artikel ist, dass ich den Code mit dem Emulator nicht testen konnte, da Bluetooth und NFC nur mit einem richtigen Gerät getestet werden können. Da ich beim Schreiben dieser Artikel kein Gerät zur Verfügung hatte, kann es sein, dass die Beispiele nicht hundertprozentig funktionieren.
Um Bluetooth oder NFC zu nutzen, müssen Sie als erstes eine entsprechende Fähigkeit im WMAppManifest.xml hinzufügen.
Nachdem wir das getan haben, sind wir bereit, eine Anwendung zu bauen, welche mit einem anderen Gerät über Bluetooth kommuniziert. Die Kommunikation über Bluetooth funktioniert durch eine Art Client/Server Verbindung. Ein Telefon wird der Server, zu welchem sich das andere Telefon verbinden kann. Damit können beide Telefone kommunizieren.
Um den Server und den Client aufzusetzen, fügen wir zwei Buttons hinzu — einem zum Start des Servers und einen für den Client.
Im Click Event des Buttons für den Server fügen wir den folgenden Code hinzu, um ein PeerFinder Objekt zu erstellen, welches auf eingehende Verbindungsversuche hört. Wenn sich ein anderes Gerät mit unserem Telefon verbindet, wird ein ConnectionRequested Event gefeuert. Im EventHandler zeigen wir einfach eine MessageBox, um zu bestätigen, dass sich das andere Gerät verbunden hat.
Um die Verbindung mit dem Server herzustellen, müssen wir die Client-Seite implementieren. Im entsprechenden Click Event fügen wir den folgenden Code hinzu, um nach Server-Telefonen zu suchen und uns zu verbinden, wenn wir eins gefunden haben. Wir verwenden wieder den PeerFinder, um nach Verbindungspartnern zu suchen. Wenn die Methode FindAllPeers zurück kommt, prüfen wir, ob es einen aktiven Server gibt, indem wir mit der Eigenschaft Count die Anzahl der Ergebnisse abfragen. Wenn Count größer 0 ist, verbinden wir uns mit diesem Partner und prüfen seinen DisplayName.
protected async void ClientButton_Click_1(object sender, RoutedEventArgs e)
{
PeerFinder.Start();
var p = await PeerFinder.FindAllPeersAsync();
if (p.Count > 0)
{
_ss = await PeerFinder.ConnectAsync(p[0]);
string phoneName = p[0].DisplayName;
}
}
Nun, da wir zwei Telefone verbunden haben, können wir eine Nachricht von einem Telefon zum anderen schicken. Wir fügen der MainPage einen weiteren Button und den folgenden Code im Click Event hinzu. Damit schicken wir eine Nachricht von einem Telefon zum anderen.
private async void SendMessageButton_Click_1(object sender, RoutedEventArgs e)
{
if (_ss == null)
throw new Exception("Socket not initialized");
DataWriter dw = new DataWriter(ss.OutputStream);
dw.WriteString("Hello Windows Phone 8");
await dw.StoreAsync();
}
Wir verwenden den StreamSocket, welchen wir bei Herstellung der Verbindung initialisiert hatten, um die Nachricht zu schicken. Wenn der StreamSocket nicht initialisiert ist, feuern wir eine Exception. Andernfalls können wir weitermachen. Wir erstellen einen DataWriter basierend auf dem OutputStream des StreamSocket und verwenden die Methode WriteString, um die Nachricht über den DataWriter zu schicken. Jetzt können wir die Methode StoreAsync aufrufen, um die Daten tatsächlich an das verbundene Telefon zu senden.
Als letztes müssen wir die Nachricht auf dem Telefon noch empfangen. Wir verbinden den StreamSocket des PeerFinder und rufen eine neue private Methode WaitForMessage.
In dieser asynchronen Methode öffnen wir einen DataReader basierend auf dem InputStream des StreamSocket. Aus dem DataReader können wir zunächst die Länge der zu empfangenden Nachricht abfragen und anschließend die Nachricht selbst auslesen. Damit sind wir fertig mit der Übertragung einer Nachricht über Bluetooth.
async void PeerFinder_ConnectionRequested(object sender, ConnectionRequestedEventArgs args)
{
MessageBox.Show("Connected");
_ss = await PeerFinder.ConnectAsync(args.PeerInformation);
string message = await WaitForMessage();
}
private async Task<string> WaitForMessage()
{
DataReader dr = null;
while (true)
{
if (dr == null)
dr = new DataReader(_ss.InputStream);
await dr.LoadAsync(4);
uint messageLen = (uint)dr.ReadInt32();
await dr.LoadAsync(messageLen);
return dr.ReadString(messageLen);
}
}
NFC
Der zweite Nahfeldsensor in Windows Phone ist NFC. Mit NFC ist es möglich, NFC Tags zu lesen oder zwischen Telefonen zu kommunizieren. In folgenden Abschnitt wollen wir uns näher damit befassen, wie man per NFC zwischen Telefonen kommunizieren kann. Um Nachrichten per NFC zu senden und zu empfangen, fügen wir wieder mal zwei Buttons zu unserer MainPage.xaml hinzu.
Im Click Event des Send Buttons fügen wir den folgenden Code hinzu. Wir erstellen eine Instanz der Klasse ProximityDevice. Dann müssen wir nur noch die Methode PublishMessage auf diesem Objekt aufrufen. Im Beispiel senden wir einen String. Es ist aber auch möglich, binäre Daten zu übertragen.
Nachdem wir die Nachricht gesendet haben, müssen wir als letztes für heute nur noch die Nachricht empfangen. Fügen Sie den folgenden Code zum Click Event des Receive Buttons hinzu. Wir erstellen wieder eine Instanz der Klasse ProximityDevice und registrieren uns für eingehende Nachrichten. Das war’s. Wenn jetzt eine Nachricht eintrifft, wird die Methode NfcReceive aufgerufen und wir können die Nachricht auslesen.
private void NfcReceiveButton_Click_1(object sender, RoutedEventArgs e)
{
ProximityDevice pd = ProximityDevice.GetDefault();
if (pd != null)
{
long Id = pd.SubscribeForMessage("8days", NfcReceive);
}
}
private void NfcReceive(ProximityDevice sender, ProximityMessage message)
{
var m = message.DataAsString;
}
Wie Sie sehen, ist die Umsetzung von NFC in Windows Phone 8 wirklich einfach. Ich kann es nicht erwarten, bis Entwickler anfangen, diese Features in ihren Anwendungen zu nutzen. Alle momentan angekündigten Windows Phone 8 Geräte haben NFC Fähigkeiten — die Voraussetzungen sind also gelegt.
Morgen, am letzten Tag der Serie, werden wir uns mit den Features zur Brieftasche beschäftigen und wie man In-App Käufe realisiert.
Das Beispielprojekt zum heutigen Artikel können Sie hier herunterladen: http://sdrv.ms/QSJ2yx.
Herzlich willkommen zu einer weiteren Ausgabe meiner Serie 8 Tage Windows Phone 8. Heute, im sechsten Artikel, widmen wir uns den APIs zur Sprachsteuerung. Sprache auf Windows Phone 8 besteht aus drei wesentlichen Bausteinen: Text To Speech (Synthese), Speech To Text (Sprachverständnis) und Voice Commands (Sprachkommandos). In diesem Artikel wollen wir uns mit allen drei Aspekten auseinandersetzen.
Als erste der drei APIs zur Sprachsteuerung diskutieren wir die Text to Speech API zur Sprachsynthese. In Windows Phone 7 mussten Sie die Bing Speech API verwenden, wenn Sie einen geschriebenen Satz in Ton übersetzen wollten. In Windows Phone 8 ist diese API Teil der Windows Phone API. Unter der Haube verwendet diese höchstwahrscheinlich auch weiterhin die Bing Server.
Erstellen wir ein neues Projekt. Als erstes müssen wir die Capability zur Spracherkennung im WMAppManifest.xml hinzufügen. Mit Visual Studio 2012 können wir das direkt im Capabilities Tab machen.
Als nächstes platzieren wir einen Button auf der MainPage und schreiben die folgenden zwei Zeilen in den Click Event Handler zu diesem Button.
private void SayHelloButton_Click(object sender, RoutedEventArgs e)
{
SpeechSynthesizer ss = new SpeechSynthesizer();
ss.SpeakTextAsync("8 days of windows phone 8, day 6");
}
Wenn Sie die App ausführen, werden Sie merken, dass Sie wirklich nur diese zwei Zeilen brauchen, um geschriebenen Text in Sprache zu verwandeln. Großartig! Beachten Sie, dass die Methode SpeakTextAsync der Klasse SpeechSynthesizer eine async Methode ist. Sie können auch die Sprache und die Stimme des gesprochenen Schnipsels verändern. Fügen wir etwas mehr Code hinzu, um eine deutsche Ausgabe, gesprochen von einer männlichen Stimme zu bekommen:
private void SayGermanButton_Click_(object sender, RoutedEventArgs e)
{
SpeechSynthesizer ss = new SpeechSynthesizer();
VoiceInformation vi = InstalledVoices.All.Where(v => v.Language == "de-DE" && v.Gender == VoiceGender.Male).FirstOrDefault();
ss.SetVoice(vi);
ss.SpeakTextAsync("8 Tage von Windows Phone 8, Tag 6");
}
Es sind nur zwei Zeilen erforderlich, um die Sprache und das Geschlecht zu setzen (ja, es würde sogar auch mit einer gehen). Der Rest bleibt unverändert. Wenn wir den Button jetzt noch mal drücken (im Beispielprojekt habe ich einen zweiten Button hinzugefügt, um beide Fälle testen zu können), hören wir eine männliche deutsche Stimme. Kommen wir zum nächsten Thema, der Übersetzung von gesprochener Sprache in Text — Speech To Text.
Speech To Text
Die im vorherigen Abschnitt diskutierte Sprachsynthese ist schon sehr praktisch. Die Konvertierung von gesprochener Sprache in Text ist noch eindrucksvoller und nicht viel schwerer zu implementieren. Bevor wir anfangen, müssen wir der App die Fähigkeit zur Nutzung des Mikrofons verleihen. Machen Sie einen Haken bei der entsprechenden Box im Capabilities Reiter der Datei WMAppManifest.xml.
Nun, da wir die Fähigkeit zur Nutzung des Mikrofons haben, beginnen wir mit etwas Code zur Spracherkennung. Wir fügen einen Button hinzu, mit dem wir den Benutzer fragen, wie er sich gerade fühlt.
<Button x:Name="AskStatusButton" Content="how are you doing?" Click="AskStatusButton_Click"></Button>
<TextBlock x:Name="StatusText"></TextBlock>
<TextBlock x:Name="ConfidenceText"></TextBlock>
Im Code Behind fügen wir im Click Event zu diesem Button etwas Code zur Spracherkennung hinzu und geben das Ergebnis der Erkennung im StatusText TextBlock aus. Wir verwenden zudem einen weiteren TextBlock, um das Confidence Level (also die Angabe, wie sicher der Text der gesprochenen Sprache entspricht) der Spracherkennung auszugeben.
Im Click Event beginnen wir, indem wir ein SpeechRecognizerUI Objekt erstellen. Auf diesem SpeechRecognizerUI Objekt setzen wir einige Eigenschaften: die Eigenschaft ListenText ist der Text, welcher als Titel im Sprachdialog angezeigt wird (siehe das Bild unten). Die nächste Eigenschaft ist der ExampleText. Mit dieser Eigenschaft können Sie ein einfaches Beispiel für die Antwort des Benutzers angeben. Danach setzen wir die Eigenschaft ReadoutEnabled auf true, damit das Telefon den Text an den Benutzer zurück spricht. Als letzte Eigenschaft setzen wir ShowConfirmation, damit der gesprochene Text dem Benutzer schriftlich auf dem Bildschirm angezeigt wird.
private async void AskStatusButton_Click(object sender, RoutedEventArgs e)
{
SpeechRecognizerUI sr = new SpeechRecognizerUI();
sr.Settings.ListenText = "How are you doing?";
sr.Settings.ExampleText = "I'm doing fine";
sr.Settings.ReadoutEnabled = true;
sr.Settings.ShowConfirmation = true;
SpeechRecognitionUIResult result = await sr.RecognizeWithUIAsync();
if (result.ResultStatus == SpeechRecognitionUIStatus.Succeeded)
{
StatusText.Text = result.RecognitionResult.Text;
ConfidenceText.Text = result.RecognitionResult.TextConfidence.ToString();
}
}
Nachdem wir alle diese Eigenschaften gesetzt haben, können wir die Methode RecognizeWithUIAsync aufrufen, um die Spracherkennung zu starten. Beachten Sie, dass beim ersten Aufruf dieser Methode der Benutzer gefragt wird, ob er zustimmt, dass die Sprachaufnahme zur Verarbeitung an die Server von Microsoft gesendet wird. Wenn der Benutzer zustimmt, wird die Spracherkennung gestartet.
Nachdem die Spracherkennung abschlossen ist, können Sie das Ergebnis im Code abfragen. Das Ergebnis, welches von RecognizeWithUIAsync zurück kommt, enthält eine Text Eigenschaft und ein Confidence Level. Mit dem Confidence Level bekommen wir einen Hinweis, ob das Ergebnis brauchbar ist. Im Beispielprojekt zeigen wir beide Werte in den beiden TextBoxes auf der MainPage an.
In manchen Fällen wollen Sie den Benutzer nur zwischen einigen vorgegebenen Optionen auswählen lassen. Das ist ebenfalls sehr einfach mit der SpeechRecognizerUI Klasse umzusetzen. Wir fügen einen weiteren Button und zwei weitere TextBoxes zur MainPage hinzu um den Benutzer zu fragen, welcher Wochentag heute ist.
<Button x:Name="AskDayButton" Content="which day is it?" Click="AskDayButton_Click"></Button>
<TextBlock x:Name="DayText"></TextBlock>
<TextBlock x:Name="DayConfidenceText"></TextBlock>
Danach fügen wir den Code zum Click Event des Buttons hinzu. Dieser ist, bis auf einige Erweiterungen, fast der selbe wie vorher.
private async void AskDayButton_Click(object sender, RoutedEventArgs e)
{
SpeechRecognizerUI sr = new SpeechRecognizerUI();
sr.Settings.ListenText = "Which day is it today?";
sr.Settings.ExampleText = "Friday";
sr.Settings.ReadoutEnabled = true;
sr.Settings.ShowConfirmation = true;
sr.Recognizer.Grammars.AddGrammarFromList("answer", new string[] { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" });
SpeechRecognitionUIResult result = await sr.RecognizeWithUIAsync();
if (result.ResultStatus == SpeechRecognitionUIStatus.Succeeded)
{
DayText.Text = result.RecognitionResult.Text;
DayConfidenceText.Text = result.RecognitionResult.TextConfidence.ToString();
}
}
In der einzigen neuen Zeile geben wir die Liste der möglichen Antworten an. Die Spracherkennung wird die gesprochene Sprache nun nur noch gegen diese Wörter prüfen. In unserem Beispiel habe ich die Wochentage als mögliche Antworten angegeben.
Wenn Sie die Anwendung ausführen, werden Sie merken, dass nur noch die Wörter in der Liste als Antwort akzeptiert werden.
Voice Commands
Das letzte Thema für heute sind die Sprachkommandos. Mit Sprachkommandos kann der Benutzer Ihre App mit einer bestimmten Aufgabe starten oder eine Aufgabe innerhalb der App ausführen, wenn diese bereits läuft. Sprachkommandos bestehen immer aus 3 Teilen: Dem Namen der App, damit das Betriebssystem weiß, an welche App das Kommando zu richten ist, dem Namen des Kommandos und einem Satz, welcher eine Art Parameter für Ihr Kommando darstellt.
Um Sprachkommandos zu realisieren, müssen Sie eine XML Datei hinzufügen, welche die Definition der Sprachkommandos beinhaltet. Um diese VCD Datei hinzuzufügen, klicken Sie mit Rechts auf Ihr Projekt im Visual Studio und wählen sie Add New Item. Dann wählen Sie Voice Command Definition aus der Liste.
Standardmäßig erhalten Sie eine VCD Datei, welche bereits einige Beispielkommandos und –Sätze beinhaltet. Wir werden diese ändern, um ein Kommando zu definieren, welches die App startet, indem es den Benutzer fragt, welcher Tag heute, gestern oder morgen ist.
Als erstes müssen Sie in der VCD Datei das CommandPrefix definieren. Das ist das Wort, welches das Betriebssystem verwendet, um Ihre App zu finden. Danach fügen wir das Kommando hinzu. Dieses braucht ein Element ListenFor, womit dem Betriebssystem angegeben wird, auf welchen Text es hören soll. Zu dem Kommando können Sie weiterhin einen Satz angeben, wie ich es im Beispiel mit dem {day} gemacht habe. Dieser Satz, zusammen mit seinen Varianten, muss ebenfalls Teil der VCD Datei sein.
<?xml version="1.0" encoding="utf-8"?>
<VoiceCommands xmlns="http://schemas.microsoft.com/voicecommands/1.0">
<CommandSet xml:lang="en-US">
<CommandPrefix>8 days</CommandPrefix>
<Example>what day is it today?</Example>
<Command Name="DayToday">
<Example>What day is it?</Example>
<ListenFor>What day is it {day}</ListenFor>
<Feedback>Checking the day</Feedback>
<Navigate Target="/MainPage.xaml"/>
</Command>
<PhraseList Label="day">
<Item> today </Item>
<Item> tomorrow </Item>
<Item> yesterday </Item>
</PhraseList>
</CommandSet>
</VoiceCommands>
Wenn die Definition der Kommandos in unserer VCD Datei abgeschlossen ist, können wir diese beim Betriebssystem registrieren. Sie müssen dies nur einmal machen, wenn Ihre Anwendung zum ersten Mal ausgeführt wird. Der Einfachheit halber fügen wir die Registrierung in unserem Beispiel einfach dem Konstruktor der MainPage hinzu.
Das ist alles, was wir tun müssen, um unsere Anwendung per Sprache zu starten. Sie können die Spracherkennung auslösen, indem Sie den Windows Button etwas länger gedrückt halten. Sie sehen dann ein kleines „Ich höre zu …“ Fenster. Wenn Sie fragen „Was kann ich sagen?“ oder das Fragezeichen drücken, kommen Sie auf eine neue Seite mit der Erklärung, welche Kommandos verfügbar sind. Die erste Seite beinhaltet die Kommandos, die in das System eingebaut sind. Wenn Sie nach rechts wischen, sehen Sie eine Übersicht über die Apps, die Sprachkommandos anbieten. Unsere App ist hier aufgeführt. Wenn Sie auf die App klicken, sehen Sie die Liste der Kommandos, die von dieser App unterstützt werden.
Wenn wir jetzt den Windows Button gedrückt halten und sagen „8 days, What day is it tomorrow?“, wird unsere Anwendung starten und die MainPage anzeigen.
Das Sprachkommando wird der MainPage in Form des Querystring übergeben. In dem wir den Handler OnNavigatedTo überschreiben, können wir prüfen, welches Sprachkommando verwendet wurde und welcher Satz vom Benutzer gesprochen wurde.
Das Kommando und der Satz werden als separate Parameter übergeben. Wenn wir die App also starten und sie über das Sprachkommando aktivieren, sehen wir im Debugger das folgende:
Wie Sie sehen, gibt es einen Parameter „day“, welchen Sie beispielsweise in einem Switch Statement nutzen können, um eine geeignete Methode aufzurufen.
Das ist alles für heute zu den APIs zur Sprachsteuerung. Hoffentlich kommen Sie morgen wieder, um mehr über die APIs zu NFC und Bluethooth zu erfahren.
Willkommen zurück zu meiner Artikelserie 8 Tage Windows Phone 8. Heute, am fünften Tag der Serie, reden wir über die neuen Features im .NET 4.5 Framework und in der Sprache C# 5.0. Diese können Ihnen dabei helfen, tolle Windows Phone Anwendungen zu bauen. Die Features, auf die wir heute besonders eingehen wollen, sind die Schlüsselwörter async und await und der DataContractJsonSerializer.
Bei der Aktualisierung einer Anwendung von Windows Phone 7 auf Windows Phone 8 aktualisieren wir diese auch von Silverlight auf das .NET 4.5 Framework und die C# 5.0 Sprache. Damit bekommen Windows Phone Entwickler einige neue Features, die nicht wirklich Teil des SDK sind, die aber extrem nützlich für Sie als Entwickler von mobilen Anwendungen sind. Eines dieser neuen Features ist das async und await Muster. Damit wird das Bauen von asynchronen Anwendungen wesentlich vereinfacht und trägt damit indirekt zu einer reaktiven Benutzerschnittstelle für den Endnutzer bei. Das andere Thema, auf das ich heute eingehen möchte, ist die Verwendung des DataContractJsonSerializer. Diese Klasse hilft Ihnen, zwischen Objekten und JSON Strings zu serialisieren und zu deserialisieren. Das ist sehr nützlich, wenn man aus einer App auf Dienste im Web zugreift.
Unglücklicherweise verwenden (noch?) nicht alle APIs im SDK die async und await Schlüsselwörter. WebClient und HttpWebRequest verwenden dieses Feature nicht. Möglicherweise werden wir irgendwann den HttpClient wie unter Windows 8 verwenden können.
In der Beispielanwendung zum heutigen Artikel wollte ich idealerweise die Demonstration des async/await-Patterns und des JSON Serializers kombinieren. Da wir das async/await-Pattern nicht verwenden können, um eine Datei aus dem Web zu holen, laden wir einfach eine lokale Datei. Die APIs zum Zugriff auf lokale Dateien verwenden die Klasse Task und damit das async/await-Pattern. Fangen wir also an, eine sehr einfache TODO-Listen-Anwendung zu bauen.
Erstellen Sie ein neues Windows Phone 8 Projekt und fügen Sie eine Klasse ToDo hinzu. Diese werden wir zur Beschreibung eines Eintrags in der TODO-Liste verwenden. Um diese Daten später in einer JSON Datei speichern zu können, fügen wir einige Attribute hinzu, damit der DataContractJsonSerializer weiß, wie die Daten in eine JSON Datei serialisiert werden sollen. Fügen Sie das [DataContract] Attribut der ToDo Klasse hinzu und das [DataMember] Attribut allen Eigenschaften, die nach JSON serialisiert werden sollen. Standardmäßig wird der Serializer den Elementen in der JSON Datei die selben Namen geben wie die entsprechenden Eigenschaften in Ihrer Klasse. Wenn Sie möchten, dass die Elemente in der JSON Datei einen anderen Namen haben, können Sie das DataMember Attribut entsprechend verändern. Im Code unten sehen Sie dies am Beispiel der Eigenschaft Content. Das entsprechende Feld in der JSON Datei wird den Namen „Text“ haben.
[DataContract]
public class ToDo
{
[DataMember]
public string Title { get; set; }
[DataMember]
public DateTime Date { get; set; }
[DataMember(Name="Text")]
public string Content { get; set; }
}
Nachdem wir jetzt einen einfachen Container für unsere Objekte haben, lassen Sie uns etwas Geschäftslogik mit dem Namen ToDoService erstellen, um die Liste der Aufgaben zu speichern und wieder aus der Datei in einem lokalen Ordner (LocalFolder) zu laden. LocalFolder ist der Wurzelordner Ihrer Anwendungsdaten — unter Windows Phone 7 war das Isolated Storage.
Als erstes fügen wir unserem ToDoService eine Methode hinzu, um die Liste der Aufgaben zu speichern.
public async Task<string> SaveToDos()
{
List<ToDo> todos = new List<ToDo>();
todos.Add(new ToDo(){Title="todo 1", Date = DateTime.Now, Content="finish this series of blogposts"});
todos.Add(new ToDo(){Title="todo 2", Date = DateTime.Now, Content="Pick up milk from the store"});
todos.Add(new ToDo(){Title="todo 3", Date = DateTime.Now, Content="Wash my car"});
var localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
DataContractJsonSerializer dcjs = new DataContractJsonSerializer(typeof(List<ToDo>));
MemoryStream ms = new MemoryStream();
dcjs.WriteObject(ms,todos);
string json = Encoding.UTF8.GetString(ms.ToArray(),0,(int)ms.Length);
StorageFile todoFile = await localFolder.CreateFileAsync("todos.json",CreationCollisionOption.ReplaceExisting);
using (var f = await todoFile.OpenStreamForWriteAsync())
{
await f.WriteAsync(ms.ToArray(),0,(int)ms.Length);
f.Close();
}
return json;
}
Beachten Sie das async Schlüsselwort vor dem Rückgabetyp in der Methode. Damit kann die Methode asynchron aufgerufen werden und sie wird immer einen Task zurückgeben, welcher den Rückgabewert beinhaltet. In der Methode füllen wir die Liste der Aufgaben zunächst mit einigen Einträgen. Danach erstellen wir einen DataContractJsonSerializer. Dabei geben wir im Konstruktor an, welchen Typ wir serialisieren wollen. Wir verwenden dann den DataContractJsonSerializer, um die Liste der Aufgaben in eine JSON Zeichenkette zu serialisieren. Diesen geben wir am Ende der Methode zurück.
Sobald wir den JSON String haben, erstellen wir eine neue Datei im lokalen Ordner, indem wir die Methode CreateFileAsync aufrufen. Beachten Sie das await Schüsselwort im Aufruf der Methode, welcher grob bedeutet, dass der Code unterhalb des Aufrufs erst ausgeführt wird, wenn der asynchrone Aufruf abgeschlossen ist. Wenn die lokale Datei erstellt ist, schreiben wir einfach die JSON Zeichenkette in diese Datei und schließen den Stream.
Jetzt, wo wir die Aufgabenliste in die lokale Datei gespeichert haben, lassen Sie uns diese wieder lesen und am Bildschirm anzeigen. Wir implementieren hierzu in unserem ToDoService eine weitere Methode mit dem Namen GetToDos(). Diese liest die JSON Datei ein und deserialisiert diese zurück in die Liste der Aufgaben.
public async Task<List<ToDo>> GetToDos()
{
var localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
StorageFile todoFile = await localFolder.GetFileAsync("todos.json");
DataContractJsonSerializer dcjs = new DataContractJsonSerializer(typeof(List<ToDo>));
var stream = await todoFile.OpenStreamForReadAsync();
var todos = dcjs.ReadObject(stream) as List<ToDo>;
return todos;
}
Wir beginnen wieder, indem wir uns eine Referenz auf den lokalen Ordner und auf die JSON Datei holen. Nachdem wir das StorageFile Objekt haben, erstellen wir wieder einen neuen DataContractJsonSerializer. Wir öffnen den Stream aus unserer StorageFile und übergeben diesen an den DataContractJsonSerializer, um die Daten zurück in eine Liste von Aufgaben zu deserialisieren. Damit sind wir fertig.
Um die Demo abzuschließen, fügen wir der Hauptseite eine TextBox hinzu, in welcher wir die JSON Zeichenkette darstellen. Weiterhin bauen wir ein ItemsControl ein, welches die Liste der Aufgaben anzeigt.
In der Code Behind Datei fügen wir nun alles zusammen, um die Daten zu schreiben, zu lesen und anzuzeigen.
public async void SaveAndGetTodos()
{
ToDoService ts = new ToDoService();
string json = await ts.SaveToDos();
List<ToDo> todos = await ts.GetToDos();
JsonText.Text = json;
TodoList.ItemsSource = todos;
}
Wenn wir unsere Demoanwendung jetzt laufen lassen, sehen wir den JSON String, welcher durch den DataContractJsonSerializer erstellt wurde und außerdem die Liste der Aufgaben im ItemsControl.
Hoffentlich hat Ihnen das Lesen dieses Artikels Spaß gemacht und Sie haben etwas über async/await und den DataContractJsonSerializer gelernt. Morgen gibt’s mehr über eines der coolsten Features im neuen SDK: die APIs zur Sprachsteuerung.