Continued from #2: First build and run

It's been a couple weeks since my last post, but I haven't been idle during that time–my Cordova efforts have been channeled into an MSDN Magazine article on Cordova, and now that I have that draft done I can share more of the insights here, meaning that I can ramble on with more details than I can fit into an article! :)

First of all, I modified my markup to be a little more generic and flexible for the app. One change is to drop the map display from the app altogether in favor of a simple field for the location. I did this because the map element took up a lot of space that I didn't have on small phones, and also created more complexity for layout. In the end, the map really isn't important to the app, and by making it a text field the user can also edit the location information if they want.

Another change is to add an explicit Share button because platforms other than Windows don't have the swipe-in-from-right-edge gesture to get to it, and don't have a system-level sharing feature either. (That's something we'll deal with later on.) I could have made use a WinJS app bar for this, but I decided to keep it simple and just create a div with a button.

I also just made all the content areas <div> elements instead of using <header> and <section> and so forth. Here's the new markup:

<body>
    <div class="mainContainer">
        <div class="title mainChild">
            <h1 class="titlearea ">Here My Am!</h1>
        </div>
        <div id="locationSection" class="mainChild subsection">
            <h2 class="section-title">Here:</h2>
            <div id="locationControls">
                <input id="txtLocation" type="text" placeholder="tap to edit" />
                <button id="btnLocate" class="iconGlobe"></button>
            </div>
        </div>
        <div id="photoSection" class="mainChild subsection">
            <h2 id="photoHeading" class="section-title">My:</h2>
            <div id="photo">
                <img id="photoImg" src="#" draggable="false"
                    alt="Tap to capture image from camera" />
            </div>
        </div>
        <div id="shareSection" class="mainChild">
            <button id="btnShare" class="shareButton" disabled>Share</button> 
        </div>
    </div>
</body>

I should note that one of the complications that came up in migrating the Windows Here My Am! app is that the map I originally used was either displayed in an iframe with an ms-appx-web:// src or an x-ms-webview element, both of which are specific to Windows. I had to do this because the map area used remote code for Bing Maps. Now Cordova apps on all platform other than windows automatically run inside a webview, which means I could have just references the Bing Maps script directly, but that would (a) not work if the device is offline, and (b) wouldn't work on Windows because you can't load script into the default local context in which index.html runs. Upon further investigation, there really isn't a workaround for this at present: your root page in a Cordova app on Windows runs in the local context, but on other platforms runs in a web context. It's a difference that you just have to live with for now, and in my case I just dropped the map feature.

My next step is to style the markup it so it looks like the following:

HereMyAm_Cordova_3-1

The big trick here is to understand the impact of Cordova's nature on your HTML/CSS work. On Windows 8/8.1 and Windows Phone 8.1, a Cordova apps runs as a native JavaScript app; on all other platforms it creates a native (non-JS) wrapper around a webview. Whatever the case, the engine that renders your HTML, CSS, and JavaScript is related to the browser of the underlying platform. On Windows it's IE (10 or 11, depending on the OS); on Android it's the Android browser, (Chrome-based, of the version that matches the OS), and on iOS it's iOS Safari (7 or 8).

Which means: a Cordova's app CSS must work in each target platform's webview, just like web applications must work in different browsers by employing the appropriate prefixed and non-prefixed styles. In short, everything you know about responsive web design applies with Cordova.

As noted in the last post, I discovered this right away because the CSS grid upon which I rely heavily on Windows isn't implemented on these other platforms. And that's not all–there are many variances and differences. But thankfully there's the site caniuse.com that will tell you what parts of HTML5 and CSS are and aren't available on which platfoms, and whether or not they need prefixing. Super helpful! I suggest you visit that site and get familiar with it.

What I found was very helpful at this stage (aside from going to a CSS framework which would relieve me from the details), was to first create a Windows app project with my HTML, and then open it in Blend for Visual Studio and do my Windows-based styling. In this case I needed to use flexbox for the layout, knowing that I couldn't use the grid. Blend gave me an easy way to work out things like margins and media queries in a very friendly environment, setting the baseline styles for the app.

Along the way I also decided that given the small size of many phone screens and the fact that I eliminated the map section, it didn't make sense to support a landscape orientation. We'll return later on how to enforce that in a Cordova app; at this point it just meant that I didn't need to worry about landscape orientations at all.

With my baseline CSS from Blend, I then dropped the HTML and CSS onto my web server; you can see it all at http://www.kraigbrockschmidt.com/src/heremyam/index.html (and it has some JS in there too now). What this allowed me to do is check the styling in a variety of browsers, and then use the browser's developer tools to find the necessary styling for that browser.

On my Windows machine I opened the site in both IE and Chrome; in the latter I worked out the webkit styles for flexbox. Once I did that and updates index.css on the web host, I then opened the same page on my Android tablet to verify the layout. Then I switched to my MacBook (which I acquired to build for iOS) and checked things out in Safari, and then also opened the site on my iPad. I opened the site on my Windows Phone too. (And, I might add, that creating a short URI saves a lot of trouble entering it into a mobile browser.)

I liked using the browsers here for three reasons. First, the developer tools let you change the live CSS and see the result, just like Blend does for Windows. Second, the mobile browsers especially are very clearly linked to the webview that a Cordova app will use. Third, a browser is easily resizable, allowing you to test and style for smaller screen sizes. For example, here are the IE, Chrome, and Safari browsers sized down to a minimum, with styles for <= 499px widths and <= 480px heights, which reduce font sizes and tighten up margins:

HereMyAm_Cordova_3-2

Here's the resulting CSS. Note that use of flex: 1 1 0px; and -webkit-flex: 1 1 0px; to expand certain elements to full available space (the location input control horizontally, the image area vertically). The #btnLocation::before rule is what sets the globe icon in the locate button, and other styles on the button make it circular.

@-ms-viewport{width:auto!important}

/* 
    Notes:
    Windows and Windows Phone (through 8.1) use -ms prefixed styles.
    Android and iOS use -webkit prefixed styles.

    There are a few general design breakpoints for layout:
    Heights: 480 and 768 (actually 767, see below)
    Widths: 480, which accomodates the 500-pixel width of Windows split-screen views.
    
    If you use device-[height | width] then there are more variations. Using
    [min | max]-[width | height] you get automatic scaling for CSS, which reduces
    the breakpoints. This has the effect that some device sizes don't translate
    exactly: the iPad's 768 dimension comes through as 767.78 so the breakpoint
    has to be 767 as CSS media queries operate on integer comparisons.    	
*/

/* 
    This app will focus on being portrait-only to simplify the layout
	requirements. The only adjustments we'll make, then, are for 
	various screen sizes and not for orientation.
*/

.mainContainer {
    /* Windows */
	display: -ms-flexbox;
	-ms-flex-flow: column;	

    /* Android, iOS */
    display: -webkit-flex;
    -webkit-flex-flow: column;
	
	height: 100%;	
}

/* Override of WinJS */
h3 {
    font-weight: 300;
}

.mainChild {
    margin-left: 20px;
    margin-right: 20px;	
}

.title {
	margin-top: 10px;	
}


.subsection {
	display:-ms-flexbox;		
    -ms-flex-flow: column;	

    display: -webkit-flex;
    -webkit-flex-flow: column;		
	
	margin-top: 15px;	
}

.section-title {
	/* height: 40px; */
}

#locationControls {
    /* Use a row flexbox here to allow the input control to fill the space */
	display:-ms-flexbox;		
    -ms-flex-flow: row;	

    display: -webkit-flex;
    -webkit-flex-flow: row;		
}

#txtLocation {
	flex: 1 1 0px;  /* Expand to fill available space */    
    -webkit-box-flex: 1 1 0px;   
    -webkit-flex: 1 1 0px;
}

#btnLocate {
    margin-left: 15px;

    /* Make it circular */
	min-width: 40px;
    width: 40px;
    border-radius: 20px;
}

#btnLocate::before {
    /* WinJS references ../fonts/Symbols.ttf from its stylesheets */
    font-family: "Symbols";
    font-weight: normal;
    font-size: 10pt;    
    content: "E12B"; /* globe */  
}

#photoSection {	
	flex: 1 1 0px;        
    -webkit-flex: 1 1 0px;    
    min-height: 100px;
}

#photo {	
	border: solid 1px gray;	
	width: 100%;    
	height: calc(100% - 40px); /* 40px is height of heading */
    margin-top: 6px;
    margin-bottom: 10px;

    /* Make sure the image is centered inside this area */
    display: -webkit-box;
    display: -ms-flexbox;    
    display: flex;
    justify-content: center;
    align-items: center;

    /* iOS */
    display: -webkit-flex;
    -webkit-flex: 1 1 0px;
    -webkit-align-items: center;
    -webkit-justify-content: center;
}

#shareSection {
    display: -webkit-box;
    display: -ms-flexbox;
    display: -webkit-flex;
    display: flex;
	-ms-flex-pack: center;	
    -ms-flex-item-align: center;
    justify-content: center;
    align-items: center;
    -webkit-justify-content: center;
    -webkit-align-items: center;

	margin-bottom: 15px;
}

.shareButton {
    margin-left: 8px;
    margin-right: 8px;
}

/* Adjustments for narrower/shorter screens */
@media screen and (orientation: portrait) and (max-width: 499px),
       screen and (orientation: portrait) and (max-height: 480px)
 {	
    h2 {
        font-size: 14pt;
    }

    button, input {
        font-size: 10pt;        
    }
}


/* Tighten up margins for short screens */
@media screen and (orientation: portrait) and (max-height: 480px) {
    .title {
        margin-top: 5px;
    }

    .mainChild {
        margin-left: 10px;
        margin-right: 10px;
    }

    .subsection {
        margin-top: 10px;
    }

    button, input {
        min-height: 15px;
    }

    #photoSection {
        margin-top: 5px;
    }

    #photo {
        margin-bottom: 5px;
    }

    #shareSection {
        margin-bottom: 5px;
    }
}

 

It's important to note–and you'll see this if you run in the Ripple emulator–that the sizes reported to CSS are scaled sizes, especially where max-height, max-width, and so forth are concerned. I would suggest sticking with these instead of trying to get device pixels, because doing that would only increase the complexity of your CSS for no benefit.

 

 


Switching platform targets in Visual Studio

One of the most important things to know when working in Cordova is how to switch between target platforms. There aren't obvious controls in Visual Studio for this, but it's there. Either use the Build > Configuration Manager menu item, click the Debug/Release drop-down in the toolbar and select Configuration Manager:

cordova1-1

Once you're there, the Active Solution Platforms drop-down is where you set your target.

cordova1-2

Doing this will change the main VS toolbar to reflect options available for that target. This is how you switch between targets and run the app on devices and emulators.

Tip: to shortcut this you can add the platform drop-down directly to the toolbar. Right click the toolbar and select Customize at the bottom of the long list. In the dialog that appears, click the Commands tab, then select Toolbar and Standard:

cordova1-6

Now click Add Command, then pick Build > Solution Platforms:

cordova1-7

This adds the platform drop down to the toolbar. You might need to use the Move Down button in the Customize dialog to place it where you want. I also used the Modify Selection button to narrow the size to 130, and placing it where I wanted I see this on my toolbar:

cordova1-8

Running the app, and first issues encountered: varying CSS support

With everything migrated now, I was able to run the app on the Local Machine (Windows) as expected. The one caveat I found is that Cordova at present targets Windows 8.0 and not 8.1 (I think Cordova version 3.6 is supposed to do 8.1), which means that it doesn't understand variable-sized views, and only snapped/filled states. This means that in a narrower view that's later than 320px you get the 320px snapped view inside the wider view like you do with any Windows 8.0 app on 8.1.

On Windows Phone the app ran great in portrait mode, but the layout wasn't working in landscape. Hard to tell why, because the present Cordova targets Windows Phone 8.0 which means the app is running inside a webview inside a Silverlight app. This means there's no debugger support whatsoever, so I can't poke into the DOM to see what's going on.

On Android the layout is all wrong…because an Android webview in which a Cordova app runs doesn't support the CSS3 grid specification as we enjoy on Windows. Urk. I've relied heavily on grid layout but clearly I'll have to convert to flexbox, which is why I said CSS comes up early.

On iOS (in the Ripple emulator), looks like we have the same issue.

The upshot here is that when working in Cordova, your HTML/CSS work is going to be very much like writing responsive web pages for different browsers, because that's really what's happening with Cordova: your code is running inside a webview which has varying capabilities depending on the target platform. 

 

Finding your DOM in the Ripple Emulator

When running on my Android device, Visual Studio's DOM Explorer works just like it does for a Windows app–you can do in and tweak styles and so forth.

cordova1-3

When running in the Ripple emulator, you still get the DOM Explorer except that you see everything that's showing in the emulator, including all the surrounding controls:

cordova1-4

Egads! Where is my app? Well, the easy way to find it is through that click-to-select button in the upper left above: click that, then click in the emulator, and you'll see the DOM tree expand to where your app is. You'll see that it's located under the sections ui > middle > device-container > viewport container > and then the iframe "document":

cordova1-5

 

What's very cool in the DOM Explorer is that the CSS editor does inherently known what styles are and aren't supported for that target device. I can see right away, for instance, that display: -ms-grid; isn't among those supported for Android and iOS webviews, which isn't at all surprising. So the next job is to convert my grid-based CSS to cross-platform flexbox, which will be the next post.


I said that CSS would come in early, but the first thing to discuss in migrating the Here My Am! files from my existing Windows Store app project to a Cordova project. For this I'm just starting with the HereMyAm2a code from Chapter 2 of my free ebook. And in fact I'll keep it simple for starters and just migrate the HTML and CSS, leaving off the JS code for now.

Preliminary notes/build environment

Although I'd started working on a universal Windows app migration for this project, it's not a necessary step for working with Cordova, because it also produces cross-platform apps that will work on Windows and Windows Phone alike, as well as iOS and Android. With Cordova, you let plugins worry about platform-specific things–you don't want that kind of code in your project. 

In addition, it's helpful to know that you don't need a Mac as a build server to work with Cordova until you really want to produce an app for the store. With the Visual Studio extension installed, you can run a Cordova app directly in the Ripple emulator , which is very cool in that you can switch between devices, resolutions, and orientations (including between target platforms) much like Blend for Visual Studio does for Windows, so you can see just how (badly) your CSS is performing.

For myself, I'm working on a Surface Pro 3 as my main machine, have a Windows Phone hanging off it, have an Asus MemoPad7 as an Android dev device, which I wrote about recently. My team has a Mac laptop set up in one of my co-worker's offices as a build server (also for Xamarin) which I can get to over VPN to the Microsoft corpnet from my home office…pretty cool!

Project structure

I started with a new Cordova project (File > New > Project > JavaScript > Multi-Device Hybrid App > Blank App (Apache Cordova)); this is what you want to do rather than trying to convert an existing Windows project. I then migrated the appropriate files over:

Windows Project File Cordova Project File
default.html index.html (This is the default; you can use default.html if you change the start page in config.xml)
js/default.js scripts/index.js (didn't migrate the code yet; the project template uses scripts instead of js as a folder…I might change this later)
package.appxmanifest config.xml (the Cordova generic manifest)
css/default.css css/index.css
html/map.html html/map.html (there is no default html folder, so I created one just to hold this file for the time being)
images/taphere.scale-100.png images/taphere.png; I'll be switching to draw this with a canvas later on, so it's just a placeholder.
images/<logos> Not migrated yet as these need to be specific for each platform, whcih I haven't done yet.
WinJS references WinJS is not a cross-platform, open-source library, an instead of having it in the project references I downloaded WinJS 3.0 and brought it directly into the project:

  • /css/frameworks/ui-dark.css (you'll get a whole ton of warnings on styles in here because of the differences in CSS support between platforms)
  • /css/fonts/Symbols.ttf (ui-dark.css and ui-light.css assume this file is in ../fonts, so it needs to be in a sibling folder unless you modify the reference)
  • /scripts/frameworks/base.js
  • /scripts/frameworks/ui.js
  • /scripts/frameworks/WinJS.js

I don't know how much of WinJS I'll be using, but given that I have lots of experience in this area, it seemed like a good opportunity to keep up! Plus the ui-dark.css has styling that I'm used to with this app. We'll see how it goes.

  The project template also includes a merges folder for platform-specific code, and a res folder for resources that I'll get to later on.

 

Updating index.html

With this migrated project, I fixed up the references in index.html to bring in the appropriate files:

    <link href="css/frameworks/ui-dark.css" rel="stylesheet" />    
    <link href="css/index.css" rel="stylesheet" />    

    <!-- Cordova standard -->
    <script src="cordova.js"></script>

    <!-- WinJS -->
    <script src="scripts/frameworks/base.js"></script>
    <script src="scripts/frameworks/ui.js"></script>
    <script src="scripts/frameworks/winjs.js"></script>

    <!-- Project references -->
    <script src="scripts/platformOverrides.js"></script>
    <script src="scripts/index.js"></script>

Note: in a Windows project, especially with WinJS page controls, it's usually helpful to prefix all references with a leading /. You do not want to do this with Cordova because it doesn't work. I'll have to see what happens later on when I try using page controls.

Note also that you need the reference to cordova.js even though it doesn't exist in your project.

With my script/css references fixed up, I just use the same markup as before in index.html:

<body>
    <div id="mainContent">
        <header id="header" aria-label="Header content" role="banner">
            <h1 class="titlearea "> 
                <span class="pagetitle">Here My Am!</span>
            </h1>
        </header>
        <section id="section" aria-label="Main content" role="main">
            <div id="photoSection" class="subsection" aria-label="Photo section">
                <h2 class="group-title" role="heading">Photo</h2>
                <img id="photo" class="graphic" src="images/taphere.png" alt="Tap to capture image from camera" role="img" />
            </div>
            <div id="locationSection" class="subsection" aria-label="Location section">
                <h2 class="group-title" role="heading">Location</h2>
                <div id="map" class="graphic" aria-label="Map"></div>
            </div>
        </section>
    </div>
</body>

 

Up next, running the app.



With my new role as a Content Developer in Microsoft's Developer Division, I'm working now with cross-platform tools like Apache Cordova and Xamarin, and dabbling a little in Unity, and also looking at the Visual Studio ALM tools and how they apply to these project types.

To help myself ramp up on Cordova, I'm in the process of migrating Here My Am!, the app that I build throughout the pages of Programming Windows Store Apps with HTML, CSS, and JavaScript, 2nd Edition, to Cordova. I can already see there will be numerous challenges to overcome, which I'll try to write about as I work through them (preview: the first post or two will be about CSS). 

For all this I'm using the Multi-Device Hybrid Apps Extension (Preview) for Visual Studio 2013 Update 3 (how's that for a name!). One thing that's immediately clear about the extension is that it saves a whole lot of trouble in getting a Cordova environment set up. Normally you have to go get a bunch of downloads from nine different places, some of which have installers and some of which are just ZIP files, and then you have to install them in the right order figure out how to configure each one with the right environment variables. The extension does that for you.

The documentation for the extension is found on http://msdn.microsoft.com/en-us/library/dn771545.aspx; the installer page specifically describes what pieces it downloads and how to do the configuration, manually. My team is, in fact, the one that produces this documentation–more to come!



From a background task, it's perfectly allowable to issue a tile update, for instance, when procedding a raw push notification in that task and updating a tile in response. With secondary tiles, you might also want to update the tile's arguments so that it launches into different content relevant to the tile update. However, secondary tile arguments are statically defined when the tile is pinned and cannot be changed later.

To work around this, you can use a set of static identifiers that you then dynamically map to your variable data. It's simplest to just keep a table of secondary tile IDs mapped to their dynamic arguments, in which case you're probably not using the static arguments at all.

Another question with background tasks is whether it's possible to do file I/O therein, because clearly if you're modifying a table of secondary tile arguments in a file you'll be doing file I/O. The thing to remember here is that if you use any kind of async APIs in the process, you need to make sure the background task stays active until the async work is complete. To do this, you use the deferral mechanism.

In C#, the general pattern is like this:

public async void Run(IBackgroundTaskInstance taskInstance)
{
    var deferral = taskInstance.GetDeferral();
    var contents = await DoAsyncWork();
    deferral.Complete();
}

In JavaScript, the deferral is found on the WebUIBackgroundTaskRuntimeClass object. You retrieve this in the worker with Windows.UI.WebUI.WebUIBackgroundTaskInstance.current, then call its getDeferral method, do your async work, and then call the deferral's completed method when you're done.

var task = Windows.UI.WebUI.WebUIBackgroundTaskInstance.current;
ver deferral = task.getDeferral();

DoWorkAsync().done(function () {
    deferral.completed();
    close();  //Shut down the background task
});

Note that JavaScript it's also necessary to call close() when the task is finished (which is WorkerGlobalScope.close()).

 


A partner we worked with had some troubles with a live tile image not appearing, and the process of working them out helped us understand better how to debug live tile issues.

First of all, make sure of two more global that affects tiles:

  • Live tiles don't appear in the Visual Studio simulator for Windows Store apps (though I believe they do on the phone emulator). Run locally or on a real device.
  • Make sure that live tiles for your app on the Start screen are not disabled (right-click to check).
  • Some live tiles won't look right if you've turned off system animations in PC Settings > Ease of Access > Other Options > Play Animations.

Second, the tile updater is quite sensitive to properly-formed XML. If you have any kind of glitch, the update payload will be ignored and you'll be left scratching your head. Here are some things that can trip you up:

  • The XML lacks the <?xml version="1.0" encoding="utf-8"?> header.
  • There is a space or linefeed before the <?xml> header.
  • The XML isn't properly formed (e.g. missing closing tags) or doesn't follow the Tile schema.
  • The elements you indicate for a tile template doesn't match the template itself.
  • You have a mismatch between the indicated template and the version number in the <visual> element.
  • You are using accidentally using <img> tags rather than <image> in the XML. Tile templates are not HTML: they use <image>.

If you think your XML is good but it's not working, try simplifying it to include only a single tile size payload, and try them one at a time. That can narrow down where an issue might be. Also use App Tiles and Badges sample as a reference point, specifically scenario 6 that lets you build payloads.

Next, a tile update can have proper XML but its image references might be invalid. If you're using http[s] references, double-check those links in a browser. If you're using the baseUri attribute anywhere in your XML, do that combining manually and check the URIs. Also check image references with any URI parameters that are added with the addImageQuery attributes. See "Using Local and Web Images" in Chapter 16 of my free ebook (page 897) for details.

Other allowed reference URI schemas are ms-appx:/// and ms-appdata:///local. You cannot use ms-appx-web:/// or ms-appdata:///[temp | roaming] — doing so will cause the update to be ignored.

Thirdly, remember that tile images must be 1024 pixels or less in both dimensions, and must be less than 200KB in size. This means potentially resampling an image to make it smaller, and it especially means being aware of the image format and quality as that significantly affects the size. Here's how I wrote about this issue in my book, page 899:

Sidebar: PNG vs. JPEG Image Sizes

When considering tile images for the larger 140% and 180% scales, the encoding you use for your images can make a big difference and keep them below the 200K size limit. As we saw in “Branding Your App 101” in Chapter 3, a wide tile at 180% is 558×270 pixels, a medium is 270×270 pixels, and a large is 558×558 pixels. With the wide and large tile sizes, a typical photographic PNG at this size will easily exceed 200K. I encountered this when first adding tile support to Here My Am! for this chapter, where it makes a smaller version of the current photo in the local appdata folder and uses ms-appdata:///local URIs in the tile XML payload. Originally the app was capturing PNG files that were too large, so I borrowed code from scenario 11 of the App tiles and badges sample, as we’ve been working with here, to create a smaller PNG from the img element using a temporary canvas and the blob APIs. This worked fine for a 270×270 medium tile image (a 180% scale that can be downsized), but for 558×270 and 558×558 the file was too large. So I borrowed code from scenario 3 of the Simple Imaging sample to directly transcode the StorageFile for the current image into a JPEG, where the compression is much better and we don’t need to use the canvas. For this second edition’s example I just capture JPEGs to begin with, which are already small enough, but I’ve preserved the code anyway for reference. You can find it in the transcodeImageFile function in pages/home/home.js, a routine that we’ll also rewrite in Chapter 18 using C# in a WinRT component. 


Such considerations are certainly important for services that handle the addImageQuery parameters for scale. For larger image sizes, it’s probably wise to stick with the JPEG format to avoid going over the 200K limit. 

 

The 200KB limit turned out to be the issue with the partner–all the images referenced in the live tile were larger than 200KB and therefore ignored. More specifically, the images were saved as PNGs, which again for photographic images produces larger file sizes than JPG. In this partner's case, even the 150×150 tile image was too large; if it had happened to be smaller, the partner would have seen it appear but not images for the other sizes, which might have revealed the problem more quickly.

What confused the process of tracking down this problem is that an invalid image will cause that particular tile update (for that size) to be ignore in toto, meaning that the text won't appear with an invalid image. This is a reasonable behavior because otherwise end users would see oddball tile updates with text but blank spots where images should be. Personally I'd love to see a debug option in the system that you could turn on so some kind of placeholder image would appear, or an error message if a tile update is invalid.

The other thing that can be confusing is that the system will always download an image from a valid URI, and then check whether it's too large to display. This makes some sense as it's difficult to know from a URI whether the image is too big or such. The system could do a round-trip to the network to check the size first and not download it, but it doesn't work that way: it will download, and then ignore the image. This means that just watching fiddler traces or such isn't deterministic in checking image validity.

If you have a service, by the way, that dynamically generates images for periodic tile updates, then it's possible to have some variance in image sizes that are being supplied in tile updates. For debugging, make sure your service is logging requests and the dimensions and file size of every image returned. Then you can see whether any images exceeded the allowable limits.


In a number of forum questions over the years, I've seen devs struggling with using relative URIs to in-package resources. Typically it boisl down to the difference between something like "images/pix1.jpg" and "/images/pix1.jpg".

These two URIs mean, in the words of Jeremy Foster, "dive into the images folder and find pix1.jpg" and "go back to the root, then dive into the images folder and find pix1.jpg", respectively.

In Windows Store apps, it's helpful to remember that all such in-package 'relative' URIs are shorthands for ms-appx://<package_id>, as in ms-appx://<package_id>/images/pix1.jpg or just ms-appx:///images/pix1.jpg. A leading / on URIs, then, is what really makes the reference an absolute one, rather than a relative one. It just feels relative because you're not specifying a schema.

To be even more precise, a relative URI always must resolve against some base URI, which is defined by the referring document. Where people get tripped up is that when you're using WinJS page controls, such as ms-appx:///pages/somePage.html, then ms-appx:///pages becomes the base URI, and images/pix1.jpg will resolve to ms-appx:///pages/images/pix1.jpg. If you thought you were referring to the root images folder in the project, then you'll likely find the image not appearing at all. If you use /images/pix1.jpg, on the other hand, then you are making an absolute reference from the package root via shorthand, which resolves to ms-appx:///images/pix1.jpg.

The bottom line is that unless you really know that you're making a relative reference to the currently loaded HTML page, use a leading / to get back to your package root.


In a bit of departure from my usual subject matter, I wanted to share a writeup I did for my team in regards to an Android device I acquired for the purposes of doing some dev work with Xamarin and Cordova in Visual Studio on Windows.

The Short Story:

If you are looking for an Android tablet for dev work (like for the purposes of doing Cordova and Xamarin), the most important thing is that you can find a Windows USB driver for the thing. This is what will allow you to debug from Visual Studio, Xamarin Studio, Eclipse, and so on, or, to be specific, to run the first Xamarin tutorial project through which you get to order a free (and real) C# T-shirt.

[I was aksed why I thought I needed a real device when there are emulators. Emulators only go so far and fall short of where a device actually touches the physical world, e.g. sensors, NFC, attaching via USB, using touch for real, seeing real-world performance on target devices, and just understanding how something works when you’re holding it in your hand. Also, an emulator will never give you the full end to end experience of a consumer, e.g. switching between apps, multitasking, acquiring apps from a store, etc. I wouldn't ever ship an app without at least one real device test. In my work, specifically, the people I'm writing for are using real devices and having one myself, and immediately hitting a significant challenge, gave me a much better understanding of issues that we can help those customers solve.]

Anyway, if you have any particular device in mind, check the list on http://developer.android.com/tools/extras/oem-usb.html, and follow the links to the manufacturer pages to verify the existence of an easy-to-acquire driver. If you don’t find one, look for a different device that has one, or you can enjoy the process I describe below to essentially hack one.

And when you have a device, http://developer.android.com/tools/device.html is where you find info on how to unlock a device for dev work—has the great UI of going to a specific setting and tapping it seven times!

 

Sidebar: Getting the Device to Show Up in the Tools

Before going into the details of hacking a driver, a little aside on running apps on an Android device from Visual Studio. Tools talk to an Android device through the Android Debug Bridge, or abd, which is managed through the command line (see http://developer.android.com/tools/help/adb.html). You can easily get to a suitable command prompt through a button that shows up in Visual Studio if you’ve installed the multi-platform hybrid app tools or the Xamarin SDK (circled below):

Android devices in VS

If the adb isn’t seeing your device (which you can check on the command line with adb devices -l), it won’t show up in the drop-down list of debug targets. If this happens, here are steps you can try:

  1. Unplug the USB cable and reattach. This might be sufficient.
  2. It might be necessary to restart Visual Studio. I’ve found that VS will refresh itself when you attached/detach a device, but if it doesn’t, there’s always a restart.
  3. Open the command prompt and do adb kill-server and then adb start-server. You might need to restart VS again.

When VS sees the device, you can then enjoy all the usual capabilities of VS. Otherwise you get stuck with emulators that you probably won’t enjoy, especially when it comes to figuring out which one has which API level support to match your code project.

 

The Long Story: Hacking a USB Driver

When looking for a tablet, I shopped around on Amazon to find a middle-of-the-road, reasonably-priced, name-brand tablet. In the end I chose the Asus MemoPad 7, which looked much more reliable than the really cheap tablets, but not so expensive as the mainline Google or Samsung models.

Knowing what I now know, I probably would have chosen differently, because once I figured out how to unlock the tablet as a dev device, I found that ASUS didn’t have an appropriate USB driver on its site. This also meant that plugging the device into my system for the first time didn’t manage to find a driver, thus resulting in the undesirable  !  in Device Manager.

Turning to a full web search, I found some other possibilities, and pointed Device Manager to the place where I’d downloaded the driver package. However, each one I tried gave me that annoying message, “Windows could not find a driver for your device.”

Let me get a bit technical for a minute. Every USB device is uniquely identified by a Vendor ID or VID, and a Product ID or PID. So USB device people talk of the VID/PID for any given device, which you can always find for any device (driver or not) by viewing its properties in Device Manager, switching to the Details tab, and selecting Hardware Ids from the drop-down:

VID PID in Device Manager

Here you can see that the VID is 0B05 (Asus) and the PID is 5507.

When you see the “Windows could not find a driver for your device” message, it means that the .INF file for the driver that you pointed to does not contain an entry that matches the VID/PID of the device, and Windows will refuse to install the driver. This is true even if you know damn well that the driver will work, and that the silly driver package just hasn’t been updated properly.

Which brings us to the hack, and some learnings along the way.

Now lest you think an .INF file is some kind of magic, it’s not. It’s just a plain text file that describes the VID/PID combinations with which the driver will work. For example, I found a generic Asus ADB Driver package (all tablets really work with the same driver), and it had lots of entries like this:

%CompositeAdbInterface%     = USB_Install, USBVID_0B05&PID_4DEF&MI_01

None matched the 5507 PID, however. OK, so perhaps I could just add an appropriate line in the .INF file for the right VID/PID? (Making sure to enter the line in both the .NTx86 and .NTamd64 sections of the .INF file.)

Rats. No luck. The .INF file is just an easy way to describe the driver’s supported devices, but there’s a second step that goes through a .CAT file. A .CAT file is a “Security Catalog” that, well, provides a security check. So although the .INF file will match, you’ll get a security warning and Windows will refuse the install the driver.

So….the natural question is, how do you make that .CAT file match the .INF file? It’s pretty easy, actually. The Windows Driver Kit (WDK), that’s an extra download from MSDN, has a little tool called inf2cat.exe that does exactly this.

After waiting a while for the rather large WDK to download and install, I ran the tool, produced the right .CAT file, and tried again.

Damn. Windows doesn’t like it when you don’t have a properly signed .CAT file with an appropriate security certificate.

Fortunately, someone has blogged about this exact thing: http://tivadj-tech.blogspot.in/2012/09/certificate-check-error-when-installing.html. Let me summarize:

  • Go to PC Settings > Update and Recovery > Recovery > Advanced Startup and press the Restart Now button.
  • Oh, wait a minute—be sure that you have your Bitlocker key handy because you’ll be required to enter it when the machine reboots!
  • When the machine reboots, and you enter that Bitlocker key, select Troubleshoot > Advanced Options > Startup Settings and tap Restart.
  • When the machine reboots (again, did I mention you’ll need your Bitlocker key?), you’ll have a list of choices for tweaking the boot process. Select the one that says “Disable driver signature enforcement.”

Bwah-ha-ha! Now you have power. Well, at least you have the power to install a hacked driver, meaning that Windows will allow you (with another dire warning that you get the perverse pleasure of ignoring), to install a driver using.INF and .CAT files that you rebuilt yourself.

Once I did this, Windows was happy with the Asus Memo Pad 7, and so was ADB and Visual Studio. This allowed me to finally run that first Xamarin project on my device, and order the T-shirt that was, of course, the whole point of all this!

 

Postscript: the Universal ADB Driver

To be honest, I didn’t end up using the ASUS driver package itself. In the course of my investigations I came across a Universal ADB Driver that works for a whole host of devices, meaning you can use this one driver for any variety of Android  devices because all those devices are basically talking to the ADB daemon running on the device once you do that seven-tappy thingy to unlock it.

The source for the driver—which really amounts to an .INF file plus copies of the generic USB driver from the Windows Driver Kit, is on GitHub: https://github.com/koush/UniversalAdbDriver. This makes it easy, and less of a hack, to modify the .INF file, rebuild the .CAT, and sign it properly. This is what I have installed now.

To be honest again, I didn’t actually get the signing certificate in the project to work, so I used advanced boot and installed the unsigned driver. Maybe someday I’ll understand this business with certificates (and I'm happy to be educated through comments–send me some links!), but I was eager to get my T-shirt.