I sometimes marvel at all the stuff that’s hanging off the humble laptop with which I’ve been writing this book. Besides the docking station that is currently also serving well as a monitor stand and rather efficient dust collector, there’s a mouse (wired), a keyboard (wireless), two large monitors, speakers, a USB thumb drive, a headset, an Xbox game controller, and the occasional external hard drive. Add to that a couple of printers and media receivers hanging off my home network and, well, I probably don’t come close to what the majority of my readers probably have around their home and workplace!
For all that might be going on within one computer itself, and for all the information it might be obtaining from online sources, the world of external devices is another great realm, especially those that apps can work with directly. Indeed, we’ve spent most of our time in this book talking about what’s going on in the app and its host system and about using networks and services to gather data. It’s time, then, that we take an introductory look at the other hardware we can draw on to make a great app experience.
We’ve already encountered a few of these areas that I’ll just mention again here:
• In Chapter 8, “State, Settings, Files, and Documents,” we learned about the Removable Storage capability (in the manifest) that enables an app to work with files on USB sticks and other pluggable media. When a device is connected, those folders become available through Windows.Storage.KnownFolders.removableDevices
, which is just a StorageFolder
object whose subfolders are each connected to a particular storage device. See the Removable storage sample.
• In Chapter 2, “Quickstart,” along with Chapter 10, “Media,” we took full advantage of the Windows.Media.Capture
API to effortlessly obtain audio, images, and video from an attached camera (see the CameraCaptureUI Sample). This included the ability to select a specific capture device through Scenario 2 of the Media capture using capture device sample, for which we used the API in Windows.Devices.Enumeration
.
• Also in Chapter 10 we looked at the Windows.Media.PlayTo
API to connect media to a PlayTo capable receiver, as demonstrated in the Media Play To sample.
Beyond these there is much more, far more than this book and chapter will allow—hardware truly is a world unto itself! But at least we’ll understand where some of the resources are and spend a little time on those areas that are likely to be interesting to apps themselves. These are:
• Using devices through an API not directly available to JavaScript, such as Win32. Apps can also enumerate additional devices with a certain class interface. (The ability to interact with those devices is limited, at present, for Windows Store apps.)
• Connecting with devices in the vicinity of the one your app is running on through means such as WiFi Direct, Bluetooth, and near-field communications (NFC). In the latter case, NFC can connect apps running on devices or be used to acquire information from an inexpensive RFID tag.
• Last but not least is printing, which is an easy feature to add to a Windows Store app.
With that, let me also mention a class of apps that we won’t be dealing with in this book: Windows Store Device Apps, as they’re called. These are the ones that can be automatically acquired from the Windows Store when their associated devices are plugged into a Windows machine. The variety in this space is quite amazing, as we see everything from the most prosaic headsets, monitors, printers, cameras, mice, and keyboards to the newest smart TVs, remotes, home audio systems, health sensors, scientific devices, toys, point of sale systems, musical instruments, and more. It’s the responsibility of device apps—the ones you always used to get on a CD that you then employed as a coaster—to light up the functionality of the device, and at present they are the only apps that can actually do so. Windows Store apps in general are not able to work with specialized devices unless there is some other public API that allows for it, as we’ll see in the first section below.
Writing device apps is well beyond the scope of this book, but if you’re interested you can refer to the Windows Store Device App Workshop (Channel 9 videos), along with Windows 8 Device Experience: Windows Store Device Apps, along with Windows Store device Apps for Specialized Connected Devices. There are also a few samples to draw on, such as the Windows 8 device app for camera sample and the Device apps for printers sample. A much more specific one is the Custom driver access sample, which works with a piece of hardware called FX2 in the OSR USB FX2 Learning Kit (from Open System Resources). This is a piece of hardware meant for people learning how to develop device drivers to use to understand the intricacies.
As mentioned earlier, Windows Store apps are not generally given access to specialized hardware and whatever interfaces exist in device drivers: this is the special privilege of device apps. However, if the device and its driver happen to plug into a system API of some kind, then there are ways for other apps to work with them, as illustrated in Figure 15-1.
The system APIs that are available to Windows Store apps are somewhat varied. As we’ve seen in previous chapters, WinRT itself enables access to cameras, PlayTo receivers, storage devices (including USB drives, cameras, and media players), and input devices, where in the latter case the hardware is hidden behind abstraction layers like pointers. WinRT also provides an abstraction through which an app can work with any number of printers, as we’ll see in “Printing Made Easy” later on.
FIGURE 15-1 Store apps in Windows 8 can work only with devices that have a representative system API; Windows Store device apps have the privilege of working directly with a specific device through its driver.
In some cases, as we’ll see in our first example of an Xbox controller, there are APIs in Win32/COM that are built on a certain device interface. In the case of the controller, the XInput API (part of DirectX) does exactly that. XInput isn’t directly available to an app written in JavaScript, but a WinRT component can perform that job on the app’s behalf. Such a component, as we’ll cover in more depth in Chapter 16, “WinRT Components,” extends the WinRT API for those apps that include it in their package. The APIs provided by such components look and feel like those in WinRT. They just start with some namespace other than Windows
!
Another capability that WinRT does provide for is enumerating devices with a particular device interface class (a GUID). This is useful for building and displaying a list of devices when more than one exists, and works for common devices (like cameras) and uncommon devices alike.
The other class of devices that apps can work with is called Windows Portable Devices, which includes removable storage as well as a host of Bluetooth gizmos. This is one place where being able to enumerate the devices is helpful, because there’s an ActiveX control, of all things—which is essentially a COM API—through which you can talk to such devices.
The XInput API, part of DirectX, is a Win32 API that specifically works with game controllers and is on the list of Win32/COM APIs that can be used from a Windows Store app. The most commonly used function within this group is probably XInputGetState
, which returns an XINPUT_STATE
structure that describes the position of the various thumb controllers, how far throttle or other triggers are depressed, and the on/off states of all the buttons. It’s basically meant to be polled with every animation frame in something like a game; the API doesn’t itself raise events when the controller state changes.
The XInput and JavaScript controller sketch sample in the Windows SDK demonstrates exactly this. Because the XInput API is not accessible directly through JavaScript, it’s necessary to create a WinRT component for this purpose. Put simply, you create a component with public classes inside a namespace that matches the component’s filename, and then you add a reference to that component to the JavaScript app’s project in Visual Studio. This imports the namespace and makes it available within JavaScript. We’ll see more details on this in Chapter 16, but basically the component’s C++ code looks like the following—first in the header (Controller.h) file within the GameController project:
namespace GameController { public value struct State { // [Omitted--just contains the same values as the Win32 XINPUT_STATE structure }; public ref class Controller sealed { ~Controller(); uint32 m_index; bool m_isControllerConnected; // Do we have a controller connected XINPUT_CAPABILITIES m_xinputCaps; // Capabilites of the controller XINPUT_STATE m_xinputState; // The current state of the controller uint64 m_lastEnumTime; // Last time a new controller connection // was checked public: Controller(uint32 index); void SetState(uint16 leftSpeed, uint16 rightSpeed); State GetState(); }; }
The implementation of GetState
in Controller.cpp then just calls XInputGetState
and copies its properties to an instance of the component’s public State
structure:
State Controller::GetState() { // defaults to return controllerState that indicates controller is not connected State controllerState; controllerState.connected = false; // An app should avoid calling XInput functions every frame if there are // no known devices connected as initial device enumeration can slow down // app performance. uint64 currentTime = ::GetTickCount64(); if (!m_isControllerConnected && currentTime - m_lastEnumTime < EnumerateTimeout) { return controllerState; } m_lastEnumTime = currentTime; auto stateResult = XInputGetState(m_index, &m_xinputState); if (stateResult == ERROR_SUCCESS) { m_isControllerConnected = true; controllerState.connected = true; controllerState.controllerId = m_index; controllerState.packetNumber = m_xinputState.dwPacketNumber; controllerState.LeftTrigger = m_xinputState.Gamepad.bLeftTrigger; controllerState.RightTrigger = m_xinputState.Gamepad.bRightTrigger; // And so on [copying all the other properties omitted.] } else { m_isControllerConnected = false; } return controllerState; }
The constructor for a Controller object is also very simple:
Controller::Controller(uint32 index) { m_index = index; m_lastEnumTime = ::GetTickCount64() - EnumerateTimeout; }
In a JavaScript app—once the reference to the component has been added—the GameController
namespace contains the component’s public API, and we can utilize it as if it were built right into Windows. In the case of the sample, it first instantiates a Controller object (with index of zero) and then kicks off animation frames (program.js):
app.onactivated = function (eventObj) { if (eventObj.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) { // [Other setup omitted] // Instantiate the Controller object from the WinRT component controller = new GameController.Controller(0); // Start rendering loop requestAnimationFrame(renderLoop); }; };
The renderLoop
function then just calls the component’s getState
method and applies the results to a canvas drawing before repeating the loop (also in program.js, though much code omitted):
function renderLoop() { var state = controller.getState(); if (state.connected) { controllerPresent.style.visibility = "hidden"; // Code added to the sample to extend its functionality if (state.leftTrigger) { context.clearRect(0, 0, sketchSurface.width, sketchSurface.height); requestAnimationFrame(renderLoop); return; } if (state.a) { context.strokeStyle = "green"; } else if (state.b) { context.strokeStyle = "red"; } else if (state.x) { context.strokeStyle = "blue"; } else if (state.y) { context.strokeStyle = "orange"; } // Process state and draw the canvas [code omitted] }; // Repeat with the next frame requestAnimationFrame(renderLoop); };
The output of this sample is shown in Figure 15-2, reflecting the features I added to the sample within the code above to make it more interesting to my young son: changing colors with the A/B/X/Y buttons and clearing the canvas with the left trigger. As you can see, my own artwork with this app isn’t a whole lot different from his!
In the end, even though WinRT doesn’t surface APIs like XInput, an app can do this for itself with a simple component implementation. Note that various aspects of the component’s interface, like the casing of method names, will change when it’s projected into JavaScript. Again, we’ll see more details in Chapter 16. For now, it shows that getting access to such specialized devices is a straightforward task.
FIGURE 15-2 The XInput and JavaScript controller sketch sample with some modifications to change colors. The varying line width is controlled by the position of the right trigger.
If you happen to know the device class interface GUID for a certain group of devices, you can use the static Windows.Devices.Enumeration.DeviceInformation.findAllAsync
method to retrieve detailed information about those devices. Many details can be found in the Enumerating Devices topic in the documentation, but let me give a quick overview.
What you give to findAllAsync
is something called a selector, specifically an Advanced Query Syntax (AQS) string, as we encountered in Chapter 8 in the section “Rich Enumeration with File Queries.” A device selector typically looks something like this:
System.Devices.InterfaceClassGuid:="{E5323777-F976-4F5B-9B55-B94699C46E44}" AND System.Devices.InterfaceEnabled:=System.StructuredQueryType.Boolean#True
where the interface class GUID shown here is the particular one for webcams.
The result of findAllAsync
is a DeviceInformationCollection
, which in JavaScript can basically be treated as an array of DeviceInformation
objects. In Scenario 1 of the as Device enumeration sample we see how to use this array to display details for each device:
Windows.Devices.Enumeration.DeviceInformation.findAllAsync(selector, null).done( function(devinfoCollection) { var numDevices = devinfoCollection.length; for (var i = 0; i < numDevices; i++) { displayDeviceInterface(devinfoCollection[i], id("scenario1Output"), i); } });
Some results from this are shown in Figure 15-3 and Figure 15-4.
FIGURE 15-3 Sample device enumeration output for a webcam—which perfectly represents the one attached to my monitor.
FIGURE 15-4 Sample device enumeration output for a printer—which also looks exactly like the one sitting next to my desk.
Scenario 2 of the sample executes the same process (with plain text output) for Plug and Play (PnP) object types using Windows.Devices.Enumeration.Pnp.PnpObject.findAllAsync
. This API lets you enumerate devices by interface, interface class, and container (the visible and localized aspects of a piece of hardware, like manufacturer and model name):
Windows.Devices.Enumeration.Pnp.PnpObject.findAllAsync(deviceContainerType, propertiesToRetrieve).done(function (containerCollection) { var numContainers = containerCollection.length; for (var i = 0; i < numContainers; i++) { displayDeviceContainer(containerCollection[i], id("scenario2Output")); } });
In the call above, the propertiesToRetrieve
variable contains an array of strings that identify the Windows properties you’re interested in. The sample uses these:
var propertiesToRetrieve = ["System.ItemNameDisplay", "System.Devices.ModelName", "System.Devices.Connected"];
The result of the enumeration—the containerCollection
variable in the code above—is a PnpObjectCollection
that contains PnpObject
instances. The sample just takes the information from these and displays a text output for each.
Note that there is a variant of findAllAsync
that accepts an AQS string as a filter. This is a string that you obtain from APIs like Windows.Devices.Portable.StorageDevice.getDeviceSelector
that makes enumeration of those particular devices easier.
In addition to enumerating printers and webcams (because they have standard interface class GUIDs), Scenario 1 of the Device enumeration sample also works with portable devices, as does the Portable device service sample. This opens the doors to the subject of Windows Portable Devices or WPD, a driver technology that supports things like phones, digital cameras, portable media players, and so on but also a growing array of Bluetooth devices, where the need is primarily to transfer data between the device and the system. WPD supplies an infrastructure for this.
In WinRT, Windows.Devices.Portable
API provides direct interaction with WPD. Here you’ll find the ServiceDevice
and StorageDevice
classes. Both of which simply provide methods that return selector strings and id
properties. In the former case, such information is meaningful only to the device app associated with the hardware. In the latter case, however, the StorageDevice.fromId
method provides a Windows.Storage.StorageFolder
through which you can enumerate its contents. This is demonstrated in Scenario 3 of the Removable storage sample that we noted in Chapter 8, where it will create a list of removable storage devices to choose from and then display the first image found on the one you select:
The Removable Storage sample, in Scenario 4, also demonstrates how to use AutoPlay to automatically launch the app when you plug in a suitable device. This involves declarations in the manifest for AutoPlay Content (inserting a storage medium) and/or AutoPlay Device (inserting a device). See Auto-launching with AutoPlay for details.
As for Bluetooth devices, the Windows.Media.Devices.CallControl
API gives you the ability to work with a telephony-related device. See How to manage calls on the default Bluetooth communi-cations device for more along with the Bluetooth call control sample.
Another group of Bluetooth devices includes those that collect information about one’s physical health, such as heart rate, blood pressure, and temperature. See Bluetooth low energy on Wikipedia to learn more; working with these is demonstrated in the Bluetooth low energy health profiles sample as we’ll briefly see below, but I’m told that access to these is presently limited to device apps.
Another specific sample is the Bluetooth simple key service sample, which works with the CC2540 Mini Development Kit controller. This little gizmo is something Texas Instruments created to assist development around their CC2540 chip; in the case of the sample, the buttons on the device control buttons in the app.
What’s interesting in these latter two samples is how the app connects with the particular device using an ActiveX control called PortableDeviceAutomation.Factory (one of the few such ActiveX objects available). For example, to connect with a thermometer we first enumerate devices with its particular class GUID, {00001809-0000-1000-8000-00805f9b34fb} (see js/thermometer.js in the Bluetooth low energy health profiles sample):
Windows.Devices.Enumeration.DeviceInformation.findAllAsync( "System.Devices.InterfaceClassGuid:=\"{00001809-0000-1000-8000-00805f9b34fb}\"", null)
With the results from this async operation in a variable called devices
, here’s how it gets to a specific device and sets up a listener for its particular events:
// Use WPD Automation to initialize the device objects var deviceFactory = new ActiveXObject("PortableDeviceAutomation.Factory"); // For the purpose of this sample we will initialize the first device deviceFactory.getDeviceFromIdAsync(devices[0].id, function (device) { // The 'device' variable will have the device object. // Initialize the temperature service and listen for measurements tempService = device.services[0]; tempService.onTemperatureMeasurement = function (timestamp, thermometerMeasurementValue) { // ... }; });
Connecting with devices that are near to the one on which your app is running is one area that I suspect will see much creative innovation in the coming years as PCs are increasing equipped with the requisite hardware. In this case we’re really speaking of “devices” more generally than we have been. In some cases there will be a separate discrete device, most notably Bluetooth devices or RFID tags. But then we’re also speaking of an app running on one machine connecting with itself or another that’s running on a different machine. In this sense, apps can communicate with each other as if they were themselves separate “devices.”
Caveat Though it is possible for different apps to know about each other and communicate, the Store certification requirements do not allow them to be interdependent. Approach such communication scenarios as a way to extend the functionality of the app, but be sure to provide value when the app is run in isolation.
Near Field Communication (NFC) is one of the key ways for apps to connect to devices and across devices. NFC works with electromagnetic sensors (including unpowered RFID tags) that resonate with each other when they get close, within 3–4 centimeters. Practically speaking, this means that the devices actually make physical contact, a tap that effectively initiates a digital handshake that opens the conversation. When this happens between the same app running on both devices, a process known as pairing, those apps can have an ongoing conversation.
When you think about “devices” in this context, though, they can vary tremendously. That is, the devices that are making the connection don’t need to be at all similar. One device might be your tablet PC, for example, and the other might by anything from a large all-in-one PC display to a simple RFID tag mounted in a poster or a name badge.
Apps can also learn about each other on different devices through WiFi Direct (if the wireless adapter supports it) and Bluetooth. In these cases it’s possible for one app to browse for available (advertised) connections on other devices, which might or might not be coming from the same app.
Whatever the case, working with proximity—as all of this is collectively referred to—is useful for many scenarios such as sharing content, setting up multiplayer experience, broadcasting activity, and really anything else where some kind of exchange might happen, including both one-time data transfers and setting up more persistent connections.
There are three main conditions for using proximity (see Guidelines for developing using proximity):
• The app must declare the Proximity capability in its manifest.
• Communications are supported only for foreground apps (there are no background tasks to maintain a conversation).
• The app must ask for user consent to enter into a multiuser relationship. An app should show waiting connections, connections being established, and connections that are active, and it should allow the user to disconnect at any time. Note that using the APIs to make a connection will automatically prompt the user.
The API for working with proximity is in the appropriately named Windows.Networking.-Proximity
namespace, as is the Proximity sample that we’ll be working with here. It almost goes without saying that doing any deep exploration of proximity will require two machines that are suitably equipped (unless you’re just working with RFID tags). For NFC between two machines there is also a driver sample in the Windows Driver Kit that simulates NFC over a network connection. To use it, note that you’ll also need Visual Studio 2012 Ultimate Edition; the Express version does not support driver development. It might make more sense to just acquire some NFC-capable tablets!
Anyway, to install the Windows Driver Kit, follow the instructions on How to get the WDK after installing Visual Studio 2012 Ultimate. When you start the download, don’t be put off by the indicated 1GB size—start the installer (it’s a 937 K download), and then select the option to acquire the kit for use on another computer (which is actually a 307 MB download). Then, according to the Proximity sample’s description page:
After you have installed the WDK and samples, you can find the proximity driver sample in the src\nfp directory in the location where you installed the WDK samples. See the NetNfpProvider.html file in the src\nfp\net directory for instructions on building and running the simulator. [Note: be sure to specify a Windows 8 target when you build.] After you start the simulator, it runs in the background while your proximity app is running in the foreground. Your app must be in the foreground for the tap simulation to work.
Assuming, then, that you have an environment in which proximity can at least be simulated, let’s look at the two mainline scenarios in the following sections. The first is using the PeerFinder
class to create a socket connection between two peer apps for ongoing communication. The second uses the ProximityDevice
class to send data packets between two devices.
To find peers, an app on one machine can advertise its availability such that apps on other devices can browse advertised peers and initiate a connection over WiFi Direct or Bluetooth. The second way to find a peer is through a direct NFC tap. Both methods are shown in Scenario 1 of the Proximity sample, but these three distinct functions—advertise, browse, and tap to connect—are somewhat intermixed, because they each use some distinct parts of the Windows.Networking.Proximity.PeerFinder
class and some parts in common.
One commonality is the static property PeerFinder.supportedDiscoveryTypes
, which indicates how connections can be made. This contains a combination of values from the PeerDiscoveryTypes
enumeration and depends on the available hardware in the device. Those values are browse
(WiFi Direct is available), triggered
(NFC tapping is available), and none
(PeerFinder
can’t be used). You can use these values to selectively enable or disable certain capabilities in your app as needed. Scenario 1 of the Proximity sample, for instance, checks the discovery types to set some flags and enable buttons for NFC activities. Otherwise, it just shows disappointing messages (this code is condensed somewhat from js/PeerFinder.js, and note the namespace variable at the top):
var ProxNS = Windows.Networking.Proximity; var supportedDiscoveryTypes = ProxNS.PeerFinder.supportedDiscoveryTypes; // Enable triggered (tap) related UI only if the hardware support is present if (supportedDiscoveryTypes & ProxNS.PeerDiscoveryTypes.triggered) { triggeredConnectSupported = true; } else { peerFinderErrorMsg = "Tap based discovery of peers not supported \n"; } // Enable browse related buttons only if the hardware support is present if (supportedDiscoveryTypes & ProxNS.PeerDiscoveryTypes.browse) { browseConnectSupported = true; // [Add listeners to buttons, code omitted] } else { // [Show messages, code omitted] } if (triggeredConnectSupported || browseConnectSupported) { // [Set up additional UI] } // ... }
Now let’s tease apart the distinct areas.
Making yourself available to others through advertising has two parts: putting out the word and listening for connections that are made.
Assuming that some form of communication is possible, the first step in all of this is to configure the PeerFinder
with the displayName
that will appear to other devices when you advertise and to set allowBluetooth
, allowInfrastructure
, and allowWiFiDirect
as desired to allow discovery over additional networks (infrastructure refers to TCP/IP). Setting none of these flags will still enable connections through NFC tapping, which is always enabled.
Next, set up a handler for the PeerFinder.onconnectionrequested
event, followed by a call to the static method PeerFinder.start
(again, ProxNS
is a namespace variable):
ProxNS.PeerFinder.onconnectionrequested = connectionRequestedEventHandler; ProxNS.PeerFinder.start();
Note connectionrequested
is an event that originates within WinRT. Because this is perhaps an event you might only listen to temporarily, be sure to call removeEventListener
or assign null
to the event property to prevent memory leaks. See the “WinRT Events and removeEventListener” section in Chapter 3, “App Anatomy and Page Navigation.”
The connectionrequested
event is triggered when other devices pick up your advertisement and call your toll-free hotline, so to speak, specifically over WiFi Direct or Bluetooth. The event receives a ConnectionRequestedEventArgs
object that contains a single property, peerInformation
, which is an instance of—not surprisingly—the PeerInformation
class. This object too is simple, containing nothing but a displayName
, but that is enough to make a connection.
function connectionRequestedEventHandler(e) { requestingPeer = e.peerInformation; ProximityHelpers.displayStatus("Connection Requested from peer: " + requestingPeer.displayName); // Enable Accept button (and hide Send and Message) [some code omitted] ProximityHelpers.id("peerFinder_AcceptRequest").style.display = "inline"; }
A connection is established by passing that PeerInformation
object to PeerFinder.-connectAsync
. This will prompt the user for consent, and given that consent, your completed handler will receive a Windows.Networking.Sockets.StreamSocket
, which we’ve already encountered in Chapter 14, “Networking.”
function peerFinder_AcceptRequest() { ProxNS.PeerFinder.connectAsync(requestingPeer).done(function (proximitySocket) { startSendReceive(proximitySocket); }); }
From this point on, you’re free to send whatever data with whatever protocols you’d like, on the assumption, of course, that the app on the other end will understand what you’re sending. This is clearly not a problem when it’s the same app on both ends of the connection; different apps, of course, will need to share a common protocol. In the sample, the “protocol” exchanges only some basic values, but the process is all there.
If at any time you want to stop advertising, call PeerFinder.stop
. To close a specific connection, call the socket’s close
method.
On the other side of a proximity relationship, an app can look for peers that are advertising themselves over WiFi Direct or Bluetooth. In the Proximity sample, a Browse Peers button is enabled if the browse
discovery type is available. This button triggers a call to the following function (js/PeerFinder.js) that uses PeerFinder.findAllPeersAsync
to populate a list of possible connections, including those from different apps:
function peerFinder_BrowsePeers() { // Empty the current option list [code omitted] ProxNS.PeerFinder.findAllPeersAsync().done(function (peerInfoCollection) { // Add newly found peers into the drop down list. for (i = 0; i < peerInfoCollection.size; i++) { var peerInformation = peerInfoCollection[i]; // Create and append option element using peerInformation.displayName // to the peerFinder_FoundPeersList control [code omitted] } }); }
When you select a peer to connect to, the sample takes its PeerInformation
object and calls PeerFinder.connectAsync
as before (during which the user is prompted for consent):
function peerFinder_Connect() { var foundPeersList = ProximityHelpers.id("peerFinder_FoundPeersList"); var peerToConnect = discoveredPeers[foundPeersList.selectedIndex]; ProxNS.PeerFinder.connectAsync(peerToConnect).done( function (proximitySocket) { startSendReceive(proximitySocket); }); }
Once again, this provides a StreamSocket
as a result, which you can use as you will. To terminate the connection, call the socket’s close
method.
To detect a direct NFC tap—which again works to connect apps running on two devices—listen to the PeerFinder.ontriggeredConnectionStateChanged
(a WinRT event that I spell out in camel casing so that it’s readable!). In response, start the PeerFinder
:
ProxNS.PeerFinder.ontriggeredconnectionstatechanged = triggeredConnectionStateChangedEventHandler; ProxNS.PeerFinder.start();
The process of connecting through tapping will go through a series of state changes (including user consent), where those states are described in the TriggeredConnectState
enumeration: listening
, connecting
, peerFound
, completed
, canceled
, and failed
. Each state is included in the event args sent to the event (a TriggeredConnectionStateChangedEventArgs
…some of these names sure get long!), and when that state reaches completed
, the socket
property in the event args will contain the StreamSocket
for the connection:
function triggeredConnectionStateChangedEventHandler(e) { // [Other cases omitted] if (e.state === ProxNS.TriggeredConnectState.completed) { startSendReceive(e.socket); } }
Again, from this point on, it’s a matter of what data is being exchanged through the socket—the NFC tap is just a means to create the connection. And once again, call the socket’s close
when you’re done with it.
When tapping connects the same app across devices, it’s possible to have the tap launch an app on one of those devices. That is, when the app is running on one of the devices and has started the PeerFinder
, Windows will know the app’s identity and can look for it on the other device. If it finds that app, it will launch it (or activate it if it’s already running). The app’s activated
handler is then called with an activation kind of launch
, where eventArgs.detail.arguments
will contain the string “Windows.Networking.Proximity.PeerFinder:StreamSocket” (see js/default.js):
var tapLaunch = ((eventObject.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) && (eventObject.detail.arguments === "Windows.Networking.Proximity.PeerFinder:StreamSocket")); if (tapLaunch) { url = scenarios[0].url; // Force scenario 0 if launched by tap to start the PeerFinder. } return WinJS.Navigation.navigate(url, tapLaunch);
The code in Scenario 1 picks up this condition (the tapLaunch
parameter to WinJS.-Navigation.Navigate
is true
) and calls PeerFinder.start
automatically instead of waiting for a button press. In the process of startup, the app also registers its own triggeredConnection-StateChanged
handler so that it will also receive a socket when the connection is complete.
Although the PeerFinder
sets up a StreamSocket
and is good for scenarios involving ongoing communication, other scenarios—like sharing a photo, a link, or really any kind of information including data from an RFID tag—need only send some data from one device to another and be done with it. For such purposes we have the Windows.Networking.Proximity.ProximityDevice
class, which you obtain as follows:
var proximityDevice = Windows.Networking.Proximity.ProximityDevice.getDefault();
An app that has something to share “publishes” that something as a message in the form of a string, a URI, or a binary buffer. RFID tags publish their messages passively; an app, on the other hand, uses the ProximityDevice
class and its publishMessage
, publishUriMessage
, and publish-BinaryMessage
methods (and a matching stopPublishing
method). For example, drawing from Scenario 2 of the Proximity sample (js/ProximityDevice.js, there publishText
contains the contents of an edit control):
var publishedMessageId = proximityDevice.publishMessage("Windows.SampleMessageType", publishText);
On the other side, an app that would like to receive such a message calls ProximityDevice.-subscribeForMessage
, passing the name of the message it expects along with a handler for when messages arrive:
var subscribedMessageId = proximityDevice.subscribeForMessage("Windows.SampleMessageType", messageReceived); function messageReceived(receivingDevice, message) { // Process the message }
If the app is no longer interested in messages, it calls stopSubscribingForMessage
.
With this simple protocol, you can see that an app that supports “tap to share” (as it’s called) would probably publish messages whenever it has appropriate content in hand. It can also use the ProximityDevice
object’s devicearrived
and devicedeparted
events to know when other tap-to-share peers are in proximity such that it’s appropriate to publish (these are WinRT events). The devicearrived
event is also what you use to discover that an RFID tag has come into proximity (see below).
What’s interesting to think about, though, is what kind of data you might share. Consider a travel app in which you can book flights, hotels, rental cars, and perhaps much more. It can, of course, publish messages with basic details but could also publish richer binary messages that would allow it to transfer an entire itinerary to the same app running on another device, typically to another user. This would enable one person to set up such an itinerary and then share it with a second person, who could then just tap a Book It button and be done! This would be far more efficient than emailing that itinerary as text and having the second person re-enter everything by hand.
On a simpler note, publishing a URI makes it super-simple for one person to tap-and-share whatever they’re looking at with another person, again avoiding the circuitous email route or other forms of sharing. A quick tap, and you’re seeing what I’m seeing. Again, though, there’s so much more than can be shared that it’s a great thing to consider in your design, especially if you’re targeting mobile devices. “What do people near each other typically do together?” That’s the question to ask—and to answer in the form of a great proximity app.
Do note that the URIs you share don’t have to be http://
references to websites but can contain any URI scheme. If there’s an app associated with that URI scheme, tap-to-share also becomes tap-to-activate, because Windows will launch the default app for that association. And if there’s no association, Windows will ask if you want to acquire a suitable app from the Store. You can also consider using a Windows Store URI that will lead a user to directly install an app. Those URIs are described on Creating links with the Windows Store protocol.
Such URIs make it possible for RFID tags, whose messages are basically hardcoded into the device, to support tap-to-share and tap-to-activate scenarios. When you tap an RFID tag to an NFC-capable device, the ProximityDevice
object will fire a devicearrived
event. An app can then receive the tag’s message through ProximityDevice.subscribeForMessage
. This means that the app will need to know what type of message might be sent from that tag—it might be a standard type, or the app might be written specifically for tags with specific programming. For example, an art gallery could place tags near every piece it displays and then make an app available in the Windows Store for that gallery (or any other galleries that work in cooperation) that knows what messages those tags will send. If the message has an appropriate URI scheme in it, tapping on an RFID tag can help the user acquire an app and enjoy a rich experience.
For more on this topic, look for an NFC-related post on the Windows Developer Blog. (One was in the publishing queue at the time of writing but not yet available.)
An embarrassingly long time ago, when I was first working in the computer industry, I remember hearing excited talk about the “paperless office” and how very soon now we wouldn’t need things like printers because everything would be shuttled around digitally.
Decades later, we do find ourselves shuttling around plenty of digital content, and yet printing still seems alive and well (except for this present book, of course, where early on we decided on an ebook format so that we could use extensive hyperlinks and color!). Maybe we still like paper for how it feels, how it uses our eyes differently, how it’s cheap and disposable (unlike your Windows 8 tablet), how it can be used to start fires in a pinch or make airplanes, and how it makes good use of all the small trees that get thinned out of commercial tree farms (at least here in the western United States). Maybe too it’s just part of the human experience—after all, as much as we play with our computers, we do still live in a physical world with physical objects, so it makes total sense that we continue to appreciate placing information onto physical media.
Sometimes I wonder whether the idea of the paperless office wasn’t fueled in part by the fact that many apps didn’t implement printing very well, an artifact of it being a difficult task to begin with. (And then there were printer drivers of dubious quality, connection difficulties, and many other challenges.) But gradually the whole world of printing has improved, both for consumers and for developers.
Of course, printing isn’t always about going to paper either. I frequently use a PDF “printer” to create read-only copies of documents that are more suitable to sharing in many cases than my originals. Occasionally I print to a fax machine (which sends a fax), and more occasionally I’ll print an email or web page directly to Microsoft OneNote for filing. In fact, I highly recommend setting your default printer to a digital target of some kind when working on printing features in your own app. That way you’ll avoid producing copious amounts of scratch paper in the process, unless you happen to own a tree farm that you’ll be thinning in a couple of years!
Get the backstory If you want to know more about how printing as a whole has been reimagined, check out Simplifying printing in Windows 8 on the Building Windows 8 Blog, a post that provides deep soul satisfaction knowing that there are fewer drivers in your printing future.
To understand how to implement printing in an app, let’s first see what it looks like to the user. Then we’ll see how to ready content for printing and how to handle the printing-related events from Windows.
Note A Windows Store app written in JavaScript can use the window.print
method to print with default settings. It’s not recommended, however, because it doesn’t work with the print UI and doesn’t always produce the best output. Windows Store apps should give the user the full Windows 8 experience as described here.
Printing typically starts in an app where the user is looking at something they want to print and invokes an appropriate command. In Scenario 2 of the Print sample, for instance, whose code we’ll be looking at in the next section, we see a big block of content along with a Print button, as shown in Figure 15-5. Note that such a Print button would normally be on the app bar and not on the app canvas, but this is a sample.
To start printing, the user can either tap this Print button or open the Charms bar and select Devices. Either way, if the app is registered for printing—that is, it’s listening for the event that’s raised from the Devices charm and provides suitable content—the user will see a list of print targets, as shown on the left side of Figure 15-6. If the app doesn’t have printable content (that is, it doesn’t listen for the event or provides no content in response), the user will see a panel like that on the right side of Figure 15-6. This is very much the same experience that a user sees with the Share charm depending on whether the app provides data for that feature. You’ve likely seen the epic fail message of “This app can’t share.” Printing supplies a similar disappointment for apps that lack the capability. Don’t let your app be one of them.
FIGURE 15-5 Scenario 2 of the Print sample shows a typical app with something ready to print.
FIGURE 15-6 The Devices charm when an app has available print content (left) and when it doesn’t (right).
From this point on, the system is really just taking whatever content the app provides and displays UI based on the capabilities of the printer driver, as shown in Figure 15-7. From the app’s point of view, it thankfully gets all of this for free! The app can also indicate additional options to customize the UI, such as paper size and duplex printing, as shown in Figure 15-8, which comes from Scenario 3 of the sample.
FIGURE 15-7 Print preview and printer options are shown once the user selects a printer. The More Settings link on the left is what opens the options pane on the right.
FIGURE 15-8 The Print pane reflecting customization options indicated by the app.
No matter where the user might want to print content, the important thing is to make that content ready for printing. The key function you need to know about here is not found in WinRT but in the MSApp
object: MSApp.getHtmlPrintDocumentSource
. I like the way the documentation put it: “This method is used as the bridge between HTML and [Windows 8 app] style printing. In other words, this is how an app dev says ‘give me some stuff to print’.” What you give it is an HTML document that contains your content.
I emphasize the word document here because what you pass to getHtmlPrintDocumentSource
cannot be any arbitrary element in the DOM. It must be the same kind of thing that the document
variable always points to, or else you’ll see a run-time exception with “no such interface supported.”
So where do you get such an object?
If what your app is showing on the screen is exactly what you want to print, you can just use the document
object directly. This is what Scenarios 1–3 of the Print sample do:
MSApp.getHtmlPrintDocumentSource(document);
Of course, you don’t necessarily want to print everything on the screen; you can see that what’s on the screen in Figure 15-4 and what appears in the print preview of Figure 15-6 and Figure 15-7 is different. This is where the print
media query in CSS comes into play:
@media print { /* Print-only styles */ }
Simply said, if there’s anything you don’t want to show up in the printed output, set the display: none
style within this media query. An alternate strategy, one that the sample employs, is to create a separate CSS file, such as css/print.css, and link it in your HTML file with the media
attribute set to print (see html/scenario1.html):
<link rel="stylesheet" type="text/css" href="/css/print.css" media="print" />
Print styles need not be limited to visibility of content: you can also use it however you like to arrange that content for more printer-friendly output. In a way, printing is like another view state where you’re not adding to or changing the content; you’re simply changing the visibility and layout. There are also some events you can use to do more specific formatting before and after printing has happened, as we’ll see later.
But what if the content you want to print isn’t your document
object at all? How do you create another? There are several options here:
• In the document.body.onbeforeprint
event handler, append additional child elements to the document and use the document.body.onafterprint
event to remove them (the structure of such handlers is shown in Scenario 2 of the Print sample). If your print CSS leaves only those newly added elements visible, that’s all that gets printed. This very effectively controls the entire print output, such as adding additional headers and footers that aren’t visible in the app. You might have a place in the app, in fact, where the user can configure those headers and footers.
• Call document.createDocumentFragment
to obtain a document fragment and then populate it with whatever elements you want to print. getHtmlPrintDocumentSource
accepts such a fragment.
• If you have an iframe
whose src
is set to an SVG document (one of the tips we discussed for SVG’s in Chapter 10), obtain that SVG document directly through the iframe
element’s contentDocument
property. This too can be passed directly to getHtmlPrintDocument-Source
and will print just that SVG, for example:
<!-- in HTML --> <iframe id="diagram" src="/images/diagram.svg"></iframe> //In JavaScript var frame = document.getElementById("diagram"); args.setSource(MSApp.getHtmlPrintDocumentSource(frame.contentDocument));
• If you want to print the contents of an altogether different HTML page, create a link
element in the document head
that points to that other page for print media (see below). This will redirect getHtmlPrintDocumentSource
to process that page’s content instead.
The latter is demonstrated in Scenario 4 of the Print sample, where a link
element is added to the document with the following code (js/scenario4.js):
var alternateLink = document.createElement("link"); alternateLink.setAttribute("id", "alternateContent"); alternateLink.setAttribute("rel", "alternate"); alternateLink.setAttribute("href", "http://go.microsoft.com/fwlink/?LinkId=240076"); alternateLink.setAttribute("media", "print"); document.getElementsByTagName("head")[0].appendChild(alternateLink);
Here the rel
attribute indicates that this is alternate content, the media
attribute indicates that it’s only for print, and href
points to the alternate content (id
is optional). Note that if the target page has any print-specific media queries, those are certainly applied when creating the print source.
Now that we know how to get a source for print content, it’s very straightforward to provide that content to Windows for printing.
First, obtain the Windows.Graphics.Printing.PrintManager
object as follows:
var printManager = Windows.Graphics.Printing.PrintManager.getForCurrentView();
and then listen for its printtaskrequested
event (a WinRT event), either through addEvent-Listener
or by assigning a handler as done in the sample:
printManager.onprinttaskrequested = onPrintTaskRequested;
If you don’t add a handler for this event, the user will see the message on the right side of Figure 15-5 when invoking the Devices charm unless you’ve also registered for other device-related events such as Windows.Media.PlayTo.PlayToManager.sourceRequested
, as we saw at the end of Chapter 10.
If you want to directly invoke printing from an app command, such as the Print button in Scenario 2 of the sample, call the PrintManager.showPrintUIAsync
method. This is equivalent to the user invoking the Devices charm when the app has registered for the printtaskrequested
event.
The printtaskrequested
event is fired when the Devices charm is invoked. In response, your handler creates a PrintTask
object with a callback function that will provide the content document when needed. Here’s how that works. First, your handler receives a PrintTaskRequest
object that has just three members:
• deadline
The date and time that indicates how long you have to fulfill the request.
• getDeferral
Returns a PrintTaskRequestedDeferral
object in case you need to perform any async operations to fulfill the request. As with all deferrals, you call its complete
method when the async operation has finished.
• createPrintTask
Creates a PrintTask
with a given title and a function that provides the source document when requested.
The structure of createPrintTask
is slightly tricky. While it returns a PrintTask
object through which you can set options and listen to task-related events, as we’ll see shortly, its source
property is read-only. So, instead of creating a task and storing your content document in this property, you instead provide a callback function that does the job when requested. The function itself is simple: it just receives a PrintTaskSourceRequestedArgs
object whose setSource
method you call with what you get back from MSApp.getHtmlDocumentPrintSource
.
This is typically where you can also do other work to configure the task, so let’s take an example from Scenario 3 of the Print sample (where I’ve added a namespace variable for brevity):
function onPrintTaskRequested(printEvent) { var printTask = printEvent.request.createPrintTask("Print Sample", function (args) { args.setSource(MSApp.getHtmlPrintDocumentSource(document)); // Choose the printer options to be shown. The order in which the options are // appended determines the order in which they appear in the UI var options = Windows.Graphics.Printing.StandardPrintTaskOptions; printTask.options.displayedOptions.clear(); printTask.options.displayedOptions.append(options.copies); printTask.options.displayedOptions.append(options.mediaSize); printTask.options.displayedOptions.append(options.orientation); printTask.options.displayedOptions.append(options.duplex); // Preset the default value of the printer option printTask.options.mediaSize = Windows.Graphics.Printing.PrintMediaSize.northAmericaLegal; // Register the handler for print task completion event printTask.oncompleted = onPrintTaskCompleted; }); }
Note that PrintTaskSourceRequestedArgs
also contains a getDeferral
method, should you need it, along with a deadline
.
Tip If you step through the code in your printtaskrequested
handler but you pass the deadline, the print UI will time out and say there’s nothing available to print. This might not be an error in the app at all—take off the breakpoints and run again to check.
You can exercise some control over the appearance of the print UI through PrintTask.options
, in which context you should review Guidelines for print-capable Windows Store apps. The options
object here, of type PrintTaskOptions
, has a number of properties. A few obvious numerical ones are maxCopies
, minCopies
, and numberOfCopies
. You can also call getPageDescription
with a page number to obtain a PrintPageDescription
with resolution information for that page.
Then there is a host of properties whose values come from various printing enumerations:
PrintTaskOptions.displayedOptions
, for its part, is a vector of strings that must come from the StandardPrintOptions
class, as shown in the code above. Each of these controls the visibility of the option in the print UI if, of course, the printer supports it (otherwise the option will not be shown). The full list of options is binding
, collation
, colorMode
, copies
, duplex
, holePunch
, inputBin
, mediaSize
, mediaType
, nUp
, orientation
, printQuality
, and staple
.
Take special note of the mediaSize
property, for which there are literally 172 different values in the PrintMediaSize
enumeration that reflect all the sizes of paper, envelopes, and so forth that we find around the world. When you intend to market a print-capable Windows Store app in different regions, you might want to include mediaSize
in displayedOptions
and set its value to something that’s applicable to the region (as the code above is doing for legal size paper). Even so, the media size is typically available in the More Settings panel in the print UI, depending on what the printer in question supports, so users will have access to it.
The final bit to mention in the code above is that a PrintTask
has a completed
event, along with previewing
, progressing
, and submitting
. You can use these to reflect the status of print tasks in your app should you choose to do so. More information about the task itself is also available through its properties
, which will typically contain the title you gave to the print job along with a unique ID. In all of this, however, you might have noticed a conspicuous absence of any method in PrintTask
that would cancel a print job—in fact, there is none. This is because the HTML print model, as presently used by Windows Store apps written in JavaScript, is an all-or-nothing affair: once the job gets into the print engine, there’s no programmatic means to stop it. The user can still go to the printer control panel on the desktop and cancel the job there, or revert to the old-school method of yanking out the paper tray, but at present an app isn’t able to provide such management functions itself.
• Although Windows Store apps in Windows 8 cannot access arbitrary hardware, they do have access to a fair number of devices through both the WinRT API and Win32 APIs like Xinput and those supporting Windows Portable Devices (WPD). In cases of Win32 APIs, a WinRT component provides a bridge to apps written in JavaScript.
• The Windows.Devices.Enumeration
API allows an app to discover what hardware is installed on a machine. If an app can access specific types of devices, such as Bluetooth devices, it can use the enumeration to present a list from which the user can select a device to use.
• The Windows.Networking.Proximity
API supports peer browsing (over WiFi Direct and Bluetooth) as well as tap-to-connect and tap-to-share scenarios with near field communication (NFC)–capable machines.
• Proximity connections can employ sockets for ongoing communication (like a multiplayer game) or can simply send messages from one device to another through a publish-and-subscribe mechanism, as is typical with tap-to-share scenarios, including RFID tags.
• Printing, having been reimagined for Windows 8 as a whole, is relatively easy to implement in a Windows Store app. It involves listening for the printing event when the Devices charm is invoked and providing HTML content to Windows.
• Printable content can come from the app’s document, a document fragment, an SVG document, or a remote source. Such content can be customized using a CSS media query for print, and Windows takes care of the layout and flow of the information on the target printer.