Wednesday, April 24, 2013

Device Detection in ASP.NET MVC 4

In this example I am going to demonstrate the MVC 4 Device detection libraries. ASP.NET MVC 4 introduces Display Modes to which allows you to write device specific application. This is a newly introduced feature of ASP.NET MVC 4. This selects a view depending on the browser making the request, which means you can target specific devices and present the user device specific customized pages.

By default Display Mode Provider implements the mobile view;

clip_image002

You just need to provide the mobile specific pages to your application by just creating a pagename.mobile.cshtml and you are done.

clip_image004

I have done few customizations in my Web and Mobile pages just to display the requesting browser UserAgent and to identify if this is a page for web or mobile. Now let’s run the application and see output.

I have used Mozilla User Agent Switcher Add-on here to switch between the browsers. clip_image002[4]

Page displayed by default User Agent (Desktop)

clip_image004[4]

Page displayed when I select the iPhone as User Agent.

You can notice here that this all achieved without even writing a piece of code till now, with just the addition of .mobile.cshtml page I am able to achieve this.

But in real world things are not so straight forward, we have plenty of devices emerging every year and we have to provide device specific pages for each one of those devices with very little or negligible development effort and of course with minimal code changes.

With display mode provider we cannot just add a device specific pages instead we can also specify Browser specific page, OS Specific pages and even Vendor specific pages yes that is true. The only thing what we need to know is the identifiable user agent which provides us enough information about the Browser, Device, OS, etc information.

Now let me add some customization to my code for iPhone. In this case I am going to display a page which is specific to only iPhone. First of all let’s create a page for iPhone as Index.iphone.cshtml

clip_image006

You can and add some code which will tell us on runtime that this page is only meant for iPhone. Now add few lines of code in Global.ascx.cs file. Here in these codes I have added entry into the DisplayModeProvider for iPhone, this will tell the application that whenever you see the iPhone text in the browser UserAgent, just take the user to Index.iphone.cshtml page.

DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("iphone")
{
     ContextCondition = (context => context.GetOverriddenUserAgent().IndexOf("iphone", StringComparison.OrdinalIgnoreCase) >= 0)
});

Now let’s see the output at runtime,

clip_image002[8]

Here in the screen above you can see that I have got an additional entry for iphone in DisplayModeProvider collection, and the output screen I got from the code is as below:

clip_image004[9]

Similarly you can add as many entries as you wish, and for e.g if you want to have same page for multiple devices like for iPad and Tablet I want to display Index.tablet.cshtml page, then you can write the codes as:

DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("tablet")
{
    ContextCondition = (context => context.GetOverriddenUserAgent().IndexOf("tablet", StringComparison.OrdinalIgnoreCase) >= 0
    || context.GetOverriddenUserAgent().IndexOf("ipad", StringComparison.OrdinalIgnoreCase) >= 0)
});

So you can see DisplayModeProvider is fully customizable and extensible based on your requirement. But the catch is you need to have a complete list of Browser User Agents to handle virtually all the possible devices programmatically from your code. For I came across one very interesting link from Tech Brij Blog, including the code provided in this blog will complete this example and you can have a complete working example of the device detection using MVC 4

private static string GetDeviceType(string ua)
{
    string ret = "";
    // Check if user agent is a smart TV - http://goo.gl/FocDk
    if (Regex.IsMatch(ua, @"GoogleTV|SmartTV|Internet.TV|NetCast|NETTV|AppleTV|boxee|Kylo|Roku|DLNADOC|CE\-HTML", RegexOptions.IgnoreCase))
    {
        ret = "tv";
    }
    // Check if user agent is a TV Based Gaming Console
    else if (Regex.IsMatch(ua, "Xbox|PLAYSTATION.3|Wii", RegexOptions.IgnoreCase))
    {
        ret = "tv";
    }
    // Check if user agent is a Tablet
    else if ((Regex.IsMatch(ua, "iP(a|ro)d", RegexOptions.IgnoreCase) || (Regex.IsMatch(ua, "tablet", RegexOptions.IgnoreCase)) && (!Regex.IsMatch(ua, "RX-34", RegexOptions.IgnoreCase)) || (Regex.IsMatch(ua, "FOLIO", RegexOptions.IgnoreCase))))
    {
        ret = "tablet";
    }
    // Check if user agent is an Android Tablet
    else if ((Regex.IsMatch(ua, "Linux", RegexOptions.IgnoreCase)) && (Regex.IsMatch(ua, "Android", RegexOptions.IgnoreCase)) && (!Regex.IsMatch(ua, "Fennec|mobi|HTC.Magic|HTCX06HT|Nexus.One|SC-02B|fone.945", RegexOptions.IgnoreCase)))
    {
        ret = "tablet";
    }
    // Check if user agent is a Kindle or Kindle Fire
    else if ((Regex.IsMatch(ua, "Kindle", RegexOptions.IgnoreCase)) || (Regex.IsMatch(ua, "Mac.OS", RegexOptions.IgnoreCase)) && (Regex.IsMatch(ua, "Silk", RegexOptions.IgnoreCase)))
    {
        ret = "tablet";
    }
    // Check if user agent is a pre Android 3.0 Tablet
    else if ((Regex.IsMatch(ua, @"GT-P10|SC-01C|SHW-M180S|SGH-T849|SCH-I800|SHW-M180L|SPH-P100|SGH-I987|zt180|HTC(.Flyer|\\_Flyer)|Sprint.ATP51|ViewPad7|pandigital(sprnova|nova)|Ideos.S7|Dell.Streak.7|Advent.Vega|A101IT|A70BHT|MID7015|Next2|nook", RegexOptions.IgnoreCase)) || (Regex.IsMatch(ua, "MB511", RegexOptions.IgnoreCase)) && (Regex.IsMatch(ua, "RUTEM", RegexOptions.IgnoreCase)))
    {
        ret = "tablet";
    }
    // Check if user agent is unique Mobile User Agent
    else if ((Regex.IsMatch(ua, "BOLT|Fennec|Iris|Maemo|Minimo|Mobi|mowser|NetFront|Novarra|Prism|RX-34|Skyfire|Tear|XV6875|XV6975|Google.Wireless.Transcoder", RegexOptions.IgnoreCase)))
    {
        ret = "mobile";
    }
    // Check if user agent is an odd Opera User Agent - http://goo.gl/nK90K
    else if ((Regex.IsMatch(ua, "Opera", RegexOptions.IgnoreCase)) && (Regex.IsMatch(ua, "Windows.NT.5", RegexOptions.IgnoreCase)) && (Regex.IsMatch(ua, @"HTC|Xda|Mini|Vario|SAMSUNG\-GT\-i8000|SAMSUNG\-SGH\-i9", RegexOptions.IgnoreCase)))
    {
        ret = "mobile";
    }
    // Check if user agent is Windows Desktop
    else if ((Regex.IsMatch(ua, "Windows.(NT|XP|ME|9)")) && (!Regex.IsMatch(ua, "Phone", RegexOptions.IgnoreCase)) || (Regex.IsMatch(ua, "Win(9|.9|NT)", RegexOptions.IgnoreCase)))
    {
        ret = "desktop";
    }
    // Check if agent is Mac Desktop
    else if ((Regex.IsMatch(ua, "Macintosh|PowerPC", RegexOptions.IgnoreCase)) && (!Regex.IsMatch(ua, "Silk", RegexOptions.IgnoreCase)))
    {
        ret = "desktop";
    }
    // Check if user agent is a Linux Desktop
    else if ((Regex.IsMatch(ua, "Linux", RegexOptions.IgnoreCase)) && (Regex.IsMatch(ua, "X11", RegexOptions.IgnoreCase)))
    {
        ret = "desktop";
    }
    // Check if user agent is a Solaris, SunOS, BSD Desktop
    else if ((Regex.IsMatch(ua, "Solaris|SunOS|BSD", RegexOptions.IgnoreCase)))
    {
        ret = "desktop";
    }
    // Check if user agent is a Desktop BOT/Crawler/Spider
    else if ((Regex.IsMatch(ua, "Bot|Crawler|Spider|Yahoo|ia_archiver|Covario-IDS|findlinks|DataparkSearch|larbin|Mediapartners-Google|NG-Search|Snappy|Teoma|Jeeves|TinEye", RegexOptions.IgnoreCase)) && (!Regex.IsMatch(ua, "Mobile", RegexOptions.IgnoreCase)))
    {
        ret = "desktop";
    }
    // Otherwise assume it is a Mobile Device
    else
    {
        ret = "mobile";
    }
        return ret;
}

The code above covers a very exhaustive list of devices/browsers/OS which are available. This is a reengineered version of Categorizr(A device detection script) provided with premium version of 51degrees.mobi

The function provided above uses RegEx library to find the matching content in the Browser User Agent string and based on the match it returns the device type as string. The Code below calls the GetDevice function to get the device type string to the ContextCondition as either tablet, mobile or tv.

DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("tablet")
{
   ContextCondition = (context => GetDeviceType(context.GetOverriddenUserAgent()) == "tablet")
});
DisplayModeProvider.Instance.Modes.Insert(1, new DefaultDisplayMode("tv")
{
   ContextCondition = (context => GetDeviceType(context.GetOverriddenUserAgent()) == "tv")
});
DisplayModeProvider.Instance.Modes.Insert(2, new DefaultDisplayMode("mobile")
{
   ContextCondition = (context => GetDeviceType(context.GetOverriddenUserAgent()) == "mobile")
}); 

This is just a small sample of the Display Mode Provided packaged with ASP.NET MVC 4, other links which might help you in understanding the overall concepts are:

References:

http://www.campusmvp.net/displaymodes-in-mvc-4/

http://msdn.microsoft.com/en-us/library/system.web.webpages.displaymodeprovider(v=vs.111).aspx

Sample project with the implementation can be downloaded from here:

https://docs.google.com/file/d/0BzIjFd_Ps-MSQThrTklrbXZOQlU/edit?usp=sharing

4 comments:

Griffin Dorsey said...

This is very good example how device detection model works for ASP.NET development.

Hire ASP.net programmer said...

Well, Giffin the good part of MVC4 is that it keeps your project well prepared and you don't have to write bunches of if-else or switch code for dissimilar devices.

Daljit Bhail said...

We (http://Bhail.com) use ASP.NET MVC Sites with responsibe web and Kendo Mobile. We wanted only redirects to Kendo for Phones and not tablets. Coded the following:

DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode()
{
ContextCondition = (context => context.GetOverriddenUserAgent().IndexOf("tablet", StringComparison.OrdinalIgnoreCase) >= 0
|| context.GetOverriddenUserAgent().IndexOf("ipad", StringComparison.OrdinalIgnoreCase) >= 0)
});

Anonymous said...

Save my day, thks

Post a Comment