Setting Permissions

The Android security and privacy model requires permissions to access certain features such as GPS. When clicking the Install button, the user sees the list of permissions and can then make an educated decision to opt out if desired.

Use the permission tag in the application descriptor to list all the permissions needed, as shown in the code below. Permissions cannot be modified at runtime.

Just before installing an application, the Android Market displays the permissions required. Figure 4-1 shows that Queue Manager, an application for managing your Netflix queue, only requires the Internet permission.

<uses-permission android:name=”android.permission.INTERNET” />
To make network requests. Can be used for remote debugging
<uses-permission android:name=”android.permission.WRITE_EXTERNAL_STORAGE” />
To write data to external storage, such as the SDCard
<uses-permission android:name=”android.permission.READ_PHONE_STATE” />
Read-only access of phone state. Used to mute AIR in case of incoming call
<uses-permission android:name=”android.permission.ACCESS_FINE_LOCATION” />
<uses-permission android:name=”android.permission.ACCESS_COARSE_LOCATION” />
Access the location information. Fine for GPS location, Coarse for Cell/Wi-Fi
<uses-permission android:name=”android.permission.CAMERA” />
Access the device camera
<uses-permission android:name=”android.permission.RECORD_AUDIO” />
Access the device microphone
<uses-permission android:name=”android.permission.DISABLE_KEYGUARD” />
<uses-permission android:name=”android.permission.WAKE_LOCK” />
Prevents device from dimming, going in sleep mode and activating keyguard
<uses-permission android:name=”android.permission.ACCESS_NETWORK_STATE” />
Access information on device network interfaces
<uses-permission android:name=”android.permission.ACCESS_WIFI_STATE” />
Access information on device Wi-Fi networks

Android offers a lot of other settings, only some of which can be set in AIR but not documented. They should all be added to the Android→Manifest Additions→Manifest node.

Queue Manager application

The Android Launcher activity keeps track of the applications that have been launched.  If a long press is applied on the home button, a list of recently run applications appears. If you do not want your application to be on this list, add excludeFromRecents to your manifest file as follows:

<android>
<manifestAdditions>
<![CDATA[
<manifest>
<application android:enabled=”true”>
<activity android:excludeFromRecents=”false”>
<intent-filter>
<action android:name=”android.intent.action.MAIN”/>
<category android:name=”android.intent.category.LAUNCHER”/>
</intent-filter>

</activity>
</application>
</manifest>
]]>
</manifestAdditions>
</android>

Applications are installed on the device memory by default. If you select Settings→ Applications→Manage Applications, you will notice that some applications have the option “Move to SD card” (which then becomes “Move to phone”).

However, some Android applications and AIR applications do not have that option. If you type the following at the command line, all applications will now be moved:

adb shell pm setInstallLocation 2

To restore the settings, type the following:

adb shell pm setInstallLocation 0

If you set the installLocation tag to preferExternal, saving internally is not an option. All of your content, including your application, is stored on the SD card. This could be helpful for large applications. Keep in mind that the content is accessible to other applications, and therefore is vulnerable. If the SD card is full, the system falls back to an installation on the device:

<android>
<manifestAdditions>
<manifest>
<attribute name=”android:installLocation” value=”preferExternal”/>
</manifest>
</manifestAdditions>
</android>

Read the Android recommendation regarding what should not be installed externally, online at http://developer.android.com/guide/appendix/install-location.html.

Users can erase the application data. If you want to prevent them from doing this, you can add the allowClearUserData attribute to the android node:

<manifest>
<application android:allowClearUserData=”false” />
</manifest>

You can also fine-tune permissions to add specific requirements. For instance, you may have an application which only works for devices with a camera auto-focus. You can add a different type of permission, uses-feature, to inform users of what is needed for your application:

<uses-permission android:name=”android.permission.CAMERA” />
<uses-feature android:name=”android.hardware.camera” />
<uses-feature android:name=”android.hardware.camera.autofocus” />

Android throws an error if a required permission is not set, such as for Internet or GPS, but not others, such as reading the phone state or disabling the keyguard. The criterion is what it considered potentially dangerous for the user. In AIR, the application always fails silently.

Native developers have a larger choice of features, and therefore permissions. You can go to http://developer.android.com/reference/android/Manifest.permission.html to learn about some of the new possibilities. If you see one that you would like to use, send Adobe a feature request, preferably with a use case so that it can be added to the AIR runtime, by going to https://www.adobe.com/cfusion/mmform/index.cfm?name=wishform .

For example, having an API to access contacts would open a lot of possibilities:

<uses-permission android:name=”android.permission.READ_CONTACTS” />

 

Choosing the Application Settings

The application descriptor contains application settings, and should look similar to the following code. Note the required or optional annotation, and default, for each setting.

Here are the required tags:

<application xmlns=http://ns.adobe.com/air/application/2.6>
// Last segment specifies the version of AIR required
<id>com.veronique.MyApplication</id>
// Universally unique identifier.
// Android converts ID to a package name by adding
// “air.” to the beginning.
// Hyphens are converted to underscore
// and digits are preceded by a capital A.
<filename>MyApplication</filename>
// name used for the APK package file
<versionNumber>1.0.0</versionNumber>
// contains three integers, up to three digits each
<initialWindow>
// properties of initial appearance of the application
<content>Main.swf</content>
// root swf
<fullScreen>false</fullScreen>
// application takes over the whole screen if true
<aspectRatio>portrait</aspectRatio>
// portrait (default) or landscape
<autoOrients>false</autoOrients>
// true (default) or false
<visible>true</visible>
<renderMode>cpu</renderMode>
// cpu (default) or gpu
</initialWindow>
</application>

The following tags are optional. They are child nodes of the application tag:

<name>Hello World</name>
// or localized
<name>
<text xml:lang=”en”>English name</text>
<text xml:lang=”fr”>nom français</text>
</name>
// name as it appears on device. Keep it short
<versionLabel>1.0.0</ versionLabel >
// Displayed in installation dialog
<description>
<text xml:lang=”en”>English description</text>
<text xml:lang=”fr”>Description française</text>
</description>
<copyright></copyright>
<icon>
<image36x36>icons/36.png</image36x36>
<image48x48>icons/48.png</image48x48>
<image72x72>icons/72.png</image72x72>
</icon>
<customUpdateUI>false</customUpdateUI>
// Update handled by user on double-click or automatic
<allowBrowserInvocation>false</allowBrowserInvocation>
// can be launched from link in a browser
<android>
<manifestAdditions>
<![CDATA[
<manifest>
<uses-permission android:name=”android.permission.CAMERA”/>
<uses-permission android:name=”android.permission.INTERNET”/>
// Add all permissions as children of manifest tag
<supports-screens android:normalScreens=”true”/>
<uses-feature android:required=”true”
android:name=”android.hardware.touchscreen.multitouch”/>
<application android:enabled=”true”>
<activity android:excludeFromRecents=”false”>
<intent-filter>
<action
android:name=”android.intent.action.MAIN”/>
<category
android:name=
“android.intent.category.LAUNCHER”/>
</intent-filter>
</activity>
</application>
</manifest>
]]>

</manifestAdditions>
</android>

 

Why Mobile?, The APK File

The mobile marketplace is a cruel world. Users often download an application on impulse, try it once, and may never open it again if their first impression is negative. You should aim for excellence and simplicity. Define your application in one sentence. Find your niche. Make it unique, if not in concept, then in execution.

Also, you should analyze mobile consumption. An application is often only used for a few minutes at a time. Tasks should be completed quickly or in incremental steps saved on the device. Whatever you do, do not attempt to replicate your existing website. Instead, adapt it for mobile or create a companion application.

The APK File

The default system icon is an Android-green icon. Icons need to be 36×36-, 48×48-, or 72×72-pixel PNG images. They are used for low-, medium-, and high-density screens, respectively.

You should make your icon stand out, while clearly identifying your application. Google provides guidelines on how to make icons, and provides a Photoshop template, at http://developer.android.com/guide/practices/ui_guidelines/icon_design.html.

In Flash Professional, go to File→AIR Android Settings→Icon and browse to the icons on your computer.

In Flash Builder, create a directory called icons in the src folder, and place your icons in it. Right-click on your project and select Properties. Then select ActionScript Build Packaging→Google Android→Package Contents and select the icons folder if it is not already selected.

In other tools, add a node to your application descriptor file as follows:

<icon>
<image36x36>icons/36.png</image36x36>
<image48x48>icons/48.png</image48x48>
<image72x72>icons/72.png</image72x72>
</icon>

Alternatively, if you are using the command line, place all your icons in a folder called icons and add it to the end of your statement:

AIR-sdk-path/bin/adt -package -target apk -storetype pkcs12
-keystore yourCertificate.p12 hello.apk Main-app.xml Main.swf icons

Publish your application one last time. Change your application to release mode if it is in debug mode. In Flash Professional, select File→AIR Android settings. Under the Deployment tab, for “Android deployment type,” choose Release.

If you are publishing from the command line, verify that the target is apk:

AIR-sdk-path/bin/adt -package -target apk -storetype pkcs12
-keystore yourCertificate.p12 hello.apk Main-app.xml Main.swf

 

Accessing Services

The MVVM pattern identifies three major components: the view, the view model, and the model. This chapter describes the UI of the Tailspin Surveys mobile client application and the way that Tailspin uses the MVVM pattern. The Tailspin mobile client application also includes a number of services. These services can be invoked from the view, view model, or model components and include services that do the following:

  • Manage the settings and surveys stores that handle data persistence for the application.
  • Save and load the application’s state when it’s activated and deactivated.
  • Synchronize survey data between the client application and the Tailspin Surveys web application.
  • Notify the application when new surveys are available.
  • Encode audio data, and support application navigation and other infrastructure services.

User Interface Notifications

The Tailspin Surveys mobile client application performs some tasks asynchronously; one example is the potentially time-consuming synchronization with the Tailspin Surveys web service. Asynchronous tasks must often inform the user of the outcome of the task or provide status information while they’re running. It’s important to consider the usability of the application when you’re deciding on the appropriate way to notify users of significant events or to provide status information. The developers at Tailspin were concerned they would either flood the user with alerts or have the user miss an important piece of information.

For the Tailspin Surveys mobile client application, the developers identified two categories of notification,  informational/warning notifications and error notifications.

Informational/Warning Notifications

Informational or warning notifications should not be disruptive, so the user should see the message but not be interrupted in their current task. The user does not need to perform any action in response to this type of message; the Tailspin mobile client application uses this type of notification to inform the user when a synchronization completes successfully, for example. Tailspin uses a custom toast notification for these messages because the application does not have access to the Windows Phone 7 toast notification system.

Error Notifications

Error notifications should be disruptive because the message is informing the user that some expected action of the application will not happen. The notification should inform the user of the actions they need to take to resolve the problem; for example, to retry the synchronization operation again if it fails for some reason. Tailspin uses message boxes for this type of message.

Inside the Implementation

This example shows how Tailspin implements custom toast notifications and error notifications on the SurveyListView page to inform users when the synchronization process finishes or fails. In the sample application, many of the notifications and error messages are not intended for the imaginary user of the sample; instead, they are there to
help you, the developer understand what the application is doing as you explore its functionality. You should follow the guidance published in the UI Design and Interaction Guide for Windows Phone 7 (http://go.microsoft.com/fwlink/?LinkID=183218) when you design the user notification system for your application.

The following code example shows the relevant declarations in the SurveyListView.xaml file.

XAML
<prismInteractionRequest:MessageBoxRequestTrigger RequestBinding=
“{Binding SubmitErrorInteractionRequest}”/>
<prismInteractionRequest:ToastRequestTrigger
RequestBinding=”{Binding SubmitNotificationInteractionRequest}”
PopupElementName=”SynchronizationToast”/>

<Popup x:Name=”SynchronizationToast”>
<Grid Background=”{StaticResource PhoneAccentBrush}”
VerticalAlignment=”Bottom” Width=”480″>
<TextBlock Text=”{Binding Title}”
HorizontalAlignment=”Stretch” Foreground=”Black”
TextWrapping=”Wrap” Margin=”14,5,14,5″>
<Custom:Interaction.Behaviors>
<pag:PopupHideOnLeftMouseUp/>
</Custom:Interaction.Behaviors>
</TextBlock>
</Grid>
</Popup>

The view model uses the SubmitNotificationInteractionRequest binding to trigger the toast notification and the SubmitError InteractionRequest binding to trigger the error message notification. The following code example shows how the SurveyListViewModel displays a toast notification when the synchronization process completes successfully and an error message when it fails.

C#
private readonly InteractionRequest
<Microsoft.Practices.Prism.Interactivity.InteractionRequest
.Notification> submitErrorInteractionRequest;
private readonly InteractionRequest
<Microsoft.Practices.Prism.Interactivity.InteractionRequest
.Notification> submitNotificationInteractionRequest;

public IInteractionRequest SubmitErrorInteractionRequest
{ get { return this.submitErrorInteractionRequest; } }
public IInteractionRequest SubmitNotificationInteractionRequest
{ get { return this.submitNotificationInteractionRequest; } }

private void SyncCompleted(
IEnumerable<TaskCompletedSummary> taskSummaries)
{

if (taskSummaries.Any(
t => t.Result != TaskSummaryResult.Success))
{
this.submitErrorInteractionRequest.Raise(
new Microsoft.Practices.Prism.Interactivity
.InteractionRequest.Notification
{ Title = “Synchronization error”, Content =
stringBuilder.ToString() },
n => { });
}
else
{
this.submitNotificationInteractionRequest.Raise(
new Microsoft.Practices.Prism.Interactivity
.InteractionRequest.Notification
{ Title = stringBuilder.ToString(), Content = null },
n => { });
}

}

 

Note: This solution uses the InteractionRequest and Notification classes and two custom behaviors, Message BoxRequestTrigger and ToastRequestTrigger, from the Prism Library.

 

Handling Navigation Requests

In addition to invoking commands from the view, the Tailspin mobile client also triggers navigation requests from the view. These requests could be to navigate to a particular view or navigate back to the previous view. In some scenarios, for example if the application needs to navigate to a new view when a command completes, this requires the view model to send a message to the view. In other scenarios, you might want to trigger the navigation request directly from the view
without involving the view model directly. When you’re using the MVVM pattern, you want to be able to do all this without using any code-behind in the view, and without introducing any dependency on the view implementation in the view model classes.

Inside the Implementation

The following code example from the FilterSettingsView.xaml file shows two examples of initiating navigation in the sample application.

XAML
<i:Interaction.Behaviors>
<prismInteractivity:ApplicationBarButtonCommand
ButtonText=”Save” CommandBinding=”{Binding SaveCommand}”/>
<prismInteractivity:ApplicationBarButtonNavigation
ButtonText=”Cancel” NavigateTo=”#GoBack” />

</i:Interaction.Behaviors>

The first example invokes the SaveCommand command in the view model. The code that implements this command causes the application to navigate back to the previous view if the command succeeds, so in this example, the navigation is initiated from the view model. The following code example from the FilterSettingsView Model class illustrates this.

C#
public DelegateCommand SaveCommand { get; set; }

public FilterSettingsViewModel(…)
{

this.SaveCommand =
new DelegateCommand(this.Submit, () => !this.CanSubmit);

}

public void Submit()
{

this.NavigationService.GoBack();

}

public bool CanSubmit
{
get { return this.canSubmit; }
set
{
if (!value.Equals(this.canSubmit))
{
this.canSubmit = value;
this.RaisePropertyChanged(() => this.CanSubmit);
}
}
}

The second example shows how the view can initiate the navigation directly by navigating back to the previous view (in this case, the SurveyListView page) without executing a command.

Both examples use the ApplicationFrameNavigationService class shown in the following code example to perform the navigation.

C#
public class ApplicationFrameNavigationService :
INavigationService
{
private readonly PhoneApplicationFrame frame;

public ApplicationFrameNavigationService(
PhoneApplicationFrame frame)
{
this.frame = frame;
}
public bool Navigate(Uri source)
{
return this.frame.Navigate(source);
}
public void GoBack()
{
this.frame.GoBack();
}
public bool CanGoBack
{
get { return this.frame.CanGoBack; }
}
public Uri CurrentSource
{
get { return this.frame.CurrentSource; }
}
}

This class, which implements Tailspin’s INavigationService interface, uses the phone’s PhoneApplicationFrame instance to perform the navigation request for the application.

A view model can invoke the Navigate method on the Application FrameNavigationService object to cause the application to navigate to a particular view in the application or the GoBack method to return to the previous view.

The ViewModel base class maintains the INavigationService instance for all the view models, and the Funq dependency injection container is responsible for initially creating the Application FrameNavigationService object that implements this interface.

To avoid any code-behind in the view when the view initiates the navigation, the developers at Tailspin use an interaction behavior from the Prism Library. The following code example shows how the Cancel button is declared in the FilterSettingsView.xaml file.

XAML
<phone:PhoneApplicationPage.ApplicationBar>
<shell:ApplicationBar …>

<shell:ApplicationBarIconButton Text=”Cancel”
IconUri=”…”/>
</shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>
<i:Interaction.Behaviors>

<prismInteractivity:ApplicationBarButtonNavigation
ButtonText=”Cancel” NavigateTo=”#GoBack” />

</i:Interaction.Behaviors>

The ApplicationBarButtonNavigation behavior object contains a reference to the view model, and the view model contains the reference to the ApplicationFrameNavigationService instance that handles the navigation request. The ApplicationBarButtonNavigation behavior object checks for the special value “#GoBack”, in which case, it uses the navigation service to navigate back to the previous view; otherwise, it uses the value of the NavigateTo attribute to identify the view to navigate to.

Commands

In a Silverlight application, you can invoke some action in response to a user action (such as a button click) by creating an event handler in the code-behind class. However, in the MVVM pattern, the responsibility for implementing the action lies with the view model, and you should avoid placing code in the code-behind classes. Therefore, you should connect the control to a method in the view model using a binding.

The version of Silverlight on the Windows Phone 7 platform does not currently support binding to a command that implements the ICommand interface like you can do in Windows Presentation Foundation (WPF). Therefore, Tailspin uses a set of binding behaviors, including the ApplicationBarButtonNavigation, ApplicationBar ButtonCommand, and ButtonCommand behaviors, from the Prism Library to locate commands from the view.

For more information about the ICommand interface in WPF, see “Commanding Overview” on MSDN (http://msdn.microsoft.com/en-us/library/ms752308.aspx).

Inside the Implementation

The developers at Tailspin use bindings to associate actions in the UI with commands in the view model. In addition to Silverlight controls on the Windows Phone 7 platform not supporting binding to ICommand instances, you cannot use the InvokeCommandAction Expression Blend behavior with the ApplicationBarIconButton or Application BarMenuItems controls because they cannot have dependency properties. Tailspin uses a custom behavior to hook up commands to the view model.

The following code example from the SurveyListView page shows how the Synchronize button and the Settings button on the application bar are associated with commands.

XAML
<phoneNavigation:PhoneApplicationPage.ApplicationBar>
<shell:ApplicationBar IsVisible=”True”>
<shell:ApplicationBarIconButton Text=”Sync” IconUri=”…” />
<shell:ApplicationBarIconButton Text=”Settings”
IconUri=”…” />
<shell:ApplicationBarIconButton Text=”Filters” IconUri=”…”/>
</shell:ApplicationBar>
</phoneNavigation:PhoneApplicationPage.ApplicationBar>

<Custom:Interaction.Behaviors>
<prismInteractivity:ApplicationBarButtonCommand
ButtonText=”Sync”
CommandBinding=”{Binding StartSyncCommand}”/>
<prismInteractivity:ApplicationBarButtonCommand
ButtonText=”Settings”
CommandBinding=”{Binding AppSettingsCommand}” />
<prismInteractivity:ApplicationBarButtonCommand
ButtonText=”Filters”
CommandBinding=”{Binding FiltersCommand}” />

</Custom:Interaction.Behaviors>

The attributes attached to the ApplicationBarIconButton controls (Text and IconUri) only affect their appearance. The Application BarButtonCommand elements handle the connection to the commands; they identify the control to associate with the command through the ButtonText attribute and the command binding through the CommandBinding attribute.

The ApplicationBarButtonCommand class from the Prism Library implements the custom behavior and sets up the binding that links a button click in the UI to the StartSyncCommand, AppSettings Command, and FiltersCommand properties in the SurveyListView Model class.

The StartSyncCommand property uses an instance of the Delegate Command class that implements the ICommand interface. The following code example from the SurveyListViewModel class shows the definition of the StartSyncCommand property.

C#
public DelegateCommand StartSyncCommand { get; set; }

public SurveyListViewModel(…) : base(…)
{

this.StartSyncCommand = new DelegateCommand(
() => { this.StartSync(); },
() => !this.IsSynchronizing &&
!this.SettingAreNotConfigured);

}

 

Note: For more information about the DelegateCommand class from Prism, and to learn more about Prism, see the Prism CodePlex site (http://compositewpf.codeplex.com/).

The following code example from the SurveyListViewModel class shows the implementation of the StartSync method and Sync Completed method. These methods run the synchronization process asynchronously by invoking the StartSynchronization method; they also control the progress bar in the UI by setting the IsSynchronizing property. This method uses the ObserveOnDispatcher method from the Reactive Extensions (Rx) for .NET to run the synchronization
process.

C#
public void StartSync()
{
if (this.IsSynchronizing)
{
return;
}
this.IsSynchronizing = true;
this.synchronizationService
.StartSynchronization()
.ObserveOnDispatcher()
.Subscribe(
taskSummaries =>
{
this.SyncCompleted(taskSummaries);
this.IsSynchronizing = false;

});
}
private void SyncCompleted(
IEnumerable<TaskCompletedSummary> taskSummaries)
{

this.BuildPivotDimensions();
this.RaisePropertyChanged(string.Empty);
this.UpdateCommandsForSync();
}

The SyncCompleted method also updates the UI to show the new list of surveys following the synchronization process. The Update CommandsForSync method disables the Synchronize button in the UI while the synchronization is running.

Displaying Data

The application displays data by binding elements of the view to properties in the view model. For example, the Pivot control on the SurveyListView page binds to the SelectedPivotIndex property in the SurveyListViewModel class.

The view can automatically update the values it displays in response to changes in the underlying view model if the view model implements the INotifyPropertyChanged interface. In the Tailspin mobile client, the abstract ViewModel class inherits from the NotificationObject class in the Prism Library. The Notification Object class implements the INotifyPropertyChanged interface. All the view model classes in the Tailspin mobile client application inherit from the abstract ViewModel class. The application also uses the ObservableCollection class from the System.Collections.Object Model name-space that also implements the INotifyProperty Changed interface.

Note: To learn more about Prism, see the Prism CodePlex site (http://compositewpf.codeplex.com/) and “Prism (Composite Client Application Guidance)” on the MSDN® developer program website (http://msdn.microsoft.com/en-us/library/ff648465.aspx).

Inside the Implementation

The following sections describe examples of data binding in the application. The first section describes a simple scenario on the AppSettingsView page, the next sections describe more complex examples using Pivot and Panorama controls, and the last section describes how Tailspin addressed an issue with focus events on the phone.

Data Binding on the Settings Screen

The AppSettingsView page illustrates a simple scenario for binding a view to a view model. On this screen, the controls on the screen must display property values from the AppSettingsViewModel class, and set the property values in the view model when the user edits the settings values.

The following code example shows the DataContext attribute and some of the control definitions in the AppSettingsView.xaml file. Tailspin chose to use the ToggleSwitch control in place of a standard CheckBox control because it better conveys the idea of switching something on and off instead of selecting something. The Toggle Switch control is part of the Microsoft Silverlight Windows Phone Toolkit available on the Silverlight Toolkit CodePlex site (http://silverlight.codeplex.com).

XAML
<phone:PhoneApplicationPage
x:Class=”TailSpin.PhoneClient.Views.AppSettingsView”

DataContext=”{Binding Source={StaticResource ViewModelLocator},
Path=AppSettingsViewModel}”
…>

<TextBox Height=”Auto” HorizontalAlignment=”Stretch”
Margin=”0,28,0,0″ Name=”textBoxUsername”
VerticalAlignment=”Top” Width=”Auto”
Text=”{Binding UserName, Mode=TwoWay}” Padding=”0″
BorderThickness=”3″>
<i:Interaction.Behaviors>
<prism:UpdateTextBindingOnPropertyChanged/>
</i:Interaction.Behaviors>
</TextBox>

<PasswordBox Height=”Auto” HorizontalAlignment=”Stretch”
Margin=”0,124,0,0″ Name=”passwordBoxPassword”
VerticalAlignment=”Top” Width=”Auto”
Password=”{Binding Password, Mode=TwoWay}”>
<i:Interaction.Behaviors>
<prism:UpdatePasswordBindingOnPropertyChanged/>
</i:Interaction.Behaviors>
</PasswordBox>

<toolkit:ToggleSwitch Header=”Subscribe to Push Notifications”
Margin=”0,202,0,0″
IsChecked=”{Binding SubscribeToPushNotifications, Mode=TwoWay}” />

<ProgressBar Height=”4″
HorizontalAlignment=”Stretch”

Name=”progressBar”
VerticalAlignment=”Bottom”
IsIndeterminate=”{Binding IsSyncing}”/>

The view model class must implement the INotifyProperty Changed interface for automatic updates in the view to work. In the Tailspin client application, this interface is implemented by the View Model class that all the view models inherit from. A view model notifies a view of a changed property value by invoking one of the RaisePropertyChanged methods. The following code example shows how the AppSettingsViewModel view model class notifies the view that it should display the in-progress indicator to the user.

C#
public bool IsSyncing
{
get { return this.isSyncing; }
set
{
this.isSyncing = value;
this.RaisePropertyChanged(() => this.IsSyncing);
}
}

The code for the AppSettingsView page illustrates a solution to a common issue in Silverlight for the Windows Phone 7 platform. By default, the view does not notify the view model of property value changes until the control loses focus. For example, new content in the textBoxUserName control is lost if the user tries to save the settings before moving to another control. The UpdateTextBindingOn PropertyChanged behavior from the Prism Library ensures that the view always notifies the view model of any changes in the TextBox control as soon as they happen. The  UpdatePasswordBindingOn PropertyChanged behavior does the same for the PasswordBox control.

Data Binding and the Pivot Control

The application uses the Pivot control to allow the user to view different filtered lists of surveys. Figure 5 shows the components in the mobile client application that relate to the Pivot control as it’s used on the SurveyListView page.

Using the Pivot control on the SurveyListView page

The following code example shows how the Pivot control on the SurveyListView page binds to the SelectedPivotIndex property of the SurveyListViewModel instance. This two-way binding determines which PivotItem control, and therefore which list of surveys, is displayed on the page. Remember, the ViewModelLocator class is responsible for locating the correct view model for a view.

XAML
<phoneControls:Pivot
Title=”TAILSPIN SURVEYS”
Name=”homePivot”
SelectedIndex=”{Binding SelectedPivotIndex, Mode=TwoWay}”
Visibility=”{Binding SettingAreConfi gured,
Converter={StaticResource VisibilityConverter}}”>

</phoneControls:PivotControl>

The following code example shows the defi nition of the Pivot Item control that holds a list of new surveys; it also shows how the ListBox control binds to the NewItems property in the view model.

XAML
<phoneControls:PivotItem Header=”new”>
<StackPanel Orientation=”Vertical”>
<ContentControl Template=”{StaticResource NoItemsTextBlock}”
Visibility=”{Binding NewItems.IsEmpty,
Converter={StaticResource VisibilityConverter}}” />
<ListBox ItemsSource=”{Binding NewItems}”
Style=”{StaticResource SurveyTemplateItemStyle}”
Visibility=”{Binding NewItems.IsEmpty,
Converter={StaticResource NegativeVisibilityConverter}}” >
</ListBox>
</StackPanel>
</phoneControls:PivotItem>

 

Note: In the list, the layout and formatting of each survey’s information is handled by the SurveyTemplateItemStyle style and the SurveyDateTemplate data template in the Styles.xaml file.

The SurveyListViewModel class uses CollectionViewSource objects to hold the list of surveys to display in the list on each Pivot Item control. This allows the view model to notice and to react to changes in the item selected in the view, without needing to know about the view itself. The following code example shows how the SurveyListViewModel class defines the properties that the ListBox controls bind to.

C#
private CollectionViewSource newSurveysViewSource;
private CollectionViewSource byLengthViewSource;
private CollectionViewSource favoritesViewSource;

public ICollectionView NewItems { get {
return this.newSurveysViewSource.View; } }
public ICollectionView FavoriteItems { get {
return this.favoritesViewSource.View; } }
public ICollectionView ByLengthItems { get {
return this.byLengthViewSource.View; } }

The following code example shows how the BuildPivot Dimensions method populates the CollectionViewSource objects.
In this example, to save space, only the code that populates the newSurveysViewSource property is shown.

C#
private void BuildPivotDimensions()
{
this.observableSurveyTemplates =
new ObservableCollection<SurveyTemplateViewModel>();
List<SurveyTemplateViewModel> surveyTemplateViewModels =
this.surveyStoreLocator.GetStore().GetSurveyTemplates()
.Select(t => new SurveyTemplateViewModel(t)
{
Completeness = this.surveyStoreLocator.GetStore()
.GetCurrentAnswerForTemplate(t) != null
? this.surveyStoreLocator.GetStore()
.GetCurrentAnswerForTemplate(t).CompletenessPercentage
: 0
}).ToList();
surveyTemplateViewModels.ForEach(
this.observableSurveyTemplates.Add);

this.newSurveysViewSource = new CollectionViewSource
{ Source = this.observableSurveyTemplates };

this.newSurveysViewSource.Filter +=
(o, e) => e.Accepted =
((SurveyTemplateViewModel)e.Item).Template.IsNew;

this.newSurveysViewSource.View.CurrentChanged +=
(o, e) => this.SelectedSurveyTemplate =
(SurveyTemplateViewModel)this.newSurveysViewSource
.View.CurrentItem;

// Initialize the selected survey template.
this.HandleCurrentSectionChanged();
}

This method first creates an ObservableCollection collection of SurveyTemplateViewModel objects. Each SurveyTemplateView Model object holds a complete definition of a survey and its questions. The method then assigns this collection to the Source property of each CollectionViewSource object. Next, the method applies a filter or a sort to each CollectionViewSource object so that it displays the correct set of surveys. The method then attaches an event handler to each CollectionViewSource object so that the Selected SurveyTemplate property of the SurveyListViewModel object is updated correctly. Finally, the method calls the HandleCurrent SectionChanged method that causes the view to update and display the currently selected PivotItem control with its list of surveys.

Data Binding and the Panorama Control

The application uses the Panorama control to display survey questions. This enables the user to scroll left and right through the questions as if the questions are all on a single large page of which the phone’s screen shows a small portion.

Using the Panorama Control on the TakeSurveyView page

As in the previous examples, the view uses the ViewModel Locator class to create the view model. In this case, the ViewModel Locator object must also tell the view model which survey to display. The following code example shows how the ViewModelLocator object instantiates a TakeSurveyViewModel object and sets its TemplateViewModel property to identify which survey to display, and it creates a new SurveyAnswer object to hold the survey question definitions and the answers that the user enters.

C#
public TakeSurveyViewModel TakeSurveyViewModel
{
get
{
var templateViewModel =
this.SurveyListViewModel.SelectedSurveyTemplate;
var vm = new TakeSurveyViewModel(this.containerLocator
.Container.Resolve<INavigationService>())
{
SurveyStoreLocator = this.containerLocator.Container
.Resolve<ISurveyStoreLocator>(),
LocationService = this.containerLocator.Container
.Resolve<ILocationService>(),
TemplateViewModel = templateViewModel,
SurveyAnswer = this.containerLocator.Container
.Resolve<ISurveyStoreLocator>().GetStore()
.GetCurrentAnswerForTemplate(templateViewModel.Template)
?? templateViewModel.Template.CreateSurveyAnswers(this
.containerLocator.Container.Resolve<ILocationService>())
};
vm.Initialize();
return vm;
}
}

The Panorama control binds to the Questions property of the TakeSurveyViewModel class and a  Panorama.ItemTemplate template controls the display of each question in the survey. However, it’s necessary to display different question types using different layouts. The following code example from the TakeSurveyView.xaml file shows
how the data binding and view layout is defined for the Panorama control using the DataTemplateSelector content selector class from the Prism Library.

XAML
<phoneControls:Panorama
SelectionChanged=”PanoramaSelectionChanged”
Loaded=”PanoramaLoaded”
VerticalAlignment=”Top”
Name=”panorama” Margin=”0,0,0,0″
ItemsSource=”{Binding Questions}”>
<phoneControls:Panorama.ItemTemplate>
<DataTemplate>
<ScrollViewer>
<prism:DataTemplateSelector Content=”{Binding}”
Grid.Row=”0″ VerticalAlignment=”Top”
HorizontalContentAlignment=”Left” IsTabStop=”False”>
<prism:DataTemplateSelector.Resources>
<DataTemplate x:Key=”OpenQuestionViewModel”>
<Views:OpenQuestionView DataContext=”{Binding}”/>
</DataTemplate>
<DataTemplate
x:Key=”MultipleChoiceQuestionViewModel”>
<Views:MultipleChoiceQuestionView
DataContext=”{Binding}”/>
</DataTemplate>

</prism:DataTemplateSelector.Resources>
</prism:DataTemplateSelector>
</ScrollViewer>
</DataTemplate>
</phoneControls:Panorama.ItemTemplate>
<phoneControls:Panorama.HeaderTemplate>
<DataTemplate>
<Canvas Width=”0″ Height=”0″/>
</DataTemplate>
</phoneControls:Panorama.HeaderTemplate>
</phoneControls:Panorama>

 

Note: It is necessary to create a HeaderTemplate data template even if you’re not using it with the Panorama control. You’ ll also notice that the XAML declares handlers for the Selection Changed and Loaded events in the code-behind. The code-behind also contains a workaround method to trim long titles that don’t always display correctly when the user scrolls in the Panorama control.

Each question view on the Panorama control binds to a view model object in the list of questions held in the Questions property of the TakeSurveyViewModel class. For example, an OpenQuestion View view binds to an  OpenQuestionViewModel object, and a VoiceQuestionView view binds to a VoiceQuestionViewModel object. The following code example shows how the TakeSurveyView Model class builds this list of question view models.

C#
public IList<QuestionViewModel> Questions { get; set; }

public void Initialize(ISurveyStoreLocator surveyStoreLocator)
{

this.Questions = this.SurveyAnswer.Answers.Select(
a => Maps[a.QuestionType].Invoke(a)).ToArray();
}

The following code sample shows the definition of the Maps property in the TakeSurveyViewModel. The Maps property maps question types to view models.

C#
private static readonly
Dictionary<QuestionType, Func<QuestionAnswer,
QuestionViewModel>> Maps =
new Dictionary<QuestionType, Func<QuestionAnswer,
QuestionViewModel>>()
{
{ QuestionType.SimpleText,
a => new OpenQuestionViewModel(a) },
{ QuestionType.MultipleChoice,
a => new MultipleChoiceQuestionViewModel(a) },

};

Handling Focus Events

The file OpenQuestionView.xaml defines the UI for entering survey results. Tailspin found that when the user clicked the Save button, the last answer wasn’t saved by the view model. This is because Application BarIconButton control is not a FrameworkElement control, so it cannot get the focus. As a result, the lost focus event on the text box wasn’t being raised; as a result, the bindings didn’t tell the view model about the new field contents.

To solve this problem, the developers at Tailspin used a behavior named UpdateTextBindingOnPropertyChanged from the Prism Library. This behavior ensures that the view notifies the view model whenever the user changes the text in the TextBox control, not just when the control loses focus. The following code example shows how this behavior is defined in OpenQuestionView.xaml.

C#

xmlns:prism=
“clr-namespace:Microsoft.Practices.Prism.Interactivity;
assembly=Microsoft.Practices.Prism.Interactivity”

<TextBox Text=”{Binding AnswerText, Mode=TwoWay}”>
<Interactivity:Interaction.Behaviors>
<prism:UpdateTextBindingOnPropertyChanged/>
</Interactivity:Interaction.Behaviors>
</TextBox>

 

Testing the Application

Tailspin uses the Silverlight unit test framework for Windows Phone 7 and Silverlight 3 that’s described in Appendix A, “Tools, Frameworks, and Processes.”

The Windows Phone 7 project named Tailspin.PhoneClient.Tests contains the unit tests for the Surveys mobile client application. To run the tests, first build and then deploy this project either to the Windows Phone 7 emulator or to a real device. On the Windows Phone 7 device, you can now launch an application named Tailspin. PhoneClient.Tests, and then select the unit tests you want to run.

The following code example shows a unit test method from the SurveyListViewModelFixture class that tests that the view model returns a list of all the surveys that are marked as favorites.

C#
[TestMethod]
public void FavoritesSectionShowsFavoritedItems()
{
var store = new SurveyStoreMock();
var surveyStoreLocator = new SurveyStoreLocatorMock(store);
store.Initialize();
var allSurveys = store.GetSurveyTemplates();
var vm = new SurveyListViewModel(surveyStoreLocator,
new SurveysSynchronizationServiceMock());
vm.Refresh();
var favoriteSurveys =
vm.FavoriteItems.Cast<SurveyTemplateViewModel>().ToList();
CollectionAssert.AreEquivalent(
allSurveys.Where(p => p.IsFavorite).ToArray(),
favoriteSurveys.Select(t => t.Template).ToArray());
}

This method first instantiates a mock survey store and store locator objects to use in the test. The method then instantiates the view model object from the Tailspin.PhoneClient project to test, passing in the mock store locator object. The method then executes the test on the view model instance and verifies that the favorite surveys in the view are the same as the ones in the underlying database.

Connecting the View and the View Model

Now is a good time to walk through the code that implements the MVVM pattern in the Windows Phone 7 application in more detail. As you go through this section, you may want to download the Microsoft Visual Studio® development system solution for the Windows Phone 7 Tailspin Surveys application; it is available on the Windows Phone 7 Developer Guide community site on CodePlex (http://go.microsoft.com/fwlink/?LinkId=205602).

There are several ways to connect the view model to the view, including direct relations and data template relations. The developers at Tailspin chose to use a view model locator because this approach means that the application has a single class that is responsible for hooking up views to view models. This still leaves developers free to choose to manually perform the hookup within the view model locator, or by using a dependency injection container.

Inside the Implementation

The mobile client application uses a view model locator to connect view models to views. The following code example from the App.xaml file shows how the view model locator class is identified and made available to the application. The application declares the TailSpin. PhoneClient.ViewModels ViewModelLocator class in the <Application. Resources> section of the App.xaml file.

XAML
<Application
x:Class=”TailSpin.PhoneClient.App”

xmlns:viewmodels=
“clr-namespace:TailSpin.PhoneClient.ViewModels”
… >
<!–Application Resources–>
<Application.Resources>

<viewmodels:ViewModelLocator x:Key=”ViewModelLocator”/>
</Application.Resources>

</Application>

The following code example from the SurveyListView.xaml file shows how a view can then reference the  ViewModelLocator class as a static resource in its data context bindings.

XAML
<phone:PhoneApplicationPage
x:Class=”
TailSpin.PhoneClient.Views.SurveyList.SurveyListView”

DataContext=
“{Binding Source={StaticResource ViewModelLocator},
Path=SurveyListViewModel}”

>

</phone:PhoneApplicationPage>

The Path attribute identifies the property of the ViewModel Locator instance that returns the view model associated with the current page.

The following code example shows the parts of the ViewModel Locator class that return the SurveyListViewModel instance.

C#
public class ViewModelLocator : IDisposable
{
private readonly ContainerLocator containerLocator;

public SurveyListViewModel SurveyListViewModel
{
get
{
return this.containerLocator.Container
.Resolve<SurveyListViewModel>();
}
}
}

Notice how the Tailspin phone client uses the Funq dependency injection container to instantiate the view model.

The following code example shows the parts of the ViewModel Locator class that return the TakeSurveyViewModel instance when the user selects a survey from the survey list. In this example, the view model is instantiated with details of the currently selected survey so that the application can display the survey questions on the Take SurveyView page.

C#
public TakeSurveyViewModel TakeSurveyViewModel
{
get
{
var templateViewModel =
this.SurveyListViewModel.SelectedSurveyTemplate;
var vm = new TakeSurveyViewModel(
this.containerLocator.Container
.Resolve<INavigationService>())
{
SurveyStoreLocator = this.containerLocator.Container
.Resolve<ISurveyStoreLocator>(),
LocationService = this.containerLocator.Container
.Resolve<ILocationService>(),
TemplateViewModel = templateViewModel,
SurveyAnswer = this.containerLocator.Container
.Resolve<ISurveyStoreLocator>().GetStore()
.GetCurrentAnswerForTemplate(templateViewModel.Template)
?? templateViewModel.Template.CreateSurveyAnswers(
this.containerLocator.Container
.Resolve<ILocationService>())
};
vm.Initialize();
return vm;
}
}

Note: The view locator is also responsible for instantiating the store and passing it to the view model, which in turn passes it on to the model.