×
Namespaces

Variants
Actions
(Difference between revisions)

What's new in Windows Phone 8

From Nokia Developer Wiki
Jump to: navigation, search
hamishwillee (Talk | contribs)
m (Hamishwillee - Fix error caused by code block rendering defect)
hamishwillee (Talk | contribs)
m (Hamishwillee - Add link to devcenter topic covering similar material.)
Line 1: Line 1:
 
[[Category:Windows Phone]][[Category:Getting Started]][[Category:General Programming]][[Category:Windows Phone 8]][[Category:Windows Phone]][[Category:Getting Started]][[Category:General Programming]][[Category:Windows Phone 8]]
 
[[Category:Windows Phone]][[Category:Getting Started]][[Category:General Programming]][[Category:Windows Phone 8]][[Category:Windows Phone]][[Category:Getting Started]][[Category:General Programming]][[Category:Windows Phone 8]]
 
{{FeaturedArticle|timestamp=20121105}}
 
{{FeaturedArticle|timestamp=20121105}}
 +
{{SeeAlso|[http://msdn.microsoft.com/en-us/library/windowsphone/develop/jj206940(v=vs.105).aspx What's new in Windows Phone SDK 8.0] (Dev Center)}}
 
{{ArticleMetaData <!-- v1.2 -->
 
{{ArticleMetaData <!-- v1.2 -->
 
|sourcecode= [[Media:WhatsNewInWP8 FullExample.zip]]  
 
|sourcecode= [[Media:WhatsNewInWP8 FullExample.zip]]  

Revision as of 08:45, 10 December 2012

{{{width}}}
05 Nov
2012
See Also
WP Metro Icon WP8.png
WP Metro Icon Baby.png
SignpostIcon Code 52.png
Article Metadata
Code ExampleCompatibility
Platform(s): Windows Phone 8 and later
Windows Phone 8
Article
Created: Justin.Angel (26 Oct 2012)
Last edited: hamishwillee (10 Dec 2012)

Introduction

The release of Windows Phone 8 marks a significant milestone in the evolution of Microsoft's operating system for mobile phones. For users this new release means expanded hardware access, support for high-end gaming, and better OS integration - all of which will soon be seen on the Nokia Lumia 920 and Nokia Lumia 820 phones.

For developers the changes are no less significant, bringing many new APIs. These APIs simplify the coding of features you may already be adding to your Windows Phone apps, but also offers many new ones too. This article will introduce you to the most significant API additions and changes.

Through the article an example application is used to show the practical code you need to write to make use of these new features. To create your own version of this application you will need the Windows Phone 8 SDK, which can be downloaded from http://go.microsoft.com/fwlink/?LinkID=261873. You can get a complete version of the example app here: Media:WhatsNewInWP8 FullExample.zip.

Contents


Native: DirectX, C++, and Direct3D graphics

A subset of DirectX has been introduced into Windows Phone 8 to allow for native high-end high-performance games. C++ has also been introduced, primarily for use in those high-end games.

In Visual Studio 2012 you can create a “Windows Phone Direct3D Application” which uses these new features.

New Windows Phone Direct3D application

Run the default new project and you will see a spinning 3D cube.

3D spinning cube in Direct3D

Direct3D isn't magic. It's pretty easy to go into CubeRenderer.cpp, find the place where 8 points are used to define the cube

VertexPositionColor cubeVertices[] =
{
{XMFLOAT3(-0.5f, -0.5f, -0.5f), XMFLOAT3(0.0f, 0.0f, 0.0f)},
{XMFLOAT3(-0.5f, -0.5f, 0.5f), XMFLOAT3(0.0f, 0.0f, 1.0f)},
{XMFLOAT3(-0.5f, 0.5f, -0.5f), XMFLOAT3(0.0f, 1.0f, 0.0f)},
{XMFLOAT3(-0.5f, 0.5f, 0.5f), XMFLOAT3(0.0f, 1.0f, 1.0f)},
{XMFLOAT3( 0.5f, -0.5f, -0.5f), XMFLOAT3(1.0f, 0.0f, 0.0f)},
{XMFLOAT3( 0.5f, -0.5f, 0.5f), XMFLOAT3(1.0f, 0.0f, 1.0f)},
{XMFLOAT3( 0.5f, 0.5f, -0.5f), XMFLOAT3(1.0f, 1.0f, 0.0f)},
{XMFLOAT3( 0.5f, 0.5f, 0.5f), XMFLOAT3(1.0f, 1.0f, 1.0f)},
};


It's also quite simple to reduce the definition t 4 points and get a tetrahedron, as follows:

VertexPositionColor cubeVertices[] =
{
{XMFLOAT3(-0.5f, -0.5f, -0.5f), XMFLOAT3(0.0f, 0.0f, 0.0f)},
{XMFLOAT3(-0.5f, -0.5f, 0.5f), XMFLOAT3(0.0f, 0.0f, 1.0f)},
{XMFLOAT3(-0.5f, 0.5f, -0.5f), XMFLOAT3(0.0f, 1.0f, 0.0f)},
{XMFLOAT3(-0.5f, 0.5f, 0.5f), XMFLOAT3(0.0f, 1.0f, 1.0f)},
//{XMFLOAT3( 0.5f, -0.5f, -0.5f), XMFLOAT3(1.0f, 0.0f, 0.0f)},
//{XMFLOAT3( 0.5f, -0.5f, 0.5f), XMFLOAT3(1.0f, 0.0f, 1.0f)},
//{XMFLOAT3( 0.5f, 0.5f, -0.5f), XMFLOAT3(1.0f, 1.0f, 0.0f)},
//{XMFLOAT3( 0.5f, 0.5f, 0.5f), XMFLOAT3(1.0f, 1.0f, 1.0f)},
};


Now, running the app, displays a flat tetrahedron spinning in 3D.

3D spinning tetrahedron in Direct3D

There are a few important caveats with this new feature set.

  1. Programming languages: C++ is meant to be used alongside DirectX. C++ with XAML or C# with DirectX aren't supported out of the box. Even though achieving interoperability between the two platforms is possible.
  2. Direct3D feature level: The supported feature level for Direct3D is Level9_3 for all Windows Phone 8 devices. Whereas Windows 8 Store apps can support additional feature levels. That limits what we can do with Direct3D. But it also creates an easy programming model for DirectX across all Windows Phone 8 devices and potential for game code reuse between Windows Phone 8 and Windows 8. Here's a partial list of Direct3D 11.1 features supported in feature level 9_3:
    Supported Direct3D featuresets in Level9 3
  3. DirectX support: Direct3D is only one technology in the larger family of technologies known as “DirectX”. Not all of those technologies are supported on Windows Phone 8 and even those that are supported are a subset of the desktop API. You can see a full list of those technologies here.
    Topic Supported WP8? Description
    Direct2D
    No
    Direct2D is a hardware-accelerated, immediate-mode, 2-D graphics API that provides high performance and high-quality rendering for 2-D geometry, bitmaps, and text.
    Direct3D Yes Direct3D enables you to create 3-D graphics for games and scientific apps.
    DirectWrite
    No
    DirectWrite supports high-quality text rendering, resolution-independent outline fonts, and full Unicode text and layouts.
    DirectXMath Yes DirectXMath provides an optimal and portable interface for arithmetic and linear algebra operations on single-precision floating-point vectors (2D, 3D, and 4D) or matrices (3×3 and 4×4).
    XAudio2 Yes Provides a signal processing and mixing foundation for games. XAudio2 replaces DirectSound.
    XInput
    No
    Describes how to use the XInput API to interact with the Xbox 360 Controller when it is connected to a Windows computer. XInput replaces DirectInput.
    Windows Imaging Component (WIC)
    No
    The Windows Imaging Component (WIC) is an extensible platform that provides low-level API for digital images. WIC supports the standard web image formats, high dynamic range images, and raw camera data.
  4. C++ cannot access managed Mango APIs: C++ code can only use new WP8 APIs from WinPRT. As a result C++ code cannot access any of the existing Mango APIs. That means C++ apps are limited to the following new windows phone namespaces:
Windows.ApplicationModel
Windows.ApplicationModel.Activation
Windows.ApplicationModel.Core
Windows.ApplicationModel.DataTransfer
Windows.ApplicationModel.Store
Windows.Devices.Geolocation
Windows.Devices.Input
Windows.Devices.Sensors
Windows.Foundation
Windows.Foundation.Collections
Windows.Foundation.Diagnostics
Windows.Foundation.Metadata
Windows.Graphics.Display
Windows.Management.Deployment
Windows.Networking
Windows.Networking.Connectivity
Windows.Networking.Proximity
Windows.Networking.Sockets
Windows.Phone.ApplicationModel
Windows.Phone.Devices.Notification
Windows.Phone.Devices.Power
Windows.Phone.Graphics.Interop
Windows.Phone.Input.Interop
Windows.Phone.Management.Deployment
Windows.Phone.Media.Capture
Windows.Phone.Media.Devices
Windows.Phone.Networking.NetworkOperators
Windows.Phone.Networking.Voip
Windows.Phone.PersonalInformation
Windows.Phone.PersonalInformation.Provisioning
Windows.Phone.Speech.Recognition
Windows.Phone.Speech.Synthesis
Windows.Phone.Speech.VoiceCommands
Windows.Phone.Storage.SharedAccess
Windows.Phone.System
Windows.Phone.System.Analytics
Windows.Phone.System.Memory
Windows.Phone.System.Power
Windows.Phone.System.Profile
Windows.Phone.System.UserProfile
Windows.Phone.System.UserProfile.GameServices.Core
Windows.Phone.UI.Core
Windows.Phone.UI.Input
Windows.Security.Authentication.OnlineId
Windows.Storage
Windows.Storage.FileProperties
Windows.Storage.Pickers
Windows.Storage.Search
Windows.Storage.Streams
Windows.System
Windows.System.Display
Windows.System.Threading
Windows.System.Threading.Core
Windows.UI
Windows.UI.Core
Windows.UI.Input
Windows.UI.Popups
Windows.UI.ViewManagement

Native: Interoperability between DirectX and C++, and XAML and C#

One of the coolest features of the new DirectX and C++ support is its easy integration with XAML and C#.

We can interweave XAML and DirectX using the new <DrawingSurface /> and <DrawingSurfaceBackgroundGrid /> controls.

<DrawingSurface x:Name="DrawingSurface" />

Because DirectX drawing surfaces can be just another element in an XAML page, we can easily integrate it with other XAML elements. This trivial example illustrates placing a button on top of a DirectX DrawingSurface.

<Grid x:Name="LayoutRoot" Background="Transparent">
<DrawingSurface x:Name="DrawingSurface" />
<Button Margin="20" Content="Tell DirectX this button was clicked"
Height="70" VerticalAlignment="Top"/>
</Grid>

The result, with the button overlaid on DirectX DrawingSurface is shown below. It even interleaves the background colour.

XAML Button overlaying DirectX

It's also possible for a C++ assembly to expose dedicated endpoints for C# code to communicate with and vice versa.

For example, in the example app you can expose the following method header in the Direct3DInterop.h header file:

public:
Direct3DInterop();
Windows::Phone::Graphics::Interop::IDrawingSurfaceContentProvider^ CreateContentProvider();
 
// Event Handlers
void MyButtonWasClicked();

Then implement this method in the Direct3DInterop.cpp so it prints out something to the debug console.

void Direct3DInterop::MyButtonWasClicked()
{
OutputDebugString(L"Button was clicked!");
}

And from C# code you can now invoke this new method.

private Direct3DInterop m_d3dInterop = new Direct3DInterop();
private void Button_Click_1(object sender, RoutedEventArgs e)
{
m_d3dInterop.MyButtonWasClicked();
}

Debugging the app shows the output string in the debug console. But first you need to start debugging the native codebase in the app. To do this open the project properties and change to the native debugger.

Changing application debugging to Native Only

Now run the app and click the button, you will see the following debug message in the output window:

Output window showing a button click passed to C++

This tells you that the XAML button click made a C# interop call successfully to the C++ codebase (that call could then affect DirectX code). The interoperability between DirectX and C++ and XAML and C# is pretty powerful.

Speech: Text-to-Speech

Text-to-speech (TTS) is now built into Windows Phone 8.

TTS was possible in previous versions of Windows Phone using an online service from Bing. If you have tried to work with this service you may have had multiple issues: Bing Translator wasn't really built for quality text-to-speech — so the speech sounded mechanical — it took a lot of work to setup and it required a network connection. All of those problems are solved with WP8: TTS sounds very natural, supports a range of languages, is added with only 2 lines of code, and works offline with no data connection.

The "Hello world" code sample for TTS is quite simple:

private async void TTS_HelloWorld(object sender, RoutedEventArgs e)
{
var text2speech = new SpeechSynthesizer();
await text2speech.SpeakTextAsync("OMG! Hello world!");
}

When you run the app you will hear this audio: The media player is loading...

One of the cool things about TTS in WP8 is that you can change the voice used to read out text. And you've got a myriad of languages and voices to use. Voices differ from one another based on gender and which culture they're optimised for. This example uses all of the installed voices in a single sample

private async void TTS_AllVoices(object sender, RoutedEventArgs e)
{
foreach (var voice in InstalledVoices.All)
{
Debug.WriteLine(voice.DisplayName + ", " +
voice.Language + ", " +
voice.Gender + ", " +
voice.Description);
using (var text2speech = new SpeechSynthesizer())
{
text2speech.SetVoice(voice);
await text2speech.SpeakTextAsync("Hello world! I'm " + voice.DisplayName + ".");
}
}
}

When run, you will hear this audio: The media player is loading...

You should also see the following debug output:

Microsoft Zira Mobile, en-US, Female, Microsoft Zira Mobile - English (United States)
Microsoft Stefan Mobile, de-DE, Male, Microsoft Stefan Mobile - German (Germany)
Microsoft George Mobile, en-GB, Male, Microsoft George Mobile - English (United Kingdom)
Microsoft Susan Mobile, en-GB, Female, Microsoft Susan Mobile - English (United Kingdom)
Microsoft Heera Mobile, en-IN, Female, Microsoft Heera Mobile - English (India)
Microsoft Ravi Mobile, en-IN, Male, Microsoft Ravi Mobile - English (India)
Microsoft Mark Mobile, en-US, Male, Microsoft Mark Mobile - English (United States)
Microsoft Katja Mobile, de-DE, Female, Microsoft Katja Mobile - German (Germany)
Microsoft Laura Mobile, es-ES, Female, Microsoft Laura Mobile - Spanish (Spain)
Microsoft Pablo Mobile, es-ES, Male, Microsoft Pablo Mobile - Spanish (Spain)
Microsoft Raul Mobile, es-MX, Male, Microsoft Raul Mobile - Spanish (Mexico)
Microsoft Sabina Mobile, es-MX, Female, Microsoft Sabina Mobile - Spanish (Mexico)
Microsoft Julie Mobile, fr-FR, Female, Microsoft Julie Mobile - French (France)
Microsoft Paul Mobile, fr-FR, Male, Microsoft Paul Mobile - French (France)
Microsoft Cosimo Mobile, it-IT, Male, Microsoft Cosimo Mobile - Italian (Italy)
Microsoft Elsa Mobile, it-IT, Female, Microsoft Elsa Mobile - Italian (Italy)
Microsoft Ayumi Mobile, ja-JP, Female, Microsoft Ayumi Mobile - Japanese (Japan)
Microsoft Ichiro Mobile, ja-JP, Male, Microsoft Ichiro Mobile - Japanese (Japan)
Microsoft Adam Mobile, pl-PL, Male, Microsoft Adam Mobile - Polish (Poland)
Microsoft Paulina Mobile, pl-PL, Female, Microsoft Paulina Mobile - Polish (Poland)
Microsoft Daniel Mobile, pt-BR, Male, Microsoft Daniel Mobile - Portuguese (Brazil)
Microsoft Maria Mobile, pt-BR, Female, Microsoft Maria Mobile - Portuguese (Brazil)
Microsoft Irina Mobile, ru-RU, Female, Microsoft Irina Mobile - Russian (Russia)
Microsoft Pavel Mobile, ru-RU, Male, Microsoft Pavel Mobile - Russian (Russia)
Microsoft Kangkang Mobile, zh-CN, Male, Microsoft Kangkang Mobile - Chinese (Simplified, PRC)
Microsoft Yaoyao Mobile, zh-CN, Female, Microsoft Yaoyao Mobile - Chinese (Simplified, PRC)
Microsoft Danny Mobile, zh-HK, Male, Microsoft Danny Mobile - Chinese (Traditional, Hong Kong S.A.R.)
Microsoft Tracy Mobile, zh-HK, Female, Microsoft Tracy Mobile - Chinese (Traditional, Hong Kong S.A.R.)
Microsoft Yating Mobile, zh-TW, Female, Microsoft Yating Mobile - Chinese (Traditional, Taiwan)
Microsoft Zhiwei Mobile, zh-TW, Male, Microsoft Zhiwei Mobile - Chinese (Traditional, Taiwan)

For more fine-grained control of text-to-speech you can use the SSML format to optimise speech and make it sound authentically human. Use the sample SSML from the W3C website:

private async void TTS_SSML(object sender, RoutedEventArgs e)
{
var text2speech = new SpeechSynthesizer();
await text2speech.SpeakSsmlAsync(@"<speak version=""1.0""
xmlns="
"http://www.w3.org/2001/10/synthesis"" xml:lang=""en-US"">
<voice gender="
"female"">
Hi, this is Justin's computer...
</voice>
<voice age="
"6"">
Hello <prosody contour="
"(0%,+20Hz)(10%,+30%)(40%,+10Hz)"">world</prosody>
</voice>
</speak>"
);
}

Run this sample you'll hear this audio: The media player is loading...


Speech: Speech to text

Speech to text used to be quite hard to do successfully. Even the best hacked-up solutions in WP7 apps were flaky and the technology never caught on with consumers. In Windows Phone 8, Microsoft has really taken the lead and created a top notch speech to text API. Most of the processing happens on a remote server, so you will need a data connection for it to work.

The following code is a simple speech to text recognising session running directly from the UI.

private async void STT_Freeform(object sender, RoutedEventArgs e)
{
SpeechRecognizerUI speechRecognizer = new SpeechRecognizerUI();
speechRecognizer.Settings.ExampleText = "Fine thanks";
speechRecognizer.Settings.ListenText = "How's it goin', eh?";
speechRecognizer.Settings.ReadoutEnabled = true;
speechRecognizer.Settings.ShowConfirmation = true;
SpeechRecognitionUIResult result = await speechRecognizer.RecognizeWithUIAsync();
if (result.ResultStatus == SpeechRecognitionUIStatus.Succeeded)
{
MessageBox.Show(result.RecognitionResult.Text);
}
}


The code snippet behaves as shown in the following video: The media player is loading...

One useful feature, that helps improve recognition accuracy further is that you can limit the supported answers by loading in a grammar from a list or from a file, as follows:

private async void STT_FromList(object sender, RoutedEventArgs e)
{
SpeechRecognizerUI speechRecognizer = new SpeechRecognizerUI();
speechRecognizer.Settings.ExampleText = "Me, You, Everyone";
speechRecognizer.Settings.ListenText = "Who's awesome?";
speechRecognizer.Settings.ReadoutEnabled = true;
speechRecognizer.Settings.ShowConfirmation = true;
 
speechRecognizer.Recognizer.Grammars.AddGrammarFromList("answer",
new string[] { "You", "Me", "Everyone" });
 
SpeechRecognitionUIResult result = await speechRecognizer.RecognizeWithUIAsync();
if (result.ResultStatus == SpeechRecognitionUIStatus.Succeeded)
{
MessageBox.Show(result.RecognitionResult.Text);
}
}

Now when you run the code the system recognises only words from the grammar list.

It's also possible to perform a speech-to-text conversion without showing a UI. For example, this code snippet will analyse any audio file:

private async void STT_NonVisual(object sender, RoutedEventArgs e)
{
SpeechRecognizer speechRecognizer = new SpeechRecognizer();
SpeechRecognitionResult result = await speechRecognizer.RecognizeAsync();
MessageBox.Show("Heard \"" + result.Text + "\" with a confidence of " + result.TextConfidence);
}

And you can see that the engine can cope with even my quaint Canadian accent (video here).

WP8 Voice Recognition confirming spoken text dictation
The media player is loading...


Speech: Voice Commands

Apps can now register voice commands that could either wake up the app or be used from within the app. Each voice command can be broken down into three pieces: the app name, the command and its phrase list.

Breaking down Voice command structure: app name, command and phrases

When building voice commands you'll need to start out by creating a VCD (Voice Command Definition) file. For a simple voice command, create this simple VCD that'll demonstrate using all three elements of the command.

<?xml version="1.0" encoding="utf-8"?>
<VoiceCommands xmlns="http://schemas.microsoft.com/voicecommands/1.0">
<CommandSet xml:lang="en-us">
 
<Example>Compute Pii to a billion places</Example>
 
<Command Name="ComputePii">
<Example>Compute Pii to a billion places</Example>
<ListenFor>Compute Pii to a {number} places</ListenFor>
<ListenFor>Compute Pii NOW</ListenFor>
<Feedback>Computing Pii...</Feedback>
<Navigate Target="MainPage.xaml"/>
</Command>
 
<PhraseList Label="number">
<Item>one</Item>
<Item>two</Item>
<Item>billion</Item>
</PhraseList>
 
</CommandSet>
</VoiceCommands>

As you can see, the purpose of this file is to define a command that's wired up to a Target URI deeplink and lists the phrases that command can use.

Note.pngNote: You may wonder why the VCD doesn't include “My App”. This is because the app name is derived from the information provided in the WmAppManfiest file. After the app has been submitted to the WP8 store, Dev Center overrides this information with the app name as it's shown in the Windows Phone 8 store.

Next, you'll have to register this file with the OS so that it knows to recognise the voice commands. Preferably this is done on app start-up and only once, but in this example you can simply register the XML in an event handler.

private async void VoiceCommands(object sender, RoutedEventArgs e)
{
await VoiceCommandService.InstallCommandSetsFromFileAsync(
new Uri("file://" +
Windows.ApplicationModel.Package.Current.InstalledLocation.Path +
@"/ComputePiiVCD.xml",
UriKind.RelativeOrAbsolute));
MessageBox.Show(@"Hit the home key to go to the start screen.
Then press and hold the Windows button and say:
My App, Compute Pii to a Billion Places"
,
"Voice Commands registered.",
MessageBoxButton.OK);
}

Finally, you'll have to handle the event when a voice command invokes the app. Do that by overriding the OnNavigatedTo handler and show a MessageBox with all the URI querystring parameters. Note, a real-world implementation would be to create your own UriMapper and use that to navigate the user to the right page.

protected async override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (NavigationContext.QueryString.Any())
{
StringBuilder sb = new StringBuilder();
foreach (var key in NavigationContext.QueryString.Keys)
{
sb.AppendLine(key + ": " + NavigationContext.QueryString[key]);
}
MessageBox.Show(sb.ToString(), "Page Querystring", MessageBoxButton.OK);
}
}

Finally you run the app, register the commands, go to the home screen, and then say "My App, Compute Pi to a Billion places".

You can download the video showing the speech commands in action or watch it below: The media player is loading...


Lock Screen: Background image, counter, and text

As part of Windows Phone 8 users can hand over control of the lock screen appearance to apps. Specifically, apps can change the lock screen background image, the five counters on the bottom and the text displayed on the home screen. In Windows Phone 7 the image had to have been specified by the user, the text must be the next meeting in outlook and only system apps could be counters. So this is a big step up in OS customization.

Sample lock screen with background image, text and counters

To start you declare a lock screen extension so your app will show up in the settings screens. Add the following code to WmAppManfiest.xml immediately after the <Tokens /> element:

<Extensions>
<Extension ExtensionName="LockScreen_Notification_IconCount"
ConsumerID="{111DFF24-AA15-4A96-8006-2BFF8122084F}" TaskID="_default" />
<Extension ExtensionName="LockScreen_Notification_TextField"
ConsumerID="{111DFF24-AA15-4A96-8006-2BFF8122084F}" TaskID="_default" />
<Extension ExtensionName="LockScreen_Wallpaper"
ConsumerID="{111DFF24-AA15-4A96-8006-2BFF8122084F}" TaskID="_default" />
</Extensions>

Specify a 24x24 pixel transparent PNG file with some white pixels too, for the app's counter icon.

<DeviceLockImageURI IsRelative="true" IsResource="false">24x24.png</DeviceLockImageURI>

Now users can see the app in the Lock Screen Settings and choose to let the app change the lock screen.

Instead of using the Settings screen, the user can opt-in to the app supplying lock screen images. You add this feature with:

private async void LockScreen_ChangeImage(object sender, RoutedEventArgs e)
{
if (!LockScreenManager.IsProvidedByCurrentApplication)
{
await LockScreenManager.RequestAccessAsync();
}
}

Running the code you see the app ask the user to give permission for the app to provide wallpaper images to the lock screen.

Dialog requesting permission to use app for lock screen.

Now the app can change the lock screen background image, as follows:

private async void LockScreen_ChangeImage(object sender, RoutedEventArgs e)
{
if (!LockScreenManager.IsProvidedByCurrentApplication)
{
await LockScreenManager.RequestAccessAsync();
}
 
if (LockScreenManager.IsProvidedByCurrentApplication)
{
LockScreen.SetImageUri(
new Uri("ms-appx:///CustomizedPersonalWalleper.jpg", UriKind.RelativeOrAbsolute));
}
MessageBox.Show("Lock screen changed. Click F12 or go to lock screen.");
}

Running the app you will see the new image is the lock screen background image. The background image can be anything you'd like it to be and can be composed dynamically.

More interesting lock screen with real content (image courtesy of Windows Phone 7 app Live Tile News).

You can add text and a counter to the lock screen as well, by assigning values to the app's pinned tile.

private void LockScreen_ChangeCounterAndText(object sender, RoutedEventArgs e)
{
ShellTile.ActiveTiles.First().Update(
new FlipTileData()
{
Count = 99,
WideBackContent = "Lock screen text",
SmallBackgroundImage = new Uri(@"Assets\Tiles\FlipCycleTileSmall.png", UriKind.Relative),
BackgroundImage = new Uri(@"Assets\Tiles\FlipCycleTileMedium.png", UriKind.Relative),
BackBackgroundImage = new Uri(@"Assets\Tiles\FlipCycleTileMedium.png", UriKind.Relative)
});
}

This results in the following lock screen:

Lock screen with custom counter, icon and text


Live Tiles: New sizes and new templates

The Windows Phone 8 home screen has been redesigned so that apps can have normal, wide, and small tiles. You can set these templates for your app as part of the WmAppManfiest XML PrimaryToken, by updating the TileData in code, and using push notifications template XML. In this article the WmAppManifest syntax are explored.


Flip Template

In Mango the StandardTileData class was introduced, FlipTileData is the equivalent of that class in WP8 but with new properties. TemplateFlip has flip content on both sizes of the tile and supports all the new tile sizes.

TemplateFlip properties overview

You specify flip tiles by specifying a PrimaryToken as “FlipTemplate” or using the FlipData class from code. Here is an example using the FlipTemplate XML.

<PrimaryToken TokenID="WP8_Beta_22Token" TaskName="_default">
<TemplateFlip>
<SmallImageURI IsRelative="true" IsResource="false">159x159.png</SmallImageURI>
<Count>5</Count>
<BackgroundImageURI IsRelative="true" IsResource="false">336x336.png</BackgroundImageURI>
<Title>Title</Title>
<BackContent>Back Content</BackContent>
<BackBackgroundImageURI IsRelative="true" IsResource="false">336x336.png</BackBackgroundImageURI>
<BackTitle>Back Title</BackTitle>
<LargeBackgroundImageURI IsRelative="true" IsResource="false">691x336.png</LargeBackgroundImageURI>
<LargeBackContent>Hello World</LargeBackContent>
<LargeBackBackgroundImageURI IsRelative="true" IsResource="false">691x336.png</LargeBackBackgroundImageURI>
<DeviceLockImageURI IsRelative="true" IsResource="false">24x24.png</DeviceLockImageURI>
<HasLarge>True</HasLarge>
</TemplateFlip>
</PrimaryToken>

The example below shows the same tile, five times to display all the tiles sizes and show back and front content.

TemplateFlip with small, normal and large tiles


Iconic Template

One of the most common uses of tiles in WP7 is to recreate the look of the built-in apps' tiles. It was such a common use case that third-party projects, such as MSP Toolkit, stepped in to fill the gap. The good news is that Windows Phone 8 provides built-in support for tile templates that look similar to native live tiles. The various properties that can be set on this new type of template are shown below:

TemplateIconic properties overview

You use the TemplateIconic XML in the WmAppManifest file or the IconicTileData class to specify the content. This example uses the TemplateIconic:

<PrimaryToken TokenID="WP8_Beta_22Token" TaskName="_default">
<TemplateIconic>
<SmallImageURI IsRelative="true" IsResource="false">110x110.png</SmallImageURI>
<Count>5</Count>
<IconImageURI IsRelative="true" IsResource="false">202x202.png</IconImageURI>
<Title>Title</Title>
<Message>Message</Message>
<BackgroundColor>#123456</BackgroundColor>
<HasLarge>True</HasLarge>
<LargeContent1>LargeContent1</LargeContent1>
<LargeContent2>LargeContent2</LargeContent2>
<LargeContent3>LargeContent3</LargeContent3>
<DeviceLockImageURI IsRelative="true" IsResource="false">24x24.png</DeviceLockImageURI>
</TemplateIconic>
</PrimaryToken>

The three tiles this code produces are shown below:

TemplateIconic in small, normal and wide live tile sizes


Cyclic Tile

One of the cool features of WP7 live tiles is the wide photos tile, which cycles through your pictures. That tile template is now available for you to use. Simply specify which nine local images you'd like the tile to cycle through as follows:

<PrimaryToken TokenID="WP8_Beta_22Token" TaskName="_default">
<TemplateCycle>
<SmallImageURI IsRelative="true" IsResource="false">159x159.png</SmallImageURI>
<Title>Title</Title>
<Photo01ImageURI IsRelative="true" IsResource="false">691x336_N1.png</Photo01ImageURI>
<Photo02ImageURI IsRelative="true" IsResource="false">691x336_N2.png</Photo02ImageURI>
<Photo03ImageURI IsRelative="true" IsResource="false">691x336_N3.png</Photo03ImageURI>
<Photo04ImageURI IsRelative="true" IsResource="false">691x336_N4.png</Photo04ImageURI>
<Photo05ImageURI IsRelative="true" IsResource="false">691x336_N5.png</Photo05ImageURI>
<Photo06ImageURI IsRelative="true" IsResource="false">691x336_N6.png</Photo06ImageURI>
<Photo07ImageURI IsRelative="true" IsResource="false">691x336_N7.png</Photo07ImageURI>
<Photo08ImageURI IsRelative="true" IsResource="false">691x336_N8.png</Photo08ImageURI>
<Photo09ImageURI IsRelative="true" IsResource="false">691x336_N9.png</Photo09ImageURI>
<Count>0</Count>
<HasLarge>True</HasLarge>
<DeviceLockImageURI IsRelative="true" IsResource="false">24x24.png</DeviceLockImageURI>
</TemplateCycle>
</PrimaryToken>

And the live tile displays as follows (note only two items are shown here):

TemplateCycle cycling through 2 local images


Maps

Nokia in partnership with Microsoft has resulted in a new first-party Map control in Windows Phone 8. In WP7 the Bing Maps control was available in a third-party SDK. However the Bing maps control had limited cartographic data, was slow, and had some features missing. The new Maps control is a fully featured map control, powered by Nokia Maps data and is ridiculously fast since it's part of the OS. The most impressive difference between the WP7 Bing Maps control and the WP8 Nokia Maps control is that the WP8 control uses vector content and not bitmap tiles. Zooming in and out is now smooth and no longer has that awkward look of pixilation and delay waiting for tiles to load.

Here's an example of a basic Map control and the permission needed to activate it:

    <Capability Name="ID_CAP_MAP" />
<maps:Map />
Basic Map control


The many, many map features…

The Nokia Maps control brings many new features, only some are discussed here. Using the Center property you set the GeoLocation of the map control. Using the Zoom feature it's possible to zoom the map in and out of view. The Heading property rotates the map tiles around its centre. The pitch property changes the elevation of the map compared to the horizon. It's possible to change the type of map displayed by setting CartographicMode to Road, Terrain, Aerial, or Hybrid. The ColorMode property can be set to either Light (for daytime driving) or Dark (for night time driving). It's possible to add 3D landmarks using LandmarksEnabled. It's also possible to add terrain features useful for pedestrians (such as stairs) using PedestrianFeaturesEnabled. This code sets all of these features for a map control:

<maps:Map
x:Name="myMap"
Center="37.792878,-122.39641"
ZoomLevel="17"
Heading="45"
Pitch="25"
CartographicMode="Road"
ColorMode="Dark"
PedestrianFeaturesEnabled="True"
LandmarksEnabled="True"
/>

Resulting in the following map display:

Map control with features enabled


Map layers

It's possible to easily add layers to your maps using a XAML element with GeoCoordinates and the map control will place those elements in the appropriate location. With this code you can add a red dot in front of the San-Francisco Ferry Building:

myMap.Layers.Add(new MapLayer()
{
new MapOverlay()
{
GeoCoordinate = new GeoCoordinate(37.795032,-122.394927),
Content = new Ellipse
{
Fill = new SolidColorBrush(Colors.Red),
Width = 40,
Height = 40
}
}
});

The dot is then displayed regardless of the rotation or centre of the map.


Routing

Another great feature of the map is the ability to add routes to the map control. For example, you can add a route from the San-Francisco Ferry Building to the Transamerica Pyramid. This is done by defining a RouteQuery that runs on the server and once it's ready you add it to the map control. One thing worth noting is that TravelMode supports the creation of routes for driving or walking.

public Maps_Routing()
{
InitializeComponent();
RouteQuery query = new RouteQuery()
{
TravelMode = TravelMode.Driving,
Waypoints = new List<GeoCoordinate>()
{
new GeoCoordinate(37.79547,-122.393129), // ferry building
new GeoCoordinate(37.794911,-122.402871) // Transamerica Pyramid
}
};
query.QueryCompleted += query_QueryCompleted;
query.QueryAsync();
}
void query_QueryCompleted(object sender, QueryCompletedEventArgs<Route> e)
{
myMap.AddRoute(new MapRoute(e.Result));
}

This code results in the following route between the Ferry Building and the Transamerica Pyramid being shown:

Route between the Ferry Building and Transamerica Pyramid

You can even get at the raw instructions for this route and display them to the user independently of the map control.

void query_QueryCompleted(object sender, QueryCompletedEventArgs<Route> e)
{
myMap.AddRoute(new MapRoute(e.Result));
StringBuilder sb = new StringBuilder();
sb.AppendLine("Distance to destination: " + e.Result.LengthInMeters);
sb.AppendLine("Time to destination: " + e.Result.EstimatedDuration);
foreach (var maneuver in e.Result.Legs.SelectMany(l => l.Maneuvers))
{
sb.AppendLine("At " + maneuver.StartGeoCoordinate + " " +
maneuver.InstructionKind + ": " +
maneuver.InstructionText + " for " +
maneuver.LengthInMeters + " meters");
}
MessageBox.Show(sb.ToString());
}

You can see, below, that you have all the data required to build our own navigation app.

All the data needed to build a navigation app.


Geocoding

As the Routing example shows it's possible to use the new Map Services without using the Map Control. Another such service is the GeoCoding service. GeoCoding is the ability to transform a string into potential geocoordinates and other geolocation data. This code demonstrates that using a search for the San-Francisco Ferry Building:

private void Maps_GeoCoding(object sender, RoutedEventArgs e)
{
GeocodeQuery query = new GeocodeQuery()
{
GeoCoordinate = new GeoCoordinate(0, 0),
SearchTerm = "Ferry Building, San-Francisco"
};
query.QueryCompleted += query_QueryCompleted;
query.QueryAsync();
}
 
void query_QueryCompleted(object sender, QueryCompletedEventArgs<IList<MapLocation>> e)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("Ferry Building Geocoding results...");
foreach (var item in e.Result)
{
sb.AppendLine(item.GeoCoordinate.ToString());
sb.AppendLine(item.Information.Name);
sb.AppendLine(item.Information.Description);
sb.AppendLine(item.Information.Address.BuildingFloor);
sb.AppendLine(item.Information.Address.BuildingName);
sb.AppendLine(item.Information.Address.BuildingRoom);
sb.AppendLine(item.Information.Address.BuildingZone);
sb.AppendLine(item.Information.Address.City);
sb.AppendLine(item.Information.Address.Continent);
sb.AppendLine(item.Information.Address.Country);
sb.AppendLine(item.Information.Address.CountryCode);
sb.AppendLine(item.Information.Address.County);
sb.AppendLine(item.Information.Address.District);
sb.AppendLine(item.Information.Address.HouseNumber);
sb.AppendLine(item.Information.Address.Neighborhood);
sb.AppendLine(item.Information.Address.PostalCode);
sb.AppendLine(item.Information.Address.Province);
sb.AppendLine(item.Information.Address.State);
sb.AppendLine(item.Information.Address.StateCode);
sb.AppendLine(item.Information.Address.Street);
sb.AppendLine(item.Information.Address.Township);
}
MessageBox.Show(sb.ToString());
}

This code return a lot of data, which can be displayed to the user to verify the result, but most importantly your app will then have the GeoLocation for the searched place.

Geolocation data


Camera: Lenses apps

One of the exciting new integration points in Windows Phone 8 is the ability to build apps into the camera app. Users can launch “Lenses” apps directly from the camera app. “Lenses” apps are so called because they should show a picture frame with the active Camera view and act as a “Lens”. However, Lenses apps aren't filters, they're fully fledged apps.

Workflow of using a Lens app

To see how this work, create a trivial “Lenses” app, have it show up in the Lens Picker, and then save a picture without applying a filter.

You start by declaring the existing app as a “Lenses” app and making sure it has permissions to access the camera and save images to the media library.

<Capabilities>
<Capability Name="ID_CAP_ISV_CAMERA" />
Capability Name="ID_CAP_MEDIALIB_PHOTO" />
</Capabilities>
 
<Extensions>
<Extension ExtensionName="Camera_Capture_App" ConsumerID="{5B04B775-356B-4AA0-AAF8-6491FFEA5631}" TaskID="_default" />
</Extensions>

Next, under the Images folder, you need to add icons for all three supported screen resolutions. Here's the Lens.Screen-WVGA.png used by the app.

Paint.net with a Lens icon

When the app starts up it'll get a deeplink to /MainPage.xaml?Action=ViewfinderLaunch. Your code can now intercept that link using a custom UriMapper and send it to /Lens.xaml.

RootFrame.UriMapper = new MyAppUriMapper();
class MyAppUriMapper : UriMapperBase
{
public override Uri MapUri(Uri uri)
{
string tempUri = uri.ToString();
if (tempUri.Contains("ViewfinderLaunch"))
{
return new Uri("/Lens.xaml", UriKind.Relative);
}
else
{
return uri;
}
}
}

Now you can add some code to Lens.xaml to display the back camera and then save the image, unchanged, to the Photos library.

<Button Content="Snap Picture" Click="SaveImage" />
<Grid x:Name="ContentPanel" Grid.Row="1" >
<Grid.Background>
<VideoBrush x:Name="viewfinderBrush" />
</Grid.Background>
</Grid>
public partial class Lense : PhoneApplicationPage
{
public Lense()
{
InitializeComponent();
this.Loaded += Lense_Loaded;
}
 
PhotoCamera cam;
MediaLibrary library = new MediaLibrary();
 
void Lense_Loaded(object sender, RoutedEventArgs e)
{
if (PhotoCamera.IsCameraTypeSupported(CameraType.Primary))
{
cam = new Microsoft.Devices.PhotoCamera(CameraType.Primary);
 
cam.CaptureImageAvailable +=cam_CaptureImageAvailable;
viewfinderBrush.SetSource(cam);
}
}
 
void cam_CaptureImageAvailable(object sender, ContentReadyEventArgs e)
{
library.SavePictureToCameraRoll(new Random().Next(1, 1000) + ".jpg", e.ImageStream);
}
 
private void SaveImage(object sender, RoutedEventArgs e)
{
cam.CaptureImage();
}
}

Running the app you will see it app in the “apps list” for the camera app and can follow the overall "Lenses" workflow.

Animated GIF showing the workflow of using a custom Lens app


Camera: Video Recording

With WP7 it was possible for apps to take videos but it was very labour intensive. With the new WP8 WinPRT APIs for Video capture it's now quite easy to capture a video and save it anywhere. Here is a quick sample of capturing a video for 2 seconds, saving it to IsoStore, and then playing it back.

private async void RecordAndPlayVideo(object sender, RoutedEventArgs e)
{
StorageFolder isoStore = await ApplicationData.Current.LocalFolder.GetFolderAsync("IsolatedStore");
var file = await isoStore.CreateFileAsync("foo.wmv", CreationCollisionOption.ReplaceExisting);
using (var s = await file.OpenAsync(FileAccessMode.ReadWrite))
{
var avDevice = await AudioVideoCaptureDevice.OpenAsync(CameraType.RearFacing,
AudioVideoCaptureDevice.GetAvailableCaptureResolutions(CameraType.RearFacing).First());
await avDevice.StartRecordingToStreamAsync(s);
Thread.Sleep(2000);
await avDevice.StopRecordingAsync();
}
 
new MediaPlayerLauncher()
{
Media = new Uri(file.Path, UriKind.Relative),
}.Show();
}

While the code sample might seem complex it's actually rather simple. First, it opens a Foo.wmv file in IsoStore to write the video to.

private async void RecordAndPlayVideo(object sender, RoutedEventArgs e)
{
StorageFolder isoStore = await ApplicationData.Current.LocalFolder.GetFolderAsync("IsolatedStore");
var file = await isoStore.CreateFileAsync("foo.wmv", CreationCollisionOption.ReplaceExisting);
using (var s = await file.OpenAsync(FileAccessMode.ReadWrite))
{
}
}

Next, it obtains the front facing camera.

private async void RecordAndPlayVideo(object sender, RoutedEventArgs e)
{
StorageFolder isoStore = await ApplicationData.Current.LocalFolder.GetFolderAsync("IsolatedStore");
var file = await isoStore.CreateFileAsync("foo.wmv", CreationCollisionOption.ReplaceExisting);
using (var s = await file.OpenAsync(FileAccessMode.ReadWrite))
{
 
var avDevice = await AudioVideoCaptureDevice.OpenAsync(CameraType.RearFacing,
AudioVideoCaptureDevice.GetAvailableCaptureResolutions(CameraType.RearFacing).First());
 
}
}

Then the code to simulate someone clicking a button that says “start capture” then waits for 2 seconds before clicking a button that says “stop capture”. Obviously in a more real-world scenario you'd have a “start recording” button and a “stop recording” button, but this code snippet will simulate that just as well.

private async void RecordAndPlayVideo(object sender, RoutedEventArgs e)
{
StorageFolder isoStore = await ApplicationData.Current.LocalFolder.GetFolderAsync("IsolatedStore");
var file = await isoStore.CreateFileAsync("foo.wmv", CreationCollisionOption.ReplaceExisting);
using (var s = await file.OpenAsync(FileAccessMode.ReadWrite))
{
var avDevice = await AudioVideoCaptureDevice.OpenAsync(CameraType.RearFacing,
AudioVideoCaptureDevice.GetAvailableCaptureResolutions(CameraType.RearFacing).First());
 
await avDevice.StartRecordingToStreamAsync(s);
Thread.Sleep(2000);
await avDevice.StopRecordingAsync();
 
}
}

And finally the app can play back the IsoStore foo.wmv file.

private async void RecordAndPlayVideo(object sender, RoutedEventArgs e)
{
StorageFolder isoStore = await ApplicationData.Current.LocalFolder.GetFolderAsync("Shared");
var file = await isoStore.CreateFileAsync("foo.wmv", CreationCollisionOption.ReplaceExisting);
using (var s = await file.OpenAsync(FileAccessMode.ReadWrite))
{
var avDevice = await AudioVideoCaptureDevice.OpenAsync(CameraSensorLocation.Back,
AudioVideoCaptureDevice.GetAvailableCaptureResolutions(CameraSensorLocation.Back).First());
await avDevice.StartRecordingToStreamAsync(s);
Thread.Sleep(2000);
await avDevice.StopRecordingAsync();
}
 
new MediaPlayerLauncher()
{
Media = new Uri(file.Path, UriKind.Relative),
}.Show();
}

Running the app will now capture 2 second of video and play it back:

Animated GIF showing recording a 2 second video


Camera: device information

Using the new Windows Phone 8 CameraDevice APIs it's possible to get additional information on photo and video camera settings. Information such as supported resolution, focus area, white balance, ISO, shutter sounds, and more are all available through this new API.

There's lots of information and features available through this API, but for the sake of brevity only a few are covered here.

private async void PhotoCameraProperties(object sender, RoutedEventArgs e)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("*** PhotoCaptureDevice statics ***");
sb.AppendLine("PhotoCaptureDevice.AvailableSensorLocations: "
+ PhotoCaptureDevice.AvailableSensorLocations.First()
+ " " + PhotoCaptureDevice.AvailableSensorLocations.Skip(1).First());
sb.AppendLine("PhotoCaptureDevice.GetAvailableCaptureResolutions(CameraSensorLocation.Back): "
+ PhotoCaptureDevice.GetAvailableCaptureResolutions(CameraSensorLocation.Back).First());
sb.AppendLine("PhotoCaptureDevice.GetAvailablePreviewResolutions(CameraSensorLocation.Back): "
+ PhotoCaptureDevice.GetAvailablePreviewResolutions(CameraSensorLocation.Back).First());
sb.AppendLine("PhotoCaptureDevice.IsFocusRegionSupported(CameraSensorLocation.Back): " +
PhotoCaptureDevice.IsFocusRegionSupported(CameraSensorLocation.Back));
sb.AppendLine("PhotoCaptureDevice.IsFocusSupported(CameraSensorLocation.Back): " +
PhotoCaptureDevice.IsFocusSupported(CameraSensorLocation.Back));
 
sb.AppendLine("*** PhotoCaptureDevice ***");
var photoDevice = await PhotoCaptureDevice.OpenAsync(CameraSensorLocation.Back,
PhotoCaptureDevice.GetAvailableCaptureResolutions(CameraSensorLocation.Back).First());
await photoDevice.ResetFocusAsync();
photoDevice.FocusRegion = new Windows.Foundation.Rect(200, 200, 200, 200);
await photoDevice.FocusAsync();
 
sb.AppendLine("*** PhotoCaptureDevice Properties ***");
sb.AppendLine("FocusRegion: " + photoDevice.FocusRegion);
sb.AppendLine("CaptureResolution: " + photoDevice.CaptureResolution);
sb.AppendLine("PreviewResolution: " + photoDevice.PreviewResolution);
sb.AppendLine("SensorLocation: " + photoDevice.SensorLocation);
sb.AppendLine("SensorRotationInDegrees: " + photoDevice.SensorRotationInDegrees);
 
sb.AppendLine("*** KnownCameraPhotoProperties Properties ***");
sb.AppendLine("ExposureCompensation: " +
photoDevice.GetProperty(KnownCameraPhotoProperties.ExposureCompensation));
sb.AppendLine("ExposureTime: " +
photoDevice.GetProperty(KnownCameraPhotoProperties.ExposureTime));
sb.AppendLine("FlashMode: " +
photoDevice.GetProperty(KnownCameraPhotoProperties.FlashMode));
sb.AppendLine("FlashPower: " +
photoDevice.GetProperty(KnownCameraPhotoProperties.FlashPower));
sb.AppendLine("FocusIlluminationMode: " +
photoDevice.GetProperty(KnownCameraPhotoProperties.FocusIlluminationMode));
sb.AppendLine("Iso: " +
 
photoDevice.GetProperty(KnownCameraPhotoProperties.Iso));
sb.AppendLine("LockedAutoFocusParameters: " +
photoDevice.GetProperty(KnownCameraPhotoProperties.LockedAutoFocusParameters));
sb.AppendLine("ManualWhiteBalance: " +
photoDevice.GetProperty(KnownCameraPhotoProperties.ManualWhiteBalance));
sb.AppendLine("SceneMode: " +
photoDevice.GetProperty(KnownCameraPhotoProperties.SceneMode));
sb.AppendLine("WhiteBalancePreset: " +
photoDevice.GetProperty(KnownCameraPhotoProperties.WhiteBalancePreset));
 
sb.AppendLine("*** KnownCameraGeneralProperties Properties ***");
sb.AppendLine("AutoFocusRange: " +
photoDevice.GetProperty(KnownCameraGeneralProperties.AutoFocusRange));
sb.AppendLine("IsShutterSoundEnabledByUser : " +
 
photoDevice.GetProperty(KnownCameraGeneralProperties.IsShutterSoundEnabledByUser ));
sb.AppendLine("IsShutterSoundRequiredForRegion: " +
photoDevice.GetProperty(KnownCameraGeneralProperties.IsShutterSoundRequiredForRegion));
sb.AppendLine("ManualFocusPosition: " +
photoDevice.GetProperty(KnownCameraGeneralProperties.ManualFocusPosition));
photoDevice.Dispose();
MessageBox.Show(sb.ToString());
}

Running this code displays the following results in the WP8 emulator:

You can get similar information for the video camera:

private async void AudioVideoCameraProperties(object sender, RoutedEventArgs e)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("*** AudioVideoCaptureDevice statics ***");
sb.AppendLine("AudioVideoCaptureDevice.AvailableSensorLocations: "
+ AudioVideoCaptureDevice.AvailableSensorLocations.First()
+ " " + AudioVideoCaptureDevice.AvailableSensorLocations.Skip(1).First());
sb.AppendLine("AudioVideoCaptureDevice.GetAvailableCaptureResolutions(CameraSensorLocation.Back): "
+ AudioVideoCaptureDevice.GetAvailableCaptureResolutions(CameraSensorLocation.Back).First());
sb.AppendLine("AudioVideoCaptureDevice.GetAvailablePreviewResolutions(CameraSensorLocation.Back): "
+ AudioVideoCaptureDevice.GetAvailablePreviewResolutions(CameraSensorLocation.Back).First());
sb.AppendLine("AudioVideoCaptureDevice.SupportedAudioEncodingFormats: " +
AudioVideoCaptureDevice.SupportedAudioEncodingFormats.First());
sb.AppendLine("AudioVideoCaptureDevice.SupportedVideoEncodingFormats: " +
AudioVideoCaptureDevice.SupportedVideoEncodingFormats.First());
 
sb.AppendLine("*** AudioVideoCaptureDevice ***");
var avDevice = await AudioVideoCaptureDevice.OpenAsync(CameraSensorLocation.Back,
AudioVideoCaptureDevice.GetAvailableCaptureResolutions(CameraSensorLocation.Back).First());
 
sb.AppendLine("*** AudioVideoCaptureDevice properties ***");
sb.AppendLine("AudioEncodingFormat: " + avDevice.AudioEncodingFormat);
sb.AppendLine("CaptureResolution: " + avDevice.CaptureResolution);
sb.AppendLine("FocusRegion: " + avDevice.FocusRegion);
sb.AppendLine("PreviewResolution: " + avDevice.PreviewResolution);
sb.AppendLine("SensorLocation: " + avDevice.SensorLocation);
sb.AppendLine("SensorRotationInDegrees: " + avDevice.SensorRotationInDegrees);
sb.AppendLine("VideoEncodingFormat: " + avDevice.VideoEncodingFormat);
 
sb.AppendLine("*** KnownCameraPhotoProperties properties ***");
sb.AppendLine("UnmuteAudioWhileRecording: " +
avDevice.GetProperty(KnownCameraAudioVideoProperties.UnmuteAudioWhileRecording));
sb.AppendLine("VideoFrameRate: " +
avDevice.GetProperty(KnownCameraAudioVideoProperties.VideoFrameRate));
sb.AppendLine("VideoTorchMode: " +
avDevice.GetProperty(KnownCameraAudioVideoProperties.VideoTorchMode));
sb.AppendLine("VideoTorchPower: " +
avDevice.GetProperty(KnownCameraAudioVideoProperties.VideoTorchPower));
 
sb.AppendLine("*** KnownCameraGeneralProperties properties ***");
sb.AppendLine("AutoFocusRange: " +
avDevice.GetProperty(KnownCameraGeneralProperties.AutoFocusRange));
sb.AppendLine("IsShutterSoundEnabledByUser: " +
avDevice.GetProperty(KnownCameraGeneralProperties.IsShutterSoundEnabledByUser));
sb.AppendLine("PlayShutterSoundOnCapture: " +
avDevice.GetProperty(KnownCameraGeneralProperties.PlayShutterSoundOnCapture));
sb.AppendLine("IsShutterSoundRequiredForRegion: " +
avDevice.GetProperty(KnownCameraGeneralProperties.IsShutterSoundRequiredForRegion));
sb.AppendLine("ManualFocusPosition: " +
avDevice.GetProperty(KnownCameraGeneralProperties.ManualFocusPosition));
 
avDevice.Dispose();
 
MessageBox.Show(sb.ToString());
}

Running this code snippet from you app displays the following information in the WP8 emulator:


Wallet: Deals

The new Wallet app in Windows Phone 8 is a great opportunity for you to get noticed. The Wallet app can be seen as a hub for apps offering local goods and services: Think of a hub built for whenever the users want to do “something somewhere”. That's the wallet. As part of the wallet API developers can add one-time deals, multi-use Payment Instruments, or just generic wallet items.

To illustrate, start off by adding a new deal to the device wallet. In the real world this would be an offering in the style of Groupon, where you can add local deals to the Wallet to be used later. In this trivial example you'll add “one free article” to the wallet. This example uses as many properties as possible to show off the versatility of the Wallet, but only a few are mandatory.

private async void Wallet_CreateNewDeal(object sender, RoutedEventArgs e)
{
var item = new Deal()
{
MerchantName = "JustinAngel.net",
DisplayName = "Free Blog article!",
Description = "Justin will give you a free blog post.",
CustomerName = "You",
ExpirationDate = DateTime.Now.AddDays(14),
IssuerName = "JustinAngel.net Inc.",
IssuerWebsite = new Uri("http://JustinAngel.net"),
 
NavigationUri = new Uri("/mainpage.xaml?wallet=Deal", UriKind.Relative),
 
Notes = "I like turtles.",
OfferWebsite = new Uri("http://JustinAngel.net/wp7"),
TermsAndConditions = "May only be used once. OK, twice.",
Logo99x99 = GetBitmapSource("99x99.png"),
Logo159x159 = GetBitmapSource("159x159.png"),
Logo336x336 = GetBitmapSource("336x336.png"),
BarcodeImage = GetBitmapSource("Barcode.png")
};
 
await item.SaveAsync();
 
Launcher.LaunchUriAsync(new Uri("wallet://", UriKind.RelativeOrAbsolute));
}
 
private BitmapSource GetBitmapSource(string url)
{
var bmp = new BitmapImage();
bmp.SetSource(Application.GetResourceStream(new Uri(url, UriKind.Relative)).Stream);
return bmp;
}

There's a few interesting things going on in this code snippet. You first create a new “Deal” wallet item. Then fill in a few properties such as a unique website for the offering, an ID, some display properties, and a barcode. The item is then saved to the wallet and the wallet app launched.

You also need to add a permission that'll allow the app to add Wallet Items.

<Capability Name="ID_CAP_WALLET" />

And here see how the final wallet item looks:

Users can click the "open app" button and it'll open the app currently associated with this Wallet Item: The code has specified a deeplink to “/mainpage.xaml?wallet=Deal” as part of the wallet item. When the user clicks that button you can see that this is the link used.

MessageBox showing the wallet=deal parameter

There's lots of ways for a user to cash in their one-time deal. It could be done at the Offer website running on a web server, as part of the app, by scanning the barcode on a Deal, and so on. It all depends on the specific business use for the Wallet.


Wallet: Background Agents

As part of the Wallet functionality a new Background Agent has been introduced to WP8. This agent helps refresh data in wallet items, receive activations/deactivations of the NFC secure element associated with the wallet item, and action any payment changes made in the wallet item.

Here's an example of a simple Background Agent that responds to a “refresh” requests in a wallet item. This is done so the app can get the latest information on the wallet items from the cloud. This simple background agent will always fail to get data (because there is no data in the cloud) and mark all items as “user attention is required”.

<Tasks>
<DefaultTask Name="_default" NavigationPage="MainPage.xaml" />
<ExtendedTask Name="BackgroundTask">
<BackgroundServiceAgent Specifier="WalletAgent"
Name="myWalletAgent"
Source="WP8_Beta_22"
Type="WP8_Beta_22.myWalletAgent" />
</ExtendedTask>
</Tasks>
public class myWalletAgent : WalletAgent
{
protected override void OnRefreshData(RefreshDataEventArgs args)
{
foreach (WalletItem item in args.Items)
{
item.SetUserAttentionRequiredNotification(true);
}
 
base.OnRefreshData(args);
NotifyComplete();
}
}

After "Refreshing" the item details, either manually or automatically, you will see that the wallet item requires attention:

Instead of marking the item as “requiring attention” you could've easily updated properties from data retrieved from the cloud.


Wallet: Payment Instrument

Personally, I've always wanted to have my own currency and call it “Justin Smiley Dollars”. Well, there are lots of apps that include virtual currency and it makes sense that those should be in the user's wallet. Payment instrument are different from “deals”, in that they maintain a balance on an account maintained by your backend.

When adding PaymentInstruments to a Wallet the user has to approve adding the wallet item and your app needs an additional capability.

<Capability Name="ID_CAP_WALLET_PAYMENTINSTRUMENTS" />

Now add 50$? (Justin Smiley dollars) to the user's wallet. (The official currency symbol of Justin Dollars is “$?”)

private void Wallet_CreateNewPaymentInstrument(object sender, RoutedEventArgs e)
{
var item = new PaymentInstrument()
{
IssuerName = "JustinAngel.net",
CustomerName = "You",
ExpirationDate = DateTime.Now.AddDays(1),
PaymentInstrumentKinds = PaymentInstrumentKinds.Debit,
DisplayName = "Justin Smiley Dollars $?",
Logo99x99 = GetBitmapSource("99x99.png"),
Logo336x336 = GetBitmapSource("336x336.png"),
Logo159x159 = GetBitmapSource("159x159.png"),
DisplayAvailableBalance = "50$?",
DisplayBalance = "100$?",
DisplayCreditLimit = "200$?",
DisplayAvailableCredit = "200$?",
BackgroundColor = Color.FromArgb(255, 70, 150, 250),
Nickname = "Justin Dollars $?",
Message = "I like turtles.",
MemberSince = DateTime.Now.AddYears(-1),
AccountNumber = "12345679",
NavigationUri = new Uri("/mainpage.xaml?wallet=PaymentInstrument", UriKind.Relative)
};
 
AddWalletItemTask task = new AddWalletItemTask()
{
Item = item
};
task.Completed += (s, args) => MessageBox.Show(args.TaskResult.ToString());
task.Show();
 
Launcher.LaunchUriAsync(new Uri("wallet://", UriKind.RelativeOrAbsolute));
}

Running this code snippet you can see that the wallet asks for permission to save the item. Tapping “save” will save the item to the Wallet. Tap “review” and you're taken to a review screen where you can save, edit or cancel.

You can now open the wallet and see how the new payment instrument looks.

As you can see clicking the “Open app” button opens up the app using with the specified querystring related to this app. In real life the Wallet Item can be anything you'd like such as a “Starbucks card” that can be scanned when you want a cup of coffee, virtual currency for games, or credits to a file sharing website.


Wallet: Generic wallet items

The third item type you can add to the wallet is the WalletTransactionItem and is really meant as a generic catch-all for whatever isn't a deal or a payment instrument. The example here shows how to use in-app purchase to add a movie ticket stub to the wallet.

private void Wallet_CreateNewWalletTransactionItem(object sender, RoutedEventArgs e)
{
var item = new WalletTransactionItem()
{
DisplayName = "Dark Knight Rises",
IssuerName = "Justin's Movie Theater",
CustomerName = "You",
Notes = "Admits one person to see Batman Dark Knight Rises.",
ExpirationDate = DateTime.Now.AddDays(14),
Logo99x99 = GetBitmapSource("99x99Batman.png"),
Logo336x336 = GetBitmapSource("336x336Batman.png"),
Logo159x159 = GetBitmapSource("159x159Batman.png"),
BarcodeImage = GetBitmapSource("Barcode.png"),
NavigationUri = new Uri("/mainpage.xaml?wallet=WalletTransactionItem", UriKind.Relative)
};
 
AddWalletItemTask task = new AddWalletItemTask()
{
Item = item
};
task.Completed += (s, args) => MessageBox.Show(args.TaskResult.ToString());
 
task.Show();
 
Launcher.LaunchUriAsync(new Uri("wallet://", UriKind.RelativeOrAbsolute));
}

Running the code snippet you can see that the wallet offers the same save/review dialogue as it did for payment instruments:

When you open up the Wallet you can see how the generic wallet item looks.

And if you tap the “open app” button you can see that it uses the deeplink specified for this item.

MessageBox showing the wallet=WalletTransactinItem parameter


Apps Tracking GeoLocation in the background

With Windows Phone 8 a new background processing model is supported for GeoLocation apps. GeoLocation apps can ask for the whole app remains alive in memory and receive GeoLocation notifications. The use case here is that after a user has initiated a GeoLocation action in an app, that shouldn't stop even if the app goes into the background. Turn-by-turn navigation is a great example of something users don't want to lose before they have reached their destination. There are a few limitations on this you should be aware of. There can be only one app tracking location in the background, it may be killed at any moment, running the app in the background will drain the phone's batteries, and you're limited to a subset of APIs that work well in the background.

In order to let apps get GeoLocation, they first must specify the required security permission.

<Capability Name="ID_CAP_LOCATION" />

To make sure your app won't get shutdown while tracking GeoLocation, set the BackgroundExecution on the app's WmAppManfiest default task.

<DefaultTask Name="_default" NavigationPage="MainPage.xaml">
<BackgroundExecution>
<ExecutionType Name="LocationTracking" />
</BackgroundExecution>
</DefaultTask>

Now register for a notification when the app goes into the background location execution mode, by registering to the RunningInBackground event in App.xaml. In this trivial example you'll show a toast notification to the user, to let them know the app is now tracking them. A more real-world app would use this event to close down any volatile resources (cameras, sensors, etc.).

<shell:PhoneApplicationService
Launching="Application_Launching" Closing="Application_Closing"
Activated="Application_Activated" Deactivated="Application_Deactivated"
RunningInBackground="Application_RunningInBackground"
/>
private void Application_RunningInBackground(object sender, RunningInBackgroundEventArgs e)
{
new ShellToast()
{
Title = "My App",
Content = "Is tracking you"
}.Show();
}

In order for the app to run in the background, you'll have to spin up one of the new GeoLocator class and start tracking the user's location.

private Geolocator geolocator = null;
private void Location_ContinuousLocation(object sender, RoutedEventArgs e)
{
geolocator = new Geolocator();
geolocator.DesiredAccuracy = PositionAccuracy.High;
geolocator.MovementThreshold = 1;
geolocator.PositionChanged += geolocator_PositionChanged;
 
MessageBox.Show("Hit the home button and start walking around. " +
"Watch out for ShellTile notfications and changes to the app's pinned tile.");
}

In this example you'll update the pinned app tile to show the toast notification whenever the user's GeoLocation changes.

void geolocator_PositionChanged(Geolocator sender, PositionChangedEventArgs args)
{
string geoLoc = args.Position.Coordinate.Latitude.ToString() + ", " +
args.Position.Coordinate.Longitude.ToString();
 
ShellToast t = new ShellToast()
{
Title = "Location @ ",
Content = geoLoc,
NavigationUri = new Uri("/MainPage.xaml?start=geoloc", UriKind.Relative)
};
t.Show();
 
ShellTile.ActiveTiles.First()
.Update(new IconicTileData()
{
Title = geoLoc
});
}

Running the code snippet starts the GeoLocation tracking and you'll see a toast notification letting you know the app is tracking your location.

Now open up the emulator location panel and see what happens when you change the GeoLocation.

GeoLocation tracking changing the live tile and showing a push notification


OS write access: Creating Calendar appointments

As part of Windows Phone 8 you can show a dialogue to the user asking to add Calendar appointments. Your apps can prepopulate this dialogue with details about the event. The app can also determine when an alarm will sound and how this event displays in the Calendar.

private void WriteAccess_Meeting(object sender, RoutedEventArgs e)
{
new SaveAppointmentTask()
{
Subject = "Meet with Justin!",
Details = "Spend quality time with Justin",
Location = "At Justin's Home",
AppointmentStatus = AppointmentStatus.Busy,
StartTime = DateTime.Now.AddHours(1),
EndTime = DateTime.Now.AddHours(2),
IsAllDayEvent = false,
Reminder = Reminder.FiveMinutes
}.Show();
}

Running this code snippet you see the following dialogue that asks confirmation to add this appointment. It also allows details to be changed before the event is saved.

Confirmation dialog for adding an appointment to the Calendar

Once saved you can open the Calendar and see the event.


OS write access: Contacts

In Windows Phone 8 you can add contacts to the People Hub without a user's explicit approval: Apps have their own ContactStore they can add contacts to. If the app is ever uninstalled all the contacts associated with that app are removed as well. That should be sufficient motivation not to spam the People Hub.

In this code your app gets the ContactStore for the app in read/write mode, add a new contact, and save it.

private async void WriteAccess_Contact(object sender, RoutedEventArgs e)
{
var store = await ContactStore.CreateOrOpenAsync(
ContactStoreSystemAccessMode.ReadWrite,
ContactStoreApplicationAccessMode.LimitedReadOnly);
 
var contact = new StoredContact(store)
{
DisplayName = "myJustin",
GivenName = "Justin",
FamilyName = "Angel",
HonorificPrefix = "Captain ",
HonorificSuffix = "III",
RemoteId = "12345"
};
 
contact.SetDisplayPictureAsync(await GetInputStreamForContent("99x99.png"));
 
await contact.SaveAsync();
 
MessageBox.Show("Open up the People hub and check it out",
"Contact saved",
MessageBoxButton.OK);
}

Running this code snippet you can see a new contact in the People Hub, which can be pinned to the phone's start menu.


App2app: Custom protocols

In Windows Phone 8 an app can expose custom endpoints that other apps can use to launch that app. An app can expose that endpoint by registering for a “custom protocol” (similar to the familiar HTTP and HTTPS protocols). The custom protocol can be any protocol that isn't currently reserved by the WP8 OS. Other apps can then invoke that custom protocol with a string payload.

It's interesting to note that if no apps that support the custom protocol are installed the store will open up with a list of apps that use the protocol. If only a single app is installed that supports that protocol then it will open up directly. And if two or more apps are installed that support the custom protocol, the user will see a screen where they can choose the app to open.

Here is an example of how to expose an app2app custom protocol endpoint. It registers the “foo” protocol so any invocation of the “foo://something” Uri will open up your app. In WmAppManifest add the following <Protocol /> elements.

<Extensions>
<Protocol Name="foo" NavUriFragment="encodedLaunchUri=%s" TaskID="_default" />
</Extensions>

You'll need to catch any navigation into our app that starts with “/Protocol”. To do this, add a custom UriMapper to your app to find the deeplink used to link into the app and send this link to MainPage.xaml.

RootFrame.UriMapper = new MyAppUriMapper();
public class MyAppUriMapper : UriMapperBase
{
public override Uri MapUri(Uri uri)
{
string tempUri = uri.ToString();
if (tempUri.StartsWith("/Protocol?encodedLaunchUri="))
{
string deeplink = tempUri.Substring(tempUri.IndexOf("foo"));
return new Uri("/MainPage.xaml?deepLink=" + deeplink, UriKind.Relative);
}
else
{
return uri;
}
}
}

Now get the deeplink and display it in a MessageBox or do whatever you'd like with it.

protected async override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (NavigationContext.QueryString.Any())
{
StringBuilder sb = new StringBuilder();
foreach (var key in NavigationContext.QueryString.Keys)
{
sb.AppendLine(key + ": " + NavigationContext.QueryString[key]);
}
MessageBox.Show(sb.ToString(), "Page Querystring", MessageBoxButton.OK);
}
}

You can now activate the app by using this link anywhere. For example, it can be embedded into a webpage.

<a href="foo://someText">Open Custom Protocol foo://someText</a>

When you open a browser with this link, tapping the link opens your app with the correct payload.

Other apps can also launch your app2app endpoint, by using the Launcher class.

private void App2app_CustomProtocol(object sender, RoutedEventArgs e)
{
Launcher.LaunchUriAsync(new Uri("foo://SomeText"));
}


App2app: File associations

Windows Phone 8 Apps can register as handlers for specific file extensions. The app exposing this app2app endpoint will have to specify which file extensions it wants to handle and what file icons to display in the email client, office hub, and browser. File associations may only be registered for file extensions that aren't used by the operating system and quite a few extensions are reserved.

When no app supports opening up a file, the store will open and allow users to download apps that support that file extension. If only a single app is installed that supports that file extension then that app will open up with a read-only copy of that file. And if more than one installed app supports an extension, the user will see a dialogue where they can choose the app to open.

Here is an example of registering to the “foo” file extension. You'll add the following registration to the WmAppManifest.

<Extensions>
<FileTypeAssociation Name="foo" TaskID="_default" NavUriFragment="fileToken=%s">
<Logos>
<Logo Size="small">33x33.png</Logo>
<Logo Size="medium">69x69.png</Logo>
<Logo Size="large">176x176.png</Logo>
</Logos>
<SupportedFileTypes>
<FileType ContentType="application/foo">.foo</FileType>
</SupportedFileTypes>
</FileTypeAssociation>
<Protocol Name="foo" NavUriFragment="encodedLaunchUri=%s" TaskID="_default" />
</Extensions>

Next you need to handle all navigation into the app that starts with “/FileTypeAssociation”: Use a custom UriMapper to route the file token to MainPage.xaml.

public class MyAppUriMapper : UriMapperBase
{
public override Uri MapUri(Uri uri)
{
string tempUri = uri.ToString();
if (tempUri.StartsWith("/FileTypeAssociation?fileToken="))
{
string token = tempUri.Substring(tempUri.IndexOf("fileToken=") + 10);
return new Uri("/MainPage.xaml?fileToken=" + token, UriKind.Relative);
}
else
{
return uri;
}
}
}

In the page itself, you'll get the deeplink used for the file token. Once you have the FileToken you can copy the file into IsoStore and read its contents.

protected async override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
base.OnNavigatedTo(e);
 
if (NavigationContext.QueryString.ContainsKey("fileToken"))
{
string token = NavigationContext.QueryString["fileToken"];
string fileName = SharedStorageAccessManager.GetSharedFileName(token);
 
StorageFolder isoStore = ApplicationData.Current.LocalFolder;
var file = await SharedStorageAccessManager.CopySharedFileAsync(
isoStore, fileName, NameCollisionOption.ReplaceExisting, token);
using (var read = await file.OpenReadAsync())
using (var s = read.AsStreamForRead())
using (var sr = new StreamReader(s))
{
string contents = sr.ReadToEnd();
 
MessageBox.Show(
"token: " + token + Environment.NewLine +
"fileName: " + fileName + Environment.NewLine +
"contents: " + contents + Environment.NewLine);
}
}
}

If you send yourselves a *.foo file via email you will see the app launch as the associated app and display its file token, file name, and contents.

This app2app file association will be invoked whenever the user browses files in the browser, in office, or via email. You can even launch the app2app file association from other apps using the Launcher class.

private async void App2app_FileExtensions(object sender, RoutedEventArgs e)
{
await Launcher.LaunchFileAsync(
await Package.Current.InstalledLocation.GetFileAsync("myCustomFile.foo"));
MessageBox.Show("This will only work when executed from another app into this app.");
}


mini-SD card: reading files

WP8 devices support additional storage using mini-SD cards. WP8 apps have read-only access to file types that are associated with the app via app2app file associations.

In order to access files hosted on the mini-SD storage card you have to specify access to the new ID_CAP_REMOVEABLE_STORAGE capability.

<Capability Name="ID_CAP_REMOVABLE_STORAGE" />

Now add a file with the “.foo” file extension to your SD card under “Justin's awesome folder”. Remember, the file extensions must match the app2app file associations.

Windows Phone SD Card in File Explorer showing a folder with a file

Once you have files on the SD card the app can iterate over it recursively and retrieve the files. You do that by first getting all ExternalStorageDevices.

private async void SDCard_iterateOverStuff(object sender, RoutedEventArgs e)
{
StringBuilder sb = new StringBuilder();
 
foreach (ExternalStorageDevice device in await ExternalStorage.GetExternalStorageDevicesAsync())
{
sb.AppendLine(device.ExternalStorageID + ": " + device.RootFolder.Name);
 
await SD_IterateOverFolders(sb, device.RootFolder);
}
MessageBox.Show(sb.ToString(), "External Storage", MessageBoxButton.OK);
}

Now that you have the RootFolder for ExternalStorageDevices the app can recursively iterate over all nested folders and find all available files in each folder. Once the app has found files, they can be opened and their content displayed.

private static async Task SD_IterateOverFolders(StringBuilder sb, ExternalStorageFolder folder)
{
sb.AppendLine(folder.Path);
 
var files = await folder.GetFilesAsync();
if (!files.Any())
{
sb.AppendLine("No files in " + folder.Path);
}
 
foreach (ExternalStorageFile file in files)
{
sb.AppendLine(file.Name);
using (var s = await file.OpenForReadAsync())
using (var sr = new StreamReader(s))
{
sb.AppendLine(sr.ReadToEnd());
}
}
 
foreach (ExternalStorageFolder nestedFolder in await folder.GetFoldersAsync())
{
SD_IterateOverFolders(sb, nestedFolder);
}
}

Run this code snippet on a phone with an SD card, the correct app2app configuration, and the suggested files you will see a MessageBox showing the SD card, a folder name, a file name, and the file's content.

MessageBox showing the SD card, a folder name, a file name and the file


Bluetooth: phone to phone

Windows Phone 8 provides a new Bluetooth APIs for third-party developers. You can use those APIs to send and receive information among phones and devices. Here is a simple example of sending and receiving messages between two WP8 phones.

Start by adding the required security permission to the app's manifest file. The ID_CAP_PROXIMITY capability is used to enable both NFC and Bluetooth.

<Capability Name="ID_CAP_PROXIMITY" />

In any phone-to-phone relationship one phone will be a “server” — listening to incoming requests and responding to them — and another phone will be the “client” — connecting to the server and issuing requests.

Here's how you setup a server to listen for incoming requests on phones with Bluetooth enabled. All you have to do is start the PeerFinder that advertises that this phone is listening to incoming requests.

private async void Bluetooth_ListenToIncomingSocket(object sender, RoutedEventArgs e)
{
PeerFinder.Start();
 
PeerFinder.ConnectionRequested += PeerFinder_OpenConnectionAndListen;
 
MessageBox.Show("Listening to Bluetooth_connections...");
}

Running this code snippet you will see a MessageBox confirming the server is listening for Bluetooth connections.

MessageBox confirming the server is listening to Bluetooth connections

Now that one phone is listening for incoming connections, the client phone can connect to the server phone.

private StreamSocket writeToSocket;
private async void Bluetooth_OpenSocket(object sender, RoutedEventArgs e)
{
PeerFinder.Start();
 
var peers = await PeerFinder.FindAllPeersAsync();
 
if (peers.Count == 0)
{
MessageBox.Show("No bluetooth peers found. Are you sure you have a listener nearby?");
}
else
{
writeToSocket = await PeerFinder.ConnectAsync(peers[0]);
 
Dispatcher.BeginInvoke(() =>
MessageBox.Show("Connected to " + peers[0].DisplayName + " bluetooth service."));
}
}

This code snippet is actually quite simple. It starts its own PeerFinder on the client, finds all the peers, and attempts to open a socket to the first peer. Running this code snippet you will see a MessageBox confirming the client app is connected to the WP8 server over Bluetooth.

MessageBox confirming the client app connected to the WP8 server via bluetooth

At this point there is a server listening to incoming requests and a client with an open socket to it. The next step will be to send a message through the open socket to the waiting server.

private IBuffer GetBufferFromString(string str)
{
using (var dw = new DataWriter())
{
dw.UnicodeEncoding = UnicodeEncoding.Utf16LE;
dw.WriteString(str);
return dw.DetachBuffer();
}
}
 
private async void Bluetooth_SendMessage(object sender, RoutedEventArgs e)
{
if (writeToSocket == null) return;
 
await writeToSocket.OutputStream.WriteAsync(GetBufferFromString(Hello world!));
 
Dispatcher.BeginInvoke(() =>
MessageBox.Show("Sent \"Hello World\"."));
}

You send the text as part of a Buffer. Using the DataWriter you could send over data of any type, such as a string, int, or byte array. Running this code snippet you will see a MessageBox confirming the client has sent a “Hello World” text.

MessageBox confirming we've sent over a “Hello World” text

The last step to creating the server-client Bluetooth relationship is to read the incoming string. Since you know the app is receiving a UTF16 string of 12 characters, the app should read 24 bytes from the buffer. However, in real-world phone-to-phone communication it's recommended that the first few bytes in every message will notify the overall size of the message. That way the server knows how many bytes to read.

private StreamSocket readStreamSocket = null;
private async void PeerFinder_OpenConnectionAndListen(object sender, ConnectionRequestedEventArgs args)
{
readStreamSocket = await PeerFinder.ConnectAsync(args.PeerInformation);
Dispatcher.BeginInvoke(() => MessageBox.Show("Acting as server to " + args.PeerInformation.DisplayName + "!"));
 
while (true)
{
// length of "Hello World!" packet
var buffer = await readStreamSocket.InputStream.ReadAsync(new Buffer(24), 24, InputStreamOptions.Partial);
var text = GetStringFromBuffer(buffer).Replace("\0", "");
 
if (!string.IsNullOrEmpty(text))
Dispatcher.BeginInvoke(() => MessageBox.Show(text, "Received text", MessageBoxButton.OK));
}
}

As you will see the code snippet listens to incoming connections and opens a listening socket as the server as it becomes available. It then goes into a while(true) loop with an async wait until the app has a fully formed message. It'll then print the message to the screen.

MessageBox showing the received text


Bluetooth: Phone to device

Windows Phone 8 Bluetooth communication can also be used to communicate between Windows Phone and a plethora of Bluetooth enabled devices. In this example you'll use the Sphero robotic ball.

Bluetooth powered Sphero robotic balls

As a security precaution Windows Phone 8 can only communicate with previously paired Bluetooth devices: So make sure the phone has Bluetooth enabled and any device paired.

In order to send Sphero commands, you'll look in the list of all Bluetooth paired devices, grab the first Sphero device you find and open a socket to it.

private StreamSocket spheroSocket;
private async void Bluetooth_ConnectToSphero(object sender, RoutedEventArgs e)
{
PeerFinder.AlternateIdentities["Bluetooth:Paired"] = "";
 
var peers = await PeerFinder.FindAllPeersAsync();
 
if (!peers.Any(p => p.DisplayName.Contains("Sphero")))
{
MessageBox.Show("Sphero not found. Is bluetooth on? Is Sphero paired?");
}
else
{
PeerInformation spheroPeer = peers.First(p => p.DisplayName.Contains("Sphero"));
 
spheroSocket = new StreamSocket();
 
await spheroSocket.ConnectAsync(spheroPeer.HostName, "1");
 
MessageBox.Show("Connected to " + peers[0].DisplayName + " bluetooth service.");
}
}

One thing you’ll notice is that you have to know ahead of time that the Bluetooth service name for Sphero is “1”.

Running this code snippet with a paired Sphero nearby you can see WP8 successfully connected to Sphero.

MessageBox confirming WP8 is connected to Sphero

Next send Sphero any commands you'd like, perhaps a simple colour change command for a random colour. Note, how the Sphero byte[] commands are calculated is not fully covered, just the final byte[] sent over to the device. (You can find more on Sphero byte commands on the Sphero developer site.)

private async void Bluetooth_SpheroToRandomColour(object sender, RoutedEventArgs e)
{
if (spheroSocket == null)
{
MessageBox.Show("Connect to Sphero first.");
return;
}
 
Random random = new Random();
Color randomColor = Color.FromArgb(254,
(byte)random.Next(1, 254),
(byte)random.Next(1, 254),
(byte)random.Next(1, 254));
 
// sample package is 255, 255, 2, 32, 1, 4, 0, 0, 0, 216
byte[] package = new ColorLedCommand(randomColor).ToPacket();
spheroSocket.OutputStream.WriteAsync(GetBufferFromByteArray(package));
}
 
private IBuffer GetBufferFromByteArray(byte[] package)
{
using (DataWriter dw = new DataWriter())
{
dw.WriteBytes(package);
return dw.DetachBuffer();
}
}

You will see that you can send any type of data over the connected Bluetooth socket. In this case you've sent over the BT-SPP packet expected by Sphero. Running this code snippet three times changes the Sphero's colour three times.

Sphero ball in a different colour after WP8 sent the change colour command


NFC: "Tap and Do" / Reading and writing from tags

In Windows Phone 8 you have a new API for NFC. NFC is a communication technology with a 2cm-4cm range and a theoretical average bandwidth of 200kbs-400kbs. NFC might be best known for its role in Wallet style solutions, similar to those discussed earlier) that use tapping to signify the intent to purchase. NFC is a pretty exciting technology that provides for communication between two phones or between a phone and an NFC tag.

For this example any NFC tag supported by Windows Phone 8 will do. You can get a sample bundle of NFC tags here.

Pictures of NFC Tags

In order to write to an NFC tag you'll need to get a ProximityDevice. Check if it's supported on the phone (by checking that ProximityDevice.GetDefault() isn't null) and, if it's supported, publish a message.

private void NFC_WriteText(object sender, RoutedEventArgs e)
{
ProximityDevice device = ProximityDevice.GetDefault();
 
if (device != null)
{
var id = device.PublishBinaryMessage("Windows:WriteTag.JustinAngel",
GetBufferFromString("Hello World!"),
UnregisterOnSend);
 
MessageBox.Show("Published Message: Hello World. ID is " + id);
}
}
 
 
private void UnregisterOnSend(ProximityDevice sender, long messageid)
{
sender.StopPublishingMessage(messageid);
}

The type of message published determines a lot about what gets written to the tag. This code snippet writes a “JustinAngel” message which is not a standard message type: So only the example app is expected to use it. Running the code snippet you will see confirmation that “Hello World!” was written on the tag.

MessageBox confirming

Next you can intercept the “Windows.JustinAngel” message type and display its contents on the screen. The message handler will only be invoked when an NFC tag using that message type is tapped on the phone.

private void NFC_ReadText(object sender, RoutedEventArgs e)
{
ProximityDevice device = ProximityDevice.GetDefault();
 
if (device != null)
{
device.SubscribeForMessage("Windows.JustinAngel", textMessageRecieved);
 
MessageBox.Show("Registered to NFC tag. Tap with NFC tag.");
}
}
 
private void textMessageRecieved(ProximityDevice sender, ProximityMessage message)
{
sender.StopSubscribingForMessage(message.SubscriptionId);
 
Dispatcher.BeginInvoke(() =>
MessageBox.Show("NFC data: " + message.DataAsString.Replace("\0", ""),
message.MessageType,
MessageBoxButton.OK));
}

Running this code snippet and tapping the NFC tag on which the message was written and your phone will show the following MessageBox confirming the content of the NFC tag.

MessageBox showing the NFC Tag data is

Instead of writing custom app messages, you can also write types used by the WP8 OS such as URIs. The default behaviour for WP8 when tapped against a HTTP URI NFC Tag is to ask permission to open the website and open it in IE10.

Start by writing a URI to an NFC tag.

private void NFC_WriteNdefUri(object sender, RoutedEventArgs e)
{
ProximityDevice device = ProximityDevice.GetDefault();
 
if (device != null)
{
long id = device.PublishBinaryMessage("WindowsUri:WriteTag",
GetBufferFromString("http://JustinAngel.net"),
UnregisterOnSend);
 
MessageBox.Show("Published Uri Message to JustinAngel.net. Tap to verify. ID is " + id);
}
}

Running this code snippet displays a confirmation that the NFC tag message was published.

MessageBox confirming NFC tag message published

Now, take that same NFC Tag, tap it against a phone, and the phone will open the URI in the IE10 browser.


NFC: "Tap and Share" / Phone to phone

NFC is often used to initiate a higher bandwidth communication channel between two phones, such as Bluetooth. In Windows Phone 8 you can publish NFC messages that will be received by another device and not written on a tag. Here is an example of that. You'll get one WP8 device to publish a message and a second device display it on the screen.

You can easily publish a message that'll be received by an app on another device as follows. You can see the “WriteTag” instructions are not included in this message type.

private void NFC_SendMessageToSameAppOnAnotherDevice(object sender, RoutedEventArgs e)
{
ProximityDevice device = ProximityDevice.GetDefault();
 
if (device != null)
{
var id = device.PublishBinaryMessage("Windows.JustinAngel",
GetBufferFromString("Hello World!"),
UnregisterOnSend);
 
MessageBox.Show("Published Message: Hello World. ID is " + id);
}
}

On the other device you'll register for all messages of the custom type.

private void NFC_ReadText(object sender, RoutedEventArgs e)
{
ProximityDevice device = ProximityDevice.GetDefault();
 
if (device != null)
{
device.SubscribeForMessage("Windows.JustinAngel", textMessageRecieved);
 
MessageBox.Show("Registered to NFC tag. Tap with NFC tag.");
}
}
 
private void textMessageRecieved(ProximityDevice sender, ProximityMessage message)
{
sender.StopSubscribingForMessage(message.SubscriptionId);
Dispatcher.BeginInvoke(() =>
MessageBox.Show("NFC data: " + message.DataAsString.Replace("\0", ""),
message.MessageType,
MessageBoxButton.OK));
}

Run these code snippets and tap two the devices together, you will see one device publishing a message and the other receiving that message.


Multi resolution

Windows Phone 8 supports phones with three screen resolutions and you should take that into account when developing apps. The resolutions are: WVGA (480x800 pixels), also used in Windows Phone 7; WXGA (768x1280 pixels), essentially an HD version of WVGA; and the wildcard 720P (720x1280 pixels) that uses a different aspect ratio to WVGA and WXGA. You should be aware of these different resolutions, make sure to use relative <Grid /> positioning in laying out screens, and potentially different media assets for different resolutions.

Here's a pixel perfect representation of the supported resolutions in Windows Phone 8.

Pixel perfect rendering of different WP8 resolutions

Windows Phone 8 introduces three new properties that can be used to check for a phone's resolution.

private void MutliRes_ScreenSize(object sender, RoutedEventArgs e)
{
MessageBox.Show(
"ActualHeight: " + Application.Current.Host.Content.ActualHeight + Environment.NewLine +
"ActualWidth: " + Application.Current.Host.Content.ActualWidth + Environment.NewLine +
"ScaleFactor : " + Application.Current.Host.Content.ScaleFactor + Environment.NewLine);
}

Windows Phone 8 also ships with an emulator for each screen resolution. You can choose to run the emulator in WVGA, WXGA, or 720P modes.

Menu on Emulator for choosing its screen resolution

Running the code snippet on all three emulators you will see something unexpected.

3 emulators in different resolutions showing the resolution properties

As expected the WVGA resolution used in Mango has 480x800 pixels and isn't scaled. But for WXGA instead of having apps with a 768x1280 canvas they still have the same 480x800 pixels only scaled up to 160%. So you don't need to worry about you UI reflowing on WXGA. For 720 it's similar story; apps don't get 720x1280 pixels canvas but rather an 853x480 canvas scaled up to 150%.

This unique implementation of multi-resolution support has a huge benefit for WP7 apps: they don't have to refactor their UIs from scratch to support multi-resolution. WVGA apps from Mango get scaled up 160% for WXGA. Those same apps will get scaled up 150% for 720p and get letterboxed for 26~ pixels at each side.

While this information about screen sizes and scaling is useful, you may have noticed you don't know exactly which resolution is used. But you can use the properties to infer which resolution is used.

private void MultiRes_Resolution(object sender, RoutedEventArgs e)
{
MessageBox.Show(
"IsHD: " + MultiRes.IsHighResolution + Environment.NewLine +
"Resolution: " + MultiRes.CurrentResolution);
}
 
public static class MultiRes
{
public static bool IsHighResolution
{
get { return Application.Current.Host.Content.ScaleFactor > 100; }
}
 
public static bool IsLowResolution
{
get { return Application.Current.Host.Content.ScaleFactor <= 100; }
}
 
public static bool IsWvga
{
get
{
return Application.Current.Host.Content.ActualHeight == 800
&& Application.Current.Host.Content.ScaleFactor == 100;
}
}
 
public static bool IsWxga
{
get { return Application.Current.Host.Content.ScaleFactor == 160; }
}
 
public static bool Is720p
{
get { return Application.Current.Host.Content.ScaleFactor == 150; }
}
 
public static String CurrentResolution
{
get
{
if (IsWvga) return "WVGA";
else if (IsWxga) return "WXGA";
else if (Is720p) return "HD720p";
else throw new InvalidOperationException("Unknown resolution");
}
}
}

Running this code snippet on an emulator in all three resolutions and you will see that it correctly reports back the current resolution and if it's HD or SD resolution.

Emulators in 3 different resolutions showing the MultiRes helper properties

Using the Information in a helper class MultiRes you can change the media assets on the screen. Even though apps will automatically scale up, bitmap assets might not scale up as well as you'd like. Vector assets will scale up well, but bitmap assets should be replaced with resolution specific ones.

Depending on your bitmap assets and how well they scale up, you can choose the right multi-resolution strategy for your app.

One option would be to create three copies of each media asset, one for each resolution:

Image myImage = new Image();
if (MultiRes.Is720p)
myImage.Source = new BitmapImage(new Uri("puppies.720p.jpg"));
else if (MultiRes.IsWvga)
myImage.Source = new BitmapImage(new Uri("puppies.wvga.jpg"));
else if (MultiRes.IsWxga)
myImage.Source = new BitmapImage(new Uri("puppies.wxga.jpg"));

Another option would be to use a different image for each aspect ratio:

if (MultiRes.Is720p)
myImage.Source = new BitmapImage(new Uri(@"assets\16by9AspectRatio\puppies.jpg"));
else
myImage.Source = new BitmapImage(new Uri(@"assets\15by9AspectRatio\puppies.jpg"));

Yet another strategy you could adopt would be to have SD and HD assets:

if (MultiRes.IsHighResolution)
myImage.Source = new BitmapImage(new Uri(@"assets\HD\puppies.jpg"));
else
myImage.Source = new BitmapImage(new Uri(@"assets\SD\puppies.jpg"));

If your app really doesn't work well on a particular resolution, you can opt the app out from specific resolutions when you add it to the store. For example, if you've used <Canvas /> to design your app it might not position perfectly on 720P, so you might want to exclude your app from that resolution. For that reason it's recommend that you always use <Grid /> relative positioning and not <Canvas /> absolute positioning to create your UI.

WmAppManifest editor allowing developers to opt out from resolutions


In-app purchase

With Windows Phone 8 your app can offer to sell virtual goods in the app using IAP (in-app purchase). Such goods can include “durables” such as a new game level, magazines, and movies or “consumable” goods such as game food and temporary access to content (e.g. renting a movie). You need to specify the in-app purchase catalogue in Developer Center during app submission, purchase something from that catalogue at runtime, and Microsoft will provide the commerce infrastructure to support completing the sale in 190 countries. Note that the IAP API doesn't provide a way to track inventory of durables or bill subscription so you'll need to work around that yourself.

In order to test IAP create a new beta app with a few IAP products and submit those products with your beta app. Note that IAP products in Developer Center beta apps will always be free, but they're useful for end-to-end testing of the IAP API. In this next print screen you can see how to enter the details for a new IAP durable product.

Entering the details for a new IAP durable

Here is an app with two IAP products: a consumable with an ID of “123Consumable” and a durable with an ID of “123Durable”.

two IAP products: a consumable with an ID of “123Consumable” and a durable with an ID of “123Durable”.

In order to gain access to these IAP products from this test app you'll need to change the WmAppManfiest.xml ProductID to correspond to the beta app.

<App xmlns="" ProductID="{331fbc13-d523-4117-a36c-2b0adc49e5af}" PublisherID="{144cccdf-12c2-4e7b-9527-87db1df42eeb}">

Now iterate over all IAP products associated with the current ProductID.

private async void IAP_IterateOverProducts(object sender, RoutedEventArgs e)
{
StringBuilder sb = new StringBuilder();
 
var listing = await CurrentApp.LoadListingInformationAsync();
foreach (var product in listing.ProductListings)
{
sb.AppendLine(string.Format("{0}, {1}, {2},{3}, {4}",
product.Key,
product.Value.Name,
product.Value.FormattedPrice,
product.Value.ProductType,
product.Value.Description));
}
 
MessageBox.Show(sb.ToString(), "List all products", MessageBoxButton.OK);
}

Running this code snippet you will see the following display of all of your IAP products including one consumable product and one durable product.

List of IAP products showing 1 consumable and 1 durable

For the next step, attempt to purchase a consumable item. In this sample you'll test a consumable as it's easier to test multiple times then a durable, which can only be purchased once. Once you have the receipt it can be displayed on the screen.

private async void IAP_BuyConsumables(object sender, RoutedEventArgs e)
{
var listing = await CurrentApp.LoadListingInformationAsync();
var firstConsumable =
listing.ProductListings.FirstOrDefault(p => p.Value.ProductType == ProductType.Consumable);
 
var recipet = await CurrentApp.RequestProductPurchaseAsync(firstConsumable.Value.ProductId, true);
 
if (CurrentApp.LicenseInformation.ProductLicenses[firstConsumable.Value.ProductId].IsActive)
{
// do something
CurrentApp.ReportProductFulfillment(firstConsumable.Value.ProductId);
}
 
MessageBox.Show(recipet, "Reciept", MessageBoxButton.OK);
}

The code snippet is actually pretty simple. First you get the correct product listing, then start a purchasing workflow, check if the product was purchased, do something if the product was purchased, and finally print a receipt. The “do something if the product was purchased” is where domain specific code should be.

Running this code snippet in the emulator you will see the following purchasing screen and receipt.

The really interesting part of the code is the “//do something” comment, which should be replaced by your own code. For consumables you should manage a persistent inventory of items. For durables you can settle for checking the CurrentApp.LicenseInformation.ProductLicenses[myProductId].IsActive. Either way once an item has been purchased you should confirm fulfilment and do something in your app.

3057 page views in the last 30 days.