Why Android Tablets haven’t gained traction in Enterprise

Apple is selling north of 10M iPads every quarter. Compare that to leading Android tablet manufacturers:

  • Motorola Xoom, the first real Android tablet, sold 250,000 in the first quarter of its release in 2011. No updated numbers are readily available since then, but given the lack anything resembling  marketing coming from Motorola around the Xoom since then, along with increased competition from other manufacturers, I would guess that last quarter saw no more than 250,000 units sold in the past quarter as well.
  • Samsung Galaxy Tab 10.1 sales numbers are not broken out. However, a Lenovo executive claimed that the device’s smaller cousin, the 7″ model, has sold no more than 20,000 units at retail.
  • ASUS Transformer Prime, the best Android tablet that I have used, revealed in a court case that they had sold 80,000 units in the first few months (~2,000/month).
In short, sales of Android tablets are measured in tens to hundreds of thousands, where sales of iPads are measured in tens of millions.
We experience the enterprise divide every day at Mediafly. Many of our enterprise customers want to support Android tablets, but when pen comes to paper, the iPad is all that managers care about. This divide is further driven home by recent research from Good Technology, makers of enterprise email solutions.  They find that 97.3% of enterprise activations for tablets in first quarter 2012 are iPad versions. The remaining 2.7% are for Android tablets.
The way this market is shaping out is quite depressing for an Android supporter like me.   (As you may know, I’ve been recognized for my work building Android apps, and have built the popular Chicago Transit Tracker in the Android Market.).  And there seems little that will change this in the near future. Below are my reasons for why Android tablets won’t come to dominate enterprise tablet usage over the next few years.
  • No price advantage. While Android smartphones have a major price advantage to the iPhone ($99 for an unlocked entry-level Android smartphone, vs. $549 for the iPhone), the same price disparity can’t be found with tablets. The Galaxy Tab 2 10.1, Xoom, and iPad 2 all hover around $399, while the Transformer Prime prices at around $499.
  • Phones are a necessity, tablets are not.  If you are going to spring for a tablet, or if your company will buy a set of tablets for you and your coworkers, you/they will usually buy the market leaders, the safe option. This means the iPad.
  • The iPad (3) is, frankly, better. I’ve used the Kindle Fire, NOOK Color, Samsung Galaxy Tab 7″, Motorola Xoom, Sony Tablet S, and ASUS Transformer Prime.  Of all of these, the only tablet that comes close to the usability and smoothness of the iPad is the ASUS Transformer Prime. And that device falls short as well: the keyboard is awful, the display is not a retina display, and there is no cellular capability.  And let’s not forget that ASUS’s marketing is hopelessly outmatched by Apple’s marketing.
You will notice that I don’t mention apps. Are apps important? For the consumer, absolutely. For the enterprise, less so. That will be a topic of another blog post.
Comments welcome.

How to tether your Galaxy Tab (7″) to your Mac OS X

Oh, how resourceful we can become!

Faced with an Internet connection reminiscent of the 56kbps dial-up days while at work, I dove into finding a way to tether the Galaxy Tab 7″ with its unlimited data plan to my Mac OS X (Snow Leopard). It turned out to be easier than I expected.

  1. Plug your Tab into your Mac.
  2. On your Tab, navigate to Settings > Wireless and network > Tethering
  3. Check “USB tethering”
  4. On your Mac, open System Preferences > Network. You should see SAMSUNG Android in the left.

    Select it and choose “Advanced…”.
  5. In Vendor, select Samsung.  In Model, select GPRS (GSM/3G). In APN, enter the APN for your Tab. You can find the APN on the tab in Settings > Wireless and network > Mobile networks > Access Point Names. Click OK.
  6. Back at the main screen, tap Connect. After a few seconds, the status next to SAMSUNG Android should turn green, and the Sent and Received bars should flicker green.

That’s it! Now wasn’t that easy?

HTTP Live Streaming and Android

(This post is cross-posted on Mediafly’s Blog.)

Google finalized Android 3.0 (“Honeycomb”)’s SDK only last week. Also last week, Motorola released the Xoom, the first Android 3.0 tablet, and a number of other manufacturers have announced their own Android 3.0 tablets.

One of the key missing features of the Android platform had been the support of HTTP Live Streaming (also known as HLS). HLS is a media streaming protocol that has been made popular by Apple. Apple rolled out the requirement that any app streaming over cellular networks must do so using HTTP Live Streaming about a year ago, and this change has resulted in a dramatic adoption of HLS by video distributors.

With Android 3.0, Google has taken steps to close that gap. From the Android 3.0 Platform Highlights page: “The media framework supports most of the HTTP Live streaming specification, including adaptive bit rate.”

Despite the “most” disclaimer, this is a major step forward for video delivery to the Android platform. HLS is a relatively simple streaming protocol that enables:

  • Dynamic bitrate switching: if you drop into a network with lower bandwidth, your video stream will gracefully switch to the lower quality stream. Conversely, if you switch to a higher bandwidth network, your player will adjust to use a higher quality stream.
  • Support for open implementations: HLS has been submitted to the IETF as a proposed Internet standard.

Scalability: HLS streams (particularly for on-demand content) can be powered purely by a web server. This means that HLS can make use of all of the recent advancements in caching and scaling that web servers have built over the years.

Currently, the only support for a live streaming technology in Android has been through Adobe Flash Player and RTMP. This has proven very challenging to integrate, because:

  • At $4,500, Flash Media Server is not cheap. HLS technology is included with every installation of OS X.
  • Flash Media Server does not scale as well as HLS out of the box. As mentioned above, HLS can be powered purely by a web server. RTMP, however, requires a proprietary server that keeps active connections open in order to maintain connections to clients.
  • Flash Player on Android only works when surrounded by the browser. This means that, as a developer, you must embed a WebView, and within that the Flash Player will operate. This additional overhead is enough to bring resource-limited phones to their knees.
  • While great improvements have taken place, Flash Player on Android remains buggy. Mediafly has taken steps to ensure its Flash Player apps and RTMP streams work smoothly over Android, but not everyone has.

If you are interested in learning more about HTTP Live Streaming, Flash, and Mediafly’s business media delivery offerings, please contact us.

Handling Android 2.3 WebView’s broken AddJavascriptInterface

(Apologies in advance to my normal readers for this technical topic.)

The Google Android team released the Android 2.3 (“Gingerbread”) SDK two days ago, to much fanfare. This has sent the tech blogging world into a publishing frenzy, as it usually does. However, a potentially disastrous bug has surfaced that could crash literally thousands of apps in the Android Market immediately after opening the app.

The problem is described succintly here:
http://code.google.com/p/android/issues/detail?id=12987
In short:
Many apps show all or part of their UI with embedded WebViews that can render HTML.
Those WebViews make use of a great feature that bridges JavaScript (in the HTML) to the native Java code that “surrounds” the WebView.
This bridge is completely broken in Android 2.3. Trying to make even a basic call breaks the WebView immediately and crashes the app.

I believe members of the Android team are aware of the problem, and from early reports, it does not affect the Nexus S (the first Android 2.3 phone). This doesn’t really help those of us working against the emulator, however.

Here is a simple solution to work around this.

1.) In onCreate, check to see if the bridge is broken, and add the JavaScript interface only if not broken.


// Determine if JavaScript interface is broken.
// For now, until we have further clarification from the Android team,
// use version number.
try {
if ("2.3".equals(Build.VERSION.RELEASE)) {
javascriptInterfaceBroken = true;
}
} catch (Exception e) {
// Ignore, and assume user javascript interface is working correctly.
}

// Add javascript interface only if it's not broken
if (!javascriptInterfaceBroken) {
webView.addJavascriptInterface(this, "jshandler");
}

2.) Create a WebViewClient that passes in a new JavaScript object with the same name as your JavaScript interface object.


@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
finishLoading();

// If running on 2.3, send javascript to the WebView to handle the function(s)
// we used to use in the Javascript-to-Java bridge.
if (javascriptInterfaceBroken) {
String handleGingerbreadStupidity=
"javascript:function openQuestion(id) { window.location='http://jshandler:openQuestion:'+id; }; "
+ "javascript: function handler() { this.openQuestion=openQuestion; }; "
+ "javascript: var jshandler = new handler();";
view.loadUrl(handleGingerbreadStupidity);
}
}

NOTE: for each of the Javascript-to-Java functions that you use, you will need to add a new Javascript function and pass it in as part of the loadUrl, like the defined below.

"javascript:function openQuestion(id) { window.location='http://jshandler:openQuestion:'+id; }; "

3.) In the same WebViewClient, override the URL handling portion to handle the URLs defined in step 2. After catching the URL, parse out the function and parameters, then use reflection to actually call the method.


@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (javascriptInterfaceBroken) {
if (url.contains("jshandler")) {
// We override URL handling to parse out the function and its parameter.
// TODO: this code can only handle a single parameter. Need to generalize it for multiple.

// Parse out the function and its parameter from the URL.
StringTokenizer st = new StringTokenizer(url, ":");
st.nextToken(); // remove the 'http:' portion
st.nextToken(); // remove the '//jshandler' portion
String function = st.nextToken();
String parameter = st.nextToken();
// Now, invoke the local function with reflection
try {
if (sMethod == null) {
sMethod = MyActivity.class.getMethod(function, new Class[] { String.class });
}
sMethod.invoke(MyActivity.this, parameter);
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
return true;
}
return false;
}

With this solution, you only need to make a few Android code changes and do not need to modify server output.

Comments welcome.