Building Mobile Websites

  • 5/15/2012

The Device-Detector Site

As an example of a mobile site that serves device-specific content, let’s consider what it takes to build a device-detector website with ASP.NET MVC. Figure 4-12 shows the desktop version of the site that is being made mobile. It has a three-column layout and a variety of links. This site doesn’t do any particularly fancy things: it is limited to displaying the UA string and a few other browser capabilities.

Figure 4-12

Figure 4-12 The desktop version of the Device-Detector sample site.

A mobile version of this site will remove a lot of bells and whistles and mostly focus on the primary use-case: providing details about the current browser.

You should expect to see a single-column template with some graphics at the top (header) and bottom (footer). In addition, a couple of links in the main content area just connect users to the page with device details and contact (see Figure 4-13).

Figure 4-13

Figure 4-13 The mobile layout for the Device-Detector sample site.

In the early days of software development, about 20 years ago, it was natural to design the main screen of applications (mainframe and desktop) through a main menu that accepted user selections via mouse clicks or keystrokes. Nearly all applications were designed around a main menu that collected the various use-cases of the application.

The advent of pop-up and floating menus marked the end of this approach, which clearly was limited to moderately complex applications. With mobile sites (and to some extent, also with native applications), this old-fashioned model is revamped due to the relative simplicity of the logic and the need to be direct and focused.

Routing to Mobile Views

For a site that serves mobile content, an accurate detection of the device is key. In addition, a (possibly) automated mechanism to route requests to the right view would be welcome. Up to ASP.NET MVC 3, you have no tools to allow you to select the view and no convention-over-configuration (CoC) approach.

In ASP.NET MVC 4, instead, an enhanced infrastructure lets you give mobile views conventional names that are resolved automatically by the system. For example, in ASP.NET MVC 4, a view named index.mobile.cshtml will be used to serve requests for the Index action when coming from a generic mobile device. ASP.NET MVC 4 is expected to give you even more control over the presentation layout, as it also supports syntax like index.iphone.cshtml for a specific class of mobile devices.

Configuring a Mobile-Aware View Engine

The source code that comes with this book provides a sample view engine that works well with ASP.NET MVC3—the AmseViewEngine class. The engine builds on top of the built-in Razor view engine and allows you to invoke both desktop and mobile views. If the browser is detected as a mobile browser, the engine will attempt to resolve any view named Xxx as a view named Xxx.mobile. If the mobile browser belongs to a specific class, then it is mapped instead to an Xxx.profile view, where profile indicates the name of the class.

You register the mobile-aware view engine in global.asax, as shown in this code:

public static void RegisterViewEngines(ViewEngineCollection viewEngines)
{
   viewEngines.Clear();
   viewEngines.Add(new AmseViewEngine(new AspnetMobileViewResolver()));
}

protected void Application_Start()
{
   ...
   RegisterViewEngines(ViewEngines.Engines);
}

The constructor of the view engine receives a user-defined class that knows the strategy to transform the originally requested view to see if it exists in the site. In particular, the AspnetMobileViewResolver class checks for xxx.mobile instead of xxx if it detects that the requesting browser is mobile.

View engines are a specific feature of ASP.NET MVC. In ASP.NET Web Forms, you can achieve the same result by registering an HTTP module that intercepts incoming page requests, detects whether the requesting browser is a mobile browser, and redirects to a corresponding mobile page, if any.

Routing to Mobile Resources

Just as any other type of view, a mobile view may rely on a bunch of external resources, such as images, style sheets, and script files. Because you control the markup of the mobile view, you can simply place all the references you need there. For example, you can link the jQuery Mobile library only from the mobile layout; likewise, you might want to use smaller images for a mobile view or, better yet, you might want to embed images in the same view as Base64-encoded strings.

It is crucial to note, however, that mobile views may have their own set of resources. Most of the time, you don’t need to differentiate mobile resources on a per-device-class basis.

Detecting Device Capabilities

So now there is a mechanism that can redirect automatically to a view that has been specifically designed for mobile browsers. But which part of the ASP.NET run time determines whether the requesting browser is a mobile browser? And, more important, which algorithm is used?

As you may understand, this is the central point of mobile site development—you may not need detailed information in all cases about what a given device can or cannot do, but you always need to know—with extreme accuracy—at least whether the requesting browser is mobile or not.

ASP.NET Native Detection Engine

ASP.NET has its own detection API centered on the following code:

HttpContext.Request.Browser.IsMobileDevice

The IsMobileDevice property returns a Boolean value and indicates whether the current request comes from a mobile device.

Without beating around the bush, this code is not really reliable. For example, it fails on a number of popular devices, such as the HTC Desire and Samsung Galaxy S smartphones (both equipped with Android); it also fails on a wide range of simpler devices, such as the Samsung GT S3370 Corby. Curiously, the native ASP.NET detection API succeeds with the BlackBerry, iPhone and iPod devices, and with Windows Phone devices. Why is this so?

The value returned by the IsMobileDevice property results from a partly accurate analysis of the UA string that ASP.NET performs under the hood. Essentially, ASP.NET uses the UA string as a key to match the requesting browser to one of the predefined device profiles. A device profile is a text file with a .browser extension located on the server under the Windows folder at the following path:

// This is the path if you have the .NET Framework 4 installed on the server
\Microsoft.NET\Framework\v4.0.30319\Config\Browsers

Figure 4-14 offers a preview of the typical content of this folder.

Figure 4-14

Figure 4-14 The content of the Browsers folder.

Files in this folder contain some basic information about a few families of browsers. Each family contains a regular expression used to match the UA string. If a match is found, then the dictionary of browser capabilities exposed to the application is filled with the values of the properties that are known to apply to that family of devices.

This solution was devised years ago when the problem was detecting just a few desktop browsers. Matching the UA string of a mobile device—and its nearly infinite variations—and identifying the right value for a given property require a much richer and articulated database. The content of browser files can be extended and new files can be created, but it doesn’t change the basic fact that something stronger is needed.

The bottom line is that if you simply rely on the IsMobileDevice property, you seriously risk offering a desktop site to many mobile devices. Worse yet, this especially happens with older devices that will display just a basic site, to the frustration of users. What else can you do?

A Better Way of Detecting Mobile Devices

Detecting device capabilities is a difficult problem in mobile (not just ASP.NET mobile) because of the wide fragmentation of devices. In my opinion, the device fragmentation problem has just one exact solution that I’ll discuss thoroughly in Chapter 6. This solution is using a DDR like the Wireless Universal Resource File (WURFL).

Most DDRs are not free for every use; and free versions of most DDRs just reduce severely the number of capabilities that they return. The bottom line is that you should be ready to spend money when it comes to DDRs; your money will be well spent.

Anyway, I’d like to illustrate quickly a couple of totally free (but possibly only approximate) solutions that you might want to consider before you go to Chapter 6 and pick up your favorite DDR framework. I’ll leave it up to you whether any of these options may work for you in the real-life battlefield.

The first option entails writing your own wrapper around the Browser.IsMobileDevice property. You can create it as your own class, or perhaps as an extension method to the Request object. Regardless of these implementation details, what really matters is the logic that you use to write the code. Here’s a sample detection function written as an extension method for the native ASP.NET Request object:

public static class RequestExtensions
{
    public static Boolean IsMobileDevice(this HttpRequestBase request)
    {
        var response = request.Browser.IsMobileDevice;
        if (response)
            return true;

        // If response is false, there are still good chances to have a mobile device.
        // Let's check the user-agent string for common substrings.
        var userAgent = request.UserAgent.ToLower();
        response = userAgent.Contains(ºopera mini") ||
                    userAgent.Contains("mobile") ||
                    userAgent.Contains("samsung") ||
                    userAgent.Contains("nokia") ||
                    userAgent.Contains("htc") ||
                    userAgent.Contains("android") ||
                    userAgent.Contains("windows phone") ||
                    userAgent.Contains("midp") ||
                    userAgent.Contains("cldc");
        return response;
    }
}

If the IsMobileDevice native property returns true, then you can be sure that the requesting browser is really a mobile browser. The problem is with false negatives. The simplest thing you can do is check the UA string looking for common substrings associated with mobile agents. The preceding example includes some manufacturer names and operating system names. The MIDP string refers to the Mobile Information Device Profile (MIDP), a specification that is part of the Java Platform Micro Edition (Java ME) framework. MIDP works on top of the Connected Limited Device Configuration (CLDC), which is instead a lower-level specification. In the end, both MIDP and CLDC are strings that appear often in UA strings sent by mobile devices.

Finally, you may have noticed that this list contains no reference to popular devices such as iPhone (and iPod/iPad) and BlackBerry. This is because ASP.NET 4 comes with .browser files for detecting both platforms (see Figure 4-14). As a result, the basic IsMobileDevice property works on iOS and BlackBerry devices.

A more powerful approach is based on a repository of mobile profiles that Microsoft built for internal purposes and made public through a CodePlex project: http://mdbf.codeplex.com. It’s called the Mobile Device Browser File (MDBF). If you visit the website, however, you find out that it is a dead project now. This means that the database for this project won’t be maintained and extended any longer, although you still can download and use it. With the wave of new devices being released every month, this is clearly a problem.

To use the MDBF repository, all you need to do is copy the file in the App_Browsers/Devices folder of your website. The content of the file will be read by the ASP.NET infrastructure and used to populate the dictionary of browser capabilities.

You can extend the MDBF repository—an 18 MB XML file—to keep it up to date. Likewise, you can update .browser files and add new ones. Both options, however, are not compelling because they require a lot of maintenance work and research. And this is probably the reason why approximate and exact solutions exist, and exact solutions are not free.

DDR Options

The quintessential DDR is, without a doubt, WURFL, by ScientiaMobile (http://www.scientiamobile.com). WURFL was created in 2002—four years before the DDR acronym was first coined on a World Wide Web Consortium (W3C) mailing list. An open-source community-based initiative, WURFL is comprised of an API and a data repository. The API maps incoming HTTP requests to a known device definition and then retrieves known capabilities for that device from the repository. The repository is updated independent of the API so that companies just refresh the repository periodically without the need to change or rebuild the application. The WURFL API is available for a variety of platforms and languages, including ASP.NET, Java, C++, Ruby, and PHP. See http://wurfl.sourceforge.net.

In the summer of 2011, the WURFL owners have moved the project to a different licensing model, which is stricter in many ways. While the API is technically still open-source, the Affero GPL v3 license now requires that users completely open-source the proprietary code linked to the WURFl API on their servers. This requirement is typically not compatible with the requirements of commercial entities. Therefore, to avoid open-source provisions, companies can buy a commercial license for WURFL API and data from ScientiaMobile. Note that the WURFL repository is distributed with a proprietary license that prevents you from copying the WURFL data and using it with third-party APIs.

Another interesting DDR specifically aimed at the ASP.NET platform is 51Degrees. See http://51degrees.codeplex.com. 51Degrees relied originally on WURFL as the source for device information, but the change in the licensing model of WURFL forced to adopt a different and proprietary repository. Recently, 51Degrees has been relaunched with a new vocabulary (i.e., set of property names) and new data. 51Degrees is a purely commercial initiative, but it offers a free version as a teaser for the platform. The free version is limited to a DDR with just four properties: isMobile, ScreenPixelWidth, ScreenPixelHeight, and LayoutEngine, which is simply the browser engine.

Other players in the DDR world are DetectRight (http://www.detectright.com) and DeviceAtlas (http://www.deviceatlas.com). MobileAware (http://www.mobileaware.com) and NetBiscuits (http://www.netbiscuits.com) are also names worth mentioning, although they do not offer pluggable DDRs as part of their main business model. In other words, the DDR is just one component of a more elaborate product.

CSS Media Queries

Lateral thinking is about solving problems using an innovative approach and unconventional reasoning. The lateral thinking about device detection seems to be CSS Media Queries and responsive (or adaptive) web design. Is detecting devices hard? Don’t do that, then; just take a bunch of basic properties (e.g., screen size) and let the page adapt and reflow accordingly.

The magic potion that enables responsive web design is CSS Media Queries. Introduced with CSS 3, media queries simplify the design of sites that might be consumed through devices of different screen sizes ranging from 24 inches on a desktop monitor to 3 inches on most smartphones. Media queries are not specifically a technology for mobile development, but the flexibility of this feature makes it really compelling to use to serve different devices with a single codebase.

The idea, in fact, is that you just create one site with a single set of functions and then apply different CSS styles to it by loading a different style sheet for different media. The great improvement brought by CSS 3 is that a medium (such as a screen) now can be restricted to all devices that match given rules. Here’s an example of media queries:

<link type="text/css"
      rel="stylesheet"
      href="downlevel.css"
      media="only screen and (max-device-width: 320px)">

Placed in a HTML page (or view), this markup links the Downlevel.css file only if the page is viewed through a browser with a width of 320 pixels or less. Note that there’s no explicit check on the type of browser, whether mobile or desktop: all that matters is the real width of the screen. (Needless to say, with a screen width of 320 pixels, it can only be a mobile phone or handheld device.) The only keyword should be added for the sole purpose of hiding the statement from browsers that don’t support media queries. These browsers, in fact, don’t understand the media type and go right ahead. The full documentation about media queries can be found at http://www.w3.org/TR/css3-mediaqueries.

What’s the problem with media queries?

It is a common idea these days that by simply adding media queries to a site, you make it ready for mobile clients. CSS media queries help making the page content more mobile-friendly, but they don’t affect other critical areas, such as the number of HTTP requests per page, whether DOM manipulation and AJAX are supported, or if a touchscreen is available.

Media queries can check out only a limited number of browser properties—namely, those listed in the W3C standard: device width and height, orientation, aspect ratio, color depth, and resolution to name the most frequently used ones. Most properties support the min- and max- prefix to help you write more precise queries. Being a CSS feature, media queries only can hide elements that are too big or low-prioritiy to display on a small screen. You still pay the costs of downloading or keeping these elements in memory. You can use some JavaScript in the pages to download or configure images programmatically. In this way, heavy elements can be managed in a more optimized manner.

In addition, media queries require a browser that supports CSS3. So they work on most smartphones, but not, for example, on Windows Phone 7.0. An all-browser solution for media queries is available through a jQuery plug-in that you can get at http://www.protofunc.com/scripts/jquery/mediaqueries. However, there’s no guarantee that the mobile browser where you may be using this plug-in can really run jQuery.

Browser Capabilities

Detecting whether the requesting browser runs on a mobile device is only the first step toward delivering an adequate experience to any mobile users. Once you have identified a device correctly as a mobile device, you should try to detect the capabilities of the device. For example, you might want to know the version of the operating system, its real screen width and height, whether it supports AJAX, if it can perform some DOM manipulation, and if CSS is supported. In addition, you might want to optimize the user interface in case the device has a touchscreen or is really a tablet.

Finally, have you ever tried to visit a site with an older phone? If you have, then you know what I mean. First, the phone will likely have no support for WiFI, so it will connect over the mobile network. The slow connection will take a lot of time to see how many different connections are made to download images, scripts, and auxiliary files. You might always want to merge CSS and minify scripts, but what about images? Sprites are a possible solution, but they require CSS support from the device. Inline images (namely, images embedded in the page as Base64 strings) are another route to explore.

You need to know these and possibly more details about the specific device. Some properties can be tested programmatically with a bit of JavaScript. The following code, for example, shows how to check programmatically whether AJAX is supported:

var xhr = window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest();
if (xhr === null)
    alert("No support for Ajax");

Many other properties can’t just be tested programmatically. For instance, how would you detect programmatically if a device understands inline images, has a touchscreen, or is a tablet? For these and other types of capabilities, you need a repository of information that is updated weekly, if not more frequently. If delivering a great user experience on a variety of mobile devices is your goal, then you need the appropriate tool. And you probably need to pay for it.

Putting the Site Up

At the end of the day, a mobile site is just a website that has been designed according to a different set of guidelines. Once you know whether the device is mobile and what its capabilities are, you can proceed safely with the actual design of the site—layout, style, and markup.

Adjusting the Layout

In a mobile site, you might want to use mostly a single-column layout and move navigation and search functions to the top and bottom of the page. Finally, you might want to leave the user free to scroll vertically to locate what is relevant but doesn’t fit on the physical page. Beyond these basic rules, the design of a mobile site is all about finding the most friendly and creative way of presenting your content. Here’s the layout file for the mobile version of our site.

Note that the listing uses the ASP.NET MVC Razor syntax to describe the view. The Razor syntax mixes plain HTML with executable expressions. Executable expressions are prefixed with the @ symbol. In particular, in the following example, the ViewBag expression refers to a collection through which the page passes values to the view. A good step-by-step tutorial to Razor can be found at http://goo.gl/9eTEm.

<html>
    <head>
        <meta name="viewport"
              content="width=device-width, initial-scale=1.0, maximum-scale=1.0,
                       user-scalable=no" />
        <title>@ViewBag.Title</title>
        <link href="@Url.Content("~/Content/Styles/Site.css", mobile:true)"
              rel="stylesheet"
              type="text/css" />
    </head>
    <body>
        <div id="header">
            <img src="@Url.Content("~/Content/Images/logo.png", mobile:true)"
                 class="image" alt="" />
        </div>
        <div id="content">
            <h2>Know Thy Devices</h2>
            <p>
                Find out details of the devices that visit your site.
                This demo also shows a sample mobile template.
            </p>
        </div>
        <div class="actual-body">
            @RenderBody()
        </div>
        <div id="footer">
            <p>Architecting Mobile Solutions for the Enterprise</p>
        </div>
    </body>
</html>

Note that this file is named Layout.mobile.cshtml and is resolved in _Viewstart.cshtml, using an enhanced version of the native UrlHelper object in ASP.NET MVC:

@using Mobi.Framework.ViewEngines;
@{
    Layout = Url.Content("~/Views/Shared/_Layout.cshtml", mobile:true);
}

As mentioned, the layout file implements a single-column view and points to external resources using our custom Url.Content method as a resource switcher.

Let’s find out more about the viewport <meta> tag.

Most mobile browsers can be assumed to have a rendering area that’s much larger than the physical width of the device. This virtual rendering area is called the viewport. The real size of the internal viewport is browser-specific. However, for most smartphones, it is around 900 pixels. Having such a large viewport allows browsers to host nearly any webpage, leaving users free to pan and zoom to view content, as in the following illustration:

httpatomoreillycomsourcemspimages1163861.png

This behavior may perhaps be desirable (or at least not too disturbing) when you have a high-resolution smartphone; but what if your users host the site within a 240 x 320 device? It’s like looking through a keyhole. To gain control over mobile browsers’ viewports, you add an explicit viewport <meta> tag and instruct the browser about it as follows:

<meta name="viewport"
      content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />

In this example, you tell the browser to define a viewport that is the same width as the actual device. Furthermore, you specify that the page isn’t initially zoomed and can’t be zoomed in by users. Setting the width property to the device width is fairly common, but you can indicate an explicit number of pixels. Figure 4-15 shows how the same page looks on an older device with and without the viewport tag.

Adjusting the Style

A mobile site deserves its own CSS file where you define a bunch of new styles required by the specific user interface. In addition, the CSS file will probably need to override some of the styles shared with the desktop site (if any).

For example, you might want to remove background images and replace them with solid colors. Background images are a great example to show how CSS media queries can hardly be the perfect fit when you need to provide multiple views for devices other than smartphones. With media queries, you only have the device width to distinguish devices. However, when it comes to devices less than 300 pixels wide, you can still find different capabilities. Some relatively powerful devices fall in this category that have touchscreen and good HTML capabilities. For these devices (e.g., Samsung Corby), you can still use background images, but you cannot for any devices smaller than 300 pixels. This is to say that you can do a lot with CSS styles, but not everything. Beyond a threshold, you just need to upgrade to another solution as WURFL. (See Chapter 6 for more information.)

Figure 4-15

Figure 4-15 Effects of the viewport tag.

In a mobile style file, it is important to use the padding property appropriately to ensure that clickable elements (in touch-enabled devices) are large enough to accommodate a relatively inaccurate pointing device like the human finger.

Adjusting the HTML View

The sample site, Device Detector, while not realistic in terms of functionality, is an excellent starting point for understanding the role of browser capabilities. Figure 4-16 compares the desktop and mobile versions of Device Detector.

Figure 4-16

Figure 4-16 Desktop and mobile site face to face.

The mobile site requires an extra step to get to the real data: you must click the Details link. The Index view is therefore different in our ASP.NET MVC application. The Index.mobile.cshtml view simply renders a static markup with a couple of links. It is interesting to see where the Details link points. It points to a Details action method on a new controller—the DeviceController—that is used only by the mobile subsystem:

public class DeviceController : Controller
{
    public ActionResult Details()
    {
        ViewBag.UserAgent = Request.UserAgent;
        ViewBag.IsMobile = Request.Browser.IsMobileDevice;
        ViewBag.SupportTables = Request.Browser.Tables;
        ViewBag.MobileDeviceInfo = String.Format("{0}, {1}",
                                                    Request.Browser.MobileDeviceModel,
                                                    Request.Browser.MobileDeviceManufacturer);
        ViewBag.PreferredImageMime = Request.Browser.PreferredImageMime;
        ViewBag.ScreenSize = String.Format("{0} x {1}",
                                            Request.Browser.ScreenPixelsWidth,
                                            Request.Browser.ScreenPixelsHeight);
        ViewBag.SupportAjax = Request.Browser.SupportsXmlHttp;
        ViewBag.DomVersion = Request.Browser.W3CDomVersion;

        // Point to the device.cshtml view
        return View();
    }
}

The method would return the view generated from the Details.cshtml template. However, because this method is invoked only from the mobile site, the actual view file will be Details.mobile.cshtml. However, the view engine being used here can pick up Details if it can’t find Details.mobile.

The Details method collects some data about the requesting browser and composes them into the view. As you can see in this example, the information about browser capabilities is what ASP.NET makes available natively. I actually took the screenshots of this chapter from a site where I installed the MDBF repository—now largely outdated, but far better than the default ASP.NET browser configuration.

In particular, you can consume some of the capabilities being passed down to the view to fork your rendering code, as shown here:

@if (ViewBag.SupportTables)
{
    <table id="deviceTableInfo" cellpadding="4" cellspacing="2">
    ...
    </table>
}
else
{
    <ul>
        <li> ... </li>
        ...
    </ul>
}

Figure 4-17 shows a screenshot of the site captured from an iPod device.

Figure 4-17

Figure 4-17 The Device Detector site displayed on an iPod device.

Figure 4-18, instead, shows the same site on a low-level device, but it is still touch-based and has some decent HTML capabilities (JavaScript and CSS support).

Figure 4-18

Figure 4-18 The Device Detector site displayed on an Samsung Corby device.

Figures Figure 4-17 and Figure 4-18 don’t show noticeable differences in the rendered markups. However, if you view it live, you see that the device of Figure 4-18—an older device—takes a while to render the page for at least a couple of reasons. First, it doesn’t have much processing power, so any operation (JavaScript or just downloading) is slower. Second, it doesn’t have WiFi support; so any download can occur only via 3G. The rendering experience is somewhat painful because it first displays the skeleton of the HTML; next, it downloads the CSS and applies colors; and finally, it gets the picture and inserts that into the layout. Tweaking HTML for older browsers is an operation that you might want to optimize on a per-page basis—if it is worth the cost.