blog.jmwhite.co.uk Open in urlscan Pro
2606:4700:3031::6815:18ae  Public Scan

URL: https://blog.jmwhite.co.uk/2015/09/19/revealing-why-emails-appear-off-centre-in-android-4-4-kitkat
Submission: On November 17 via api from SG — Scanned from DE

Form analysis 2 forms found in the DOM

Name: searchGET https://blog.jmwhite.co.uk

<form action="https://blog.jmwhite.co.uk" name="search" class="blogsearch" method="get">
  <fieldset>
    <input type="text" class="s" name="s" value="Search My Blog" onfocus="if (this.value == 'Search My Blog') {this.value = '';}" onblur="if (this.value == '') {this.value = 'Search My Blog';}">
    <input type="submit" class="submit" value="">
  </fieldset>
</form>

POST /index.php

<form id="polls_form_3" class="wp-polls-form" action="/index.php" method="post">
  <p style="display: none;"><input type="hidden" id="poll_3_nonce" name="wp-polls-nonce" value="99ccdeb6ec"></p>
  <p style="display: none;"><input type="hidden" name="poll_id" value="3"></p>
  <p>Every so often I'll ask a question related to website development for a bit of fun. Feel free to take part in the poll below.</p>
  <p><strong>Do you use an IPv6 tunnel service?</strong></p>
  <div id="polls-3-ans" class="wp-polls-ans">
    <ul class="wp-polls-ul">
      <li><input type="radio" id="poll-answer-8" name="poll_3" value="8"> <label for="poll-answer-8">Yes</label></li>
      <li><input type="radio" id="poll-answer-9" name="poll_3" value="9"> <label for="poll-answer-9">No</label></li>
      <li><input type="radio" id="poll-answer-10" name="poll_3" value="10"> <label for="poll-answer-10">What's an IPv6 tunnel?</label></li>
    </ul>
    <p><input type="button" name="vote" value="   Vote   " class="Buttons" onclick="poll_vote(3);"></p>
    <p style="text-align: center;"><a href="#ViewPollResults" onclick="poll_result(3); return false;" title="View Results Of This Poll">View Results</a></p>
  </div>
</form>

Text Content

James' Blog
Mobile Nav
 * Home
 * About me
 * Web stuff
 * Email dev
 * Contact


REVEALING WHY EMAILS APPEAR OFF-CENTRE IN ANDROID 4.4 (KITKAT)

4
Saturday September 19th 2015 at 11:10 AM
Email Development Linux

Recently some members of the Litmus Community noticed that email campaigns in
Android 4.4 were no longer being centred to the device width. I was slightly
puzzled by this as overall Android 4.4 has a pretty decent email experience, but
sure enough I started seeing email content being cut off on the right hand side.
Looking at my code I couldn’t see anything out of the ordinary that would cause
this. Wanting to find out more, I dived into the source code of the Android 4.4
mail app to get some answers! Find out what I discovered.



The work that lead to this write up involved some technical steps, so rather
than make you read all of that first, I’ll provide a summary of the fix and if
you want, you can read on how I discovered it! Keep scrolling if you like source
code!


WE DON’T NEED YOUR MARGINS ANDROID!

The problem is simply margins, the Android 4.4 email client is applying a margin
value on all sides (but its not actually visible on the right side), so even
before your email message is rendered, its messed with overall viewport already.
It applies the side margin to the body element and the top and bottom sides with
a wrapping div which your email is placed within:


THE EMAIL WRAPPER IN ANDROID 4.4 KITKAT

1
2
3
4
5
6
7
<!--  Margin on the body, which is a variable value (more on that below) -->
<body style="margin: 0 ?px;">
    <!-- Wrapping div that applies margin to the top and bottom -->
    <div style="margin: 16px 0; font-size: 80%">
         <!-- Your email template goes here -->
    </div>
</body>

The margin applied on the body is the one that’s causing the most trouble as
this is what the off-centre appearance is being caused by. The wrapping div is
adding a bit more margin to the top and bottom, which isn’t necessarily that
much of a bad thing, though if you’ve already applied padding in your email
template its going to create further spacing that you might not want.


NORMALISE THE MARGINS

1
2
body { margin:0 !important; }
div[style*="margin: 16px 0"] { margin:0 !important; font-size:100% !important; }

Placing this in the head of your template in a style block should resolve the
issue. The body margin is simple enough to override using !important, the div
wrapper is a little more complicated because it has no id or class to
specifically target, likewise the body tag doesn’t have any specific values
either. So in order to target the div wrapper you have to use attribute
selectors in a rather crazy way.


TARGETING THE DIV WRAPPER

The only property unique about that div wrapper is the margin property and its
value, therefore we use that as the value to target conditionally. It has to be
written exactly how its defined in the source code of the email client (note the
space), otherwise it won’t evaluate as true conditionally.

The only caveat is if you have a div with the same margin property written with
the same values somewhere else in your email template, it will also tag that as
well, there really isn’t another way to target it however. You could just
normalise the margin on the body and just accept the additional margin on the
top and bottom and leave it at that I guess. Though from a boilerplate sense you
want to try and remove all pre-defined client specific stuff to get the best
foundation for your email campaigns.

There is also a font-size property declared on the wrapper. It doesn’t actually
appear to cause any problems, you should be defining your font sizes inline on
block elements anyway so you’ll never notice it, however if you want to reset
you can.


TECHNICAL DETAILS

So if your interested on how I discovered all of the information above, look no
further! It involved a bit of CSS debugging and reading the code of some
specific Java classes that relate to the message view. I’ll admit I can’t
actually write Java, but generally I can read code from a programmatic sense,
you know being a web developer and all that!


DEBUGGING LAYOUTS

First of all it became visually apparent that there was some form of “spacing”
issue in the email client, but without being able to actually see the DOM/box
model, its becomes harder to discover what the spacing actually is, cue a bit of
CSS debug magic:

1
2
3
4
5
6
* { background-color: rgba(255,0,0,.2); }
* * { background-color: rgba(0,255,0,.2); }
* * * { background-color: rgba(0,0,255,.2); }
* * * * { background-color: rgba(255,0,255,.2); }
* * * * * { background-color: rgba(0,255,255,.2); }
* * * * * * { background-color: rgba(255,255,0,.2); }

This essentially turns layouts into different coloured zones so you can see
margin/padding as well as other areas represented in block layers. Doing this in
Android 4.4 was very telling:



Pink outer layer: Unexpected Android 4.4 spacing
Green inner layer: Padding on the actual email template (10px)

This spacing is actually present on the top, left and bottom sides, in theory it
should be on the right side as well, but I guess its not visible due to the
email client message view cutting it off.

Knowing that this spacing is being added, it now became a case of finding out
where its being applied, if its margin or padding and can it actually be
removed? I took a complete stab in the dark and created the following CSS rule
to see what would happen.

1
div { margin:0 !important; padding:0 !important; }

Sure enough the spacing on the top and bottom disappeared, but the spacing on
the left side remained, obviously there is more than just margin/padding on a
div at play. In order to see what was happening I dived into the Android 4.4
email app source code.


TRACKING DOWN THE PROBLEM FROM THE SOURCE

Because Android is open source, its source code can be viewed via the Android
Git repo or on the GitHub mirror. I know I’m looking for the email client code
on the KitKat release branches. I decided that I should download an .apk of the
Android 4.4 email client and decompile it, which is what I did. After
decompiling and throwing in some HTML based keywords to cherry pick and filter
Java files of interest, I came across a specific class in the
SecureConversationViewController.java file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public void renderMessage(ConversationMessage conversationmessage)
    {
        mMessage = conversationmessage;
        conversationmessage = mWebView.getSettings();
        boolean flag;
        if (!mMessage.alwaysShowImages)
        {
            flag = true;
        } else
        {
            flag = false;
        }
        conversationmessage.setBlockNetworkImage(flag);
        conversationmessage = new StringBuilder(String.format("<body
style=\"margin: 0 %spx;\"><div style=\"margin: 16px 0; font-size: 80%%\">", new
Object[] {
            Integer.valueOf(mSideMarginInWebPx)
        }));
        conversationmessage.append(mMessage.getBodyAsHtml());
        conversationmessage.append("</div></body>");
        mWebView.loadDataWithBaseURL(mCallbacks.getBaseUri(),
conversationmessage.toString(), "text/html", "utf-8", null);
        conversationmessage = ConversationViewAdapter.newMessageHeaderItem(null,
mDateBuilder, mMessage, true, mMessage.alwaysShowImages);
        mMessageHeaderView.unbind();
        mMessageHeaderView.bind(conversationmessage, false);
        if (mMessage.hasAttachments)
        {
            mMessageFooterView.setVisibility(0);
            mMessageFooterView.bind(conversationmessage,
mCallbacks.getAccountUri(), false);
        }
    }

Here the wrapper is exposed and it turns out margin is the problem. We have
margin being applied on the body and via a wrapping div. What’s interesting is
the margin on the body isn’t a fixed value, its actually variable as there is a
placeholder (%s) on the margin value for the right and left side (expressed in
shorthand). This value is actually calculated by this bit of code also part of
the same Java file:

1
mSideMarginInWebPx = (int)((float)bundle.getDimensionPixelOffset(0x7f09001b) /
bundle.getDisplayMetrics().density);

The calculation is an integer offset divided by the device density which will be
obviously be variable based on different Android devices and their DPI. For more
info on these functions see the Android SDK articles below:

getDimensionPixelOffset
getDisplayMetrics
getDisplayMetrics (density)


CONCLUSION

So after a bit of layout debugging and reading some source code we now know why
Android 4.4 doesn’t like to centre your emails. I hope my CSS fix is useful to
your email campaigns going forward!

SHARE THIS:


Tags:
 * android 4.4
 * email
 * margin


 * Subscribe to my RSS Feed
 * Follow me @jamesmacwhite

WHAT'S POPULAR ON MY BLOG?

 * Popular
 * Comments
 * Latest


 * Today Week Month All

 * Revealing why emails appear off-centre in Android 4.4 (KitKat)
   
 * Solving DPI scaling issues with HTML email in Outlook
   
 * Office 365 (OWA) and its many, many quirks for email designers
   
 * Dell XPS 9530 UEFI BIOS Recovery
   
 * How to fix the 413 Request Entity Too Large error for SVN
   

 * My Netflix rant and workarounds for the recent blocking of IPv6 tunnels
   
 * CrashPlan and Filebot, beware of extended metadata/attributes!
   
 * Windows 10 Outlook Mail gets updated with email rendering “improvements”
   
 * Dovecot and IMAP issues on Windows 10 Mobile
   
 * Skype Video app setup error 0xA1080001 on Windows 10
   

 * Today Week Month All

 * The Shadow effect in CSS3
   
 * Creating a website navigation bar with CSS
   
 * Creating a localhost in Windows (Part 2: Installing PHP 5)
   
 * Rounded Corners in CSS3
   
 * Creating a glassy non div navigation bar
   



GETTING YOUR THOUGHTS

Every so often I'll ask a question related to website development for a bit of
fun. Feel free to take part in the poll below.

Do you use an IPv6 tunnel service?

 * Yes
 * No
 * What's an IPv6 tunnel?



View Results

 Loading ...

LATEST POSTS

 * My Netflix rant and workarounds for the recent blocking of IPv6 tunnels
 * CrashPlan and Filebot, beware of extended metadata/attributes!
 * Windows 10 Outlook Mail gets updated with email rendering “improvements”
 * Dovecot and IMAP issues on Windows 10 Mobile
 * Skype Video app setup error 0xA1080001 on Windows 10

RECENT CHATTER

 * Mike commented on Dell XPS 9530 UEFI BIOS Recovery
 * Carodak commented on Dell XPS 9530 UEFI BIOS Recovery
 * Spark Email Design commented on Solving DPI scaling issues with HTML email in
   Outlook
 * Frankie commented on My Netflix rant and workarounds for the recent blocking
   of IPv6 tunnels

LATEST TWEETS

 * I wish Google Tag Manager allowed you create lookup tables from external data
   sources easily. Importing from JSON directly would be amazing.
   
   
   3 years ago
   
   
 * Receive email marked "High importance". Brain immediately downgrades that
   statement to "Low importance".
   
   
   3 years ago
   
   
 * GTM hack of the day: A third party site is insane and encodes & in a query
   URL to &amp; (breaks URL variable). Work… https://t.co/xq2dp3Ggol
   
   
   3 years ago
   
   

FIVE RANDOM FACTS

Something a little bit quirky, here's five random facts about me.

 1. My last name isn't blogs
 2. I am half English and Scottish
 3. I can touch my nose with my tongue
 4. I despise cheese with great intensity. Ewwww.
 5. When I was three years old I removed a child proof lid off a medicine
    bottle. Skills!

James' Blog
 * RSS
 * Get in touch
 * Follow me on Twitter
 * W3C Valid
 * Powered by WordPress

© Copyright 2008 - 2021, James' Blog - Some rights reserved.
Powered by WordPress CMS, CentOS & Linode

You are not that 0.1% that happens to be unique

ShareThis Copy and Paste