Wednesday 10 October 2012

Android ICS and target-densityDPI

I was debugging an interesting issue on our mobile site recently, and the problem came down to densityDpi

This attribute in the viewport meta tag for android devices is documented here: http://developer.android.com/reference/android/webkit/WebView.html

So what's the problem?

We have a little pull-down console that is included in pages whose content is mainly under the control of a third party supplier (I won't go into the complexity there).  This console would shrink down during the page load.

On Gingerbread and Jellybean, the  shrinkage would only affect the offending page, but on Icecream Sandwich, when we then navigated to another page, we would find the whole page had shrunk - and it would continue to display "zoomed out" pages until we reset the browser settings.



The culprit

Every page included the "viewport" meta tag, but the offending page included "target-densityDpi=device-dpi".

So why is that a problem?

CSS Pixels

When you define a size in CSS, everything is in relation to a CSS pixel.   A CSS pixel is not the same as a pixel on your device.  CSS pixels are meant to be displayed at 96dpi - that's not always the case, but in general if you make something 96px wide it will be an inch wide.

Devices will scale the assets on the page so that 96px is an inch wide, regardless of how many pixels that device has in an inch.  That doesn't mean that the display becomes blocky - except for raster images that only have that many pixels.  This is why we need to use better resolution images for more modern devices.

Android, target-densityDpi and javascript.

As we get more advanced devices, we sometimes feel the need to use more of those pixels - in general I feel that this was a bad move.  The device manufacturers should have implemented an interface that would allow us to detect that their device supports a higher resolution so that better resolution assets could be supplied to it, and we should have left it to the device to render the assets in the best way.

But that's not what has happened.

Javascript for android devices will report the screen width in device pixels in the screen.width variable; and the window width (usually only a little smaller) in CSS pixels from window.innerWidth.  This should enable the developer to access the correct asset for the device.

If the target-densityDpi has been set in the viewport, the dpi of a CSS pixel is altered.  The default is "medium-dpi", but the set values "high-dpi", "low-dpi" or "device-dpi" can also be used, or any value between 70 and 200.

This isn't a big problem on an individual page, since usually the whole page is under your control and the right assets and CSS sizes can be used.

The big problem is that Icecream sandwich retains the dpi setting between pages, and not even resetting it when you move between websites.  A malicious page somewhere out on the internet could change your dpi setting, whilst looking normal itself - and every subsequent page could look enlarged or shrunk, and in some cases so much to be unusable.

So how do we fix it?

Icecream sandwich is common enough that we need to do something to fix the problem for any user who happens to visit the site using it.  Fortunately, we can just reset the target-densityDpi back to the default value of "medium-dpi".  This isn't even necessary on every page - just on any landing page that they would be expected to visit first, or we could detect users entering the site from their referrer header.

No comments:

Post a Comment