Showing posts with label mobile. Show all posts
Showing posts with label mobile. Show all posts

Monday, August 17, 2015

Smartwatch Manifesto


As I mentioned in my previous post I've owned a Pebble Time for a few weeks now which basically makes me an expert on all things smartwatch related (please note the sarcasm). But seriously I do think I have a much better understanding now of what a smartwatch is and should be for. So here's my Smartwatch Manifesto.

A Smartwatch is a companion not the star of the show


What I mean by this is that a smartwatch should help you with some other task, unrelated and uninitiated by the watch (with the one exception of telling time). To prove this I present a simple test. While standing up straight, hold your arm out parallel to the floor for 3 minutes without putting it down. Go do it now. Sucks doesn't it?....

You're obviously not going to be interacting with your watch in that position but that test is to show you that it is unnatural for you to not have your arms at your side or in a sort of resting position. When you initiate doing stuff with your watch it makes you do something unnatural.

The goal of a Smartwatch app should be to get you in and out as quickly as possible


This rule is a direct result of rule #1. The ways that a smartwatch app can enable you to get in and out as quickly as possible are:
  • Present the minimal amount of information on the screen as needed to fulfill the task.
  • Be context aware, and use that context to decrease interaction.
    • For example, if you know the user is running or biking, default to voice control rather than button or touch input.
  • Allow voice interaction to both initiate activities as well as respond to notification.
  • When using voice dictation, validate using audio.
    • You already know audio is okay since I initiated the interaction with voice.
    • It prevents me from having to focus on my watch instead of my primary activity.
    • It helps me get the context someone else will hear/read my statement in.

A Smartwatch needs to enhances communication


Society today communicates via voice, touch, and text. We use different means of communication in different contexts. A smartwatch should enhance communication in the context the user is in. For example; when I'm out and about I will primarily want to respond to things using voice commands as it's easier than having to pay attention to the screen.

For each primary means of communication that the smartwatch enhances the wearer should be able to initiate the communication, see current meta-data about the communication, and respond to or interact with the communication.

Primary means of communication that a smartwatch should enhance include:
    • Phone calls
    • SMS
    • Email
    • Calendar

A Smartwatch allows you to get a glance at meta-data


Minimization of data should the the smartwatch apps goal. The watch has a smaller screen and different input. Therefore it should initially present meta-data and not full data. Allow the user to dig in where appropriate but make that the exception to the rule and not the rule itself.

For example, email meta data should be from, subject, and first line or two. I know the date since it came from a notification, and it was to me or I wouldn't be reading it (even if I was BCC'd the primary purpose is that I read it, and thus it is to me). I should be able to decide from the meta-data whether I want to dig deeper into the full data.

A Smartwatch is context aware


Watch wearers typically wear their watches all the time. The watch should be able to determine the wearers current activity and respond in kind. If I'm moving the watch should be able to determine the difference between walking, running, biking, or traveling by car or plane. If I'm still the watch should be able to determine (using enviornmental and other factors like my calendar) whether I'm asleep, in a meeting, on the couch, or at a movie.

Watch app makers should be able to ask what the context of the user is so that the user is able to interact with the watch in the most appropriate fashion for their current activity. For instance, if I'm biking I should be interacting with the watch primarily through voice and audio playback.

A Smartwatch has a long battery life and is water proof


Watches are cumbersome to put on and take off. Therefore a smartwatch should optimize for not being taken off. This means that it needs to have a long enough battery life that having to charge it is the exception to the rule and not the rule itself. This also means that the watch needs to be waterproof in order to allow me to take a shower or go swimming.

A Smartwatch enables fitness tracking


For a smartwatch to enable fitness tracking it needs to be able to determine the following:
  • Current Activity (walking biking running swimming)
  • stair counts
  • step counts
  • elevation gain/loss
  • heart rate
  • calorie count
  • pace
  • sleep tracking

Monday, August 10, 2015

Pebble Time: My (incomplete) Review

About a month ago I received my Pebble Time. I missed getting in on the Kickstarter campaign but I was able to take advantage of the pre-release ordering through Best Buy. I ordered early enough that I got my Pebble Time the first week of July.

My review of the Pebble Time isn't going to be a standard review. Mostly because I don't think that most people or tech publications really know what a smart watch is supposed to do. Most of the reviews that I read talk about them as if they're gadgets that serve a purpose on their own. I've got some thoughts on that which I plan to publish here in the next couple of weeks.  But at the core I've decided that a smart watch is a companion.

In this post I'm only going to go over what I like about the watch. I'll go over what makes the watch a good companion. Honestly there's not much I don't like about the watch. All it's flaws have been covered elsewhere on the interwebs.

The Watch ... As a Watch


A watch serves two main purposes for me. First, it needs to tell me the time. Second, I need to be able to set an alarm. The Pebble Time, like the Pebble before it, is actually pretty awesome with regards to telling time in that you can install custom watch faces. I've downloaded several different ones and I would say that about every week or so I like that I have the option to use a different watch face. It actually makes the watch feel new for a day or two with a new face.

The Pebble Time also does a good job in the alarm category. In fact it's alarms are better than most watches because of the flexibility. It's similar to the alarm clock on a smart phone in that you can set multiple alarms up and have them set for a single occurrence, every day, weekdays, weekends, or a custom set of days during the week. The alarm vibrates and has been good enough to wake me up in the mornings.

Timeline


I didn't own the original Pebble so I don't know what the OS (operating system) was like. But the Pebble Time OS is very intuitive. There's four buttons on a Pebble Time; one on the left side and three on the right. The one on the left is the back/home button. The top right button will go up in a list or if you're on the home screen will take you into your timeline starting at the most recent past. The bottom right button will go down in a list or if you're on the home screen will take you into your timeline starting with the most recent current or future events. The middle right button allows you to select something in a list and when pressed on the home screen takes you to the list of apps on your phone.

As of right now I don't have a lot of stuff on my timeline but the stuff that's there is useful. I have my calendar, ESPN and weather on my timeline. The calendar is probably the most helpful as I don't think I realized how often I pull my phone out of my pocket just to look at my calendar. I probably save myself from pulling out my phone one or two dozen times a day. That in and of itself is probably worth the price of the watch.

Voice Responses


The Pebble Time comes with a microphone so that you can respond to emails and text messages right from your watch. This is hands down the feature of the watch I love the most. I HATE making phone calls so I mostly stick with text messaging people. I probably use voice dictation to respond to 20 text messages a day.

There's one huge miss with voice on the watch though. You can only respond to messages with the mic, you can't initiate text messages or emails with it. Email would be more difficult but I don't actually understand why initiating a text isn't there on day one. The functionality to get a list of contacts, get the cell number for a particular selected contact, and then send an SMS on Android and iOS isn't that difficult. So it surprises me that their Pebble app didn't add this functionality so that they could implement it on the watch.

The accuracy of the speech to text translation is scary good sometimes and hilarious at other times. For me, I've found that it has a direct correlation to how loud I'm talking. When in the office I try to whisper and the mic doesn't like that at all.  I've found that if I talk at a normal volume with my normal cadence there are just a handful of words it confuses. For some reason every time I say "and" it thinks I'm saying "end". I'm wondering if there's some Pacific Northwest accent I've got that's throwing it off. At the end of the day it's accurate enough that I still use it 10 - 20 times a day.

The App Store


I'll start by saying that there are a lot of apps for the Pebble. So many in fact that it highlights how awful the Pebble app store app is. The search is truly terrible, often not returning apps that contain the search term in their name. There are only a couple of categories that you can browse and they're not very helpful in my opinion. Most of the apps I use I found by either word of mouth or by spending an hour trolling through the app lists looking for something interesting.

I've found that there are about a dozen apps I've downloaded that I use daily/weekly. Cards for Pebble is a great app because it let's me see stock quotes and weather very easily. Pebble Music is the best music app I've tried so far. It's companion app isn't free but it's worth the money. I can initiate and control music playback entirely from the watch which is awesome as I'm a bike commuter and I often have my phone in my bag and am wearing bluetooth head phones (in one ear of course). Pebble Movies is great for getting movie listings and showtimes. CatchOneBus has been great when I'm riding the bus instead of biking. Other useful apps include Timer+, ESPN, Misfit, and Ventoo Bike Computer.

Battery Life


The battery is awesome. I regularly get 5 days out of a full charge. Which means if I charge it Sunday evening I'm good for the week. When I'm using it for development I get less battery life because I'm using it constantly for hours upon end. A note of caution, some watch faces with heavy animation will kill your battery quicker. I think it's key to find a watch face that looks great but isn't updating every second.

Would I Recommend It?


If you're looking for a companion to your phone that allows you to pull it out of their pocket less often then this is the watch for you. If you're looking to replace your phone with a watch then you're going to be sorely disappointed with every smart watch on the market.

Monday, March 2, 2015

Android WebView: HTML5 Video and Rotation

In the last few posts I've shown you have to add a Web View to your Android application. Web View's are useful for showing external content without leaving your app, adding dynamic content to your app allowing change without an app update, or simply display legal pages (terms of service, end user license agreements, etc) directly from your site.

I wanted to end this brief series talking about HTML5 video and rotation. There are some gotchas that it's important to be aware of in these areas that I've come across over the past few years.

Stopping Video


In some versions of Android the audio of video that was playing in a Web View will continue to play in the background even after exiting the Fragment or Activity that is hosting the web view. This appears to be a bug where onPause isn't being called on the web view in those versions of Android.

Handling this isn't simply a matter or calling onPause explicitly on the Web View if your app is targeting devices running Gingerbread or below. This is because those versions of Android don't have a public onPause method on the Web View.

The easiest way to handle this in a platform version independent way is to call the Web View's onPause method via reflection.

Class.forName("android.webkit.WebView")
         .getMethod("onPause", (Class[])null)
         .invoke(yourWebView, (Object[])null);

Playing Arbitrary HTML5 Videos


Web video players are very very fragile when it comes to playing arbitrary HTML5 video. Sites that host HTML5 video often rely on nuances that are present in some browsers or javascript engines but not in others. You may find that some HTML5 video just will not play in your embedded Web View.

Playing HTML5 Video Using A Native Video Player


While it is possible to create a custom WebChromeClient to intercept HTML5 video and play it using a native video player I would not recommend this. There are several problems with this approach that are very difficult to overcome.

  • You need to intercept ALL media playback events from javascript and handle them natively.
  • Handling all video related javascript events can only be accomplished by injecting javascript in the page or having direct access to the javascript engine.
  • Some pages dynamically load videos on the page which means you have to be monitoring the DOM for changes.

Rotation


Depending on how you handle rotation in your Activities and Fragments you may be accidentally reloading web pages on rotation. This happens when the call to loadUrl is made during the Activity or Fragment setup, since the rotation workflow causes the Activity or Fragment to be recreated.

This can be made even worse when the user had navigated several links deep into a web page. When the app is rotated and the Url is reloaded the user will find themselves back on the initial web page and not where they expect.

Handling rotation with a WebView can be done by:

Monday, February 23, 2015

Android WebView: Interacting with your apps Java from the Web Page.

In my previous post, Android WebView: Interacting with the Web Page from Java, I showed you how to call javascript functions from your native Java code. I also briefly mentioned that on Android KitKat and above you have the option of receiving the result directly from the javascript call using the evaluateJavascript method. This is an easy way to handle any result that may have been returned from the javascript call.

The evaluateJavascript method is very helpful but does not handle all use cases in which you may need to execute native Java code initiated by your web page. One use case would be handling web events (like a button click) or for returning data from a method on Android Jelly Bean or below.

One word of warning before we begin, allowing an HTML page to execute native Java code is inherently insecure. Web pages are isolated from the app they're running in by design. Creating a bridge between the web page and the app means that javascript on the page may have the ability to do anything on the device for which the app that's hosting the web page has permissions for. It is extremely important that you keep this in mind when creating the bridge from the web page to your app.

There are three main things that you need to do in order to call native Java methods from javascript:

  • Create a javascript interface object which serves as the bridge between javascript and Java.
  • Add the interface to the Web View so that any page that it hosts can use the bridge.
  • Update your javascript to use the bridge.

Creating a Javascript Interface Object


In this very basic class I am declaring a listener interface which I have the caller pass in via the constructor. This is one way to make sure that you only allow javascript to interact with your app in the way you expect. By using a well defined interface you are making it more difficult for malicious javascript to execute arbitrary code.

To make a method visible to javascript the method needs to be decorated with the @JavascriptInterface attribute.

Notice that I'm calling the listener back using a handler and runnable. This is because the bridge class methods aren't executing on the main thread. We need to execute the listener callback on the main thread.

public class WebViewNativeBridge
{
    public interface OnSomeChangeListener {
        public void onSomeChange(String text);
    } 
    private OnSomeChangeListener listener = null;
    public WebViewNativeBridge(OnSomeChangeListener listener) {
        this.listener = listener;
    } 
    @JavascriptInterface @SuppressWarnings("unused")
    public void someChangeFromWeb(String text)
    {
        final String newText = text; 
        // This code is not executed in the UI thread
        // so we must force it to happen
        new Handler(Looper.getMainLooper()).post(new Runnable()
        {
            @Override
            public void run()
            {
                if(listener != null)
                    listener.onSomeChange(newText);
            }
        });
    }
}

Add The Javascript Bridge Object To Your WebView


The way you wire up your native javascript bridge is to add the javascript interface to your Web View using the addJavascriptInterface method. This method takes in two parameters, your bridge object and the key word you want to use to access the bridge from within javascript.

WebViewNativeBridge.OnSomeChangeListener listener
                                                        = this.getOnSomeChangeListener();
WebViewNativeBridge bridge = new WebViewNativeBridge(listener);
browser.addJavascriptInterface(bridge, "NativeBridge");

I created the bridge object with a custom listener I created:

private WebViewNativeBridge.OnSomeChangeListener getOnSomeChangeListener() {
    return new WebViewNativeBridge.OnSomeChangeListener() {
        public void onSomeChange(String val) {
            // do something with the value passed in
        }
    };
}

Update The Javascript To Use The Native Bridge Object


The last step is to call into your native Java code from javascript. Using the NativeBridge object we registered with the Web View we can call the someChangeFromWeb method from our javascript. Note, the name of the object you use in javascript has to be exactly the same as the second parameter you passed into the addJavascriptInterface method.
function notifyNativeOfChange() {
    NativeBridge.someChangeFromWeb("some change");
}

Monday, February 16, 2015

Android WebView: Interacting with the Web Page from Java.

In my previous post, Android WebView: Displaying web content in your app, I explained the basic's of setting up a WebView to display web content in your app. In this post I'm going to explain how you can interact with the web page you're displaying.

It's not a good idea to try to mess with web page content for pages that you don't control because those pages can change and those changes may break your app or provide a poor user experience in your app. But there are cases when you do want to interact with web pages that you control the content of.

One example is if you provide content in your app via a web page whose content structure changes often. Using a web browser to display this data in your app provides the benefit to your users of being able to update that content without updating the app itself.

The way to accomplish this while providing a good user experience that's less likely to break when you update your web site is to define a Javascript contract for interacting with the web content. This contract is made up of a specific set of Javascript functions that your page offers the app which provides a known outcome. This allows the web page to change and add additional content while adhering to the predefined contract and gracefully failing the web page when the contract needs to change.

Executing Javascript


There are two main options you have to execute Javascript within a web page from native Java code. The option you choose will depend on what the minimum version of Android is that you are targeting in your app.

Executing Javascript on Android 4.4+


The preferred option is to use the evaluateJavascript method in the WebView class. This allows you to send some Javascript to the browser to execute and handle any expected result in native code. The  evaluateJavascript method was introduced in Android 4.4 with a new WebView class based on the Chromium.  Here's an example of how to scroll a web page to the top using this new method:

private void scrollWebPageToTop_KitKatOrAbove(WebView browser)
{
    String myScript = "window.scrollTo(0,0);";
    browser.evaluateJavascript(myScript, new ValueCallback<String>() {
        @Override
        public void onReceiveValue(String result) {
            // Do something with the result
           Log.d("JSEXAMPLE", result);
        }
    });
}

Executing Javascript on Android 4.3 or below


If you are targeting Android 4.3 or below you'll need to use the WebView's loadUrl method to execute your Javascript. Prefacing the your URL with "javascript:" lets the WebView know that you would like to execute Javascript in the context of the current page being displayed.

private void scrollWebPageToTop_JellyBeanOrBelow(WebView browser)
{
    String myScript = "javascript:window.scrollTo(0,0);";
    browser.loadUrl(myScript);
}

The first thing you may have noticed is that you don't get the ability to deal with the result of the Javascript method you just executed. If you are executing Javascript from which you expect a result you'll need to use the WebView's addJavascriptInterface method and explicitly call the callback you've registered. Next week's post will go into greater detail on using this method to allow your web page to trigger native Java code to deal with the result of your Javascript code execution.

For more information on the new WebView based on the Chromium browser see the Migrating to WebView in Android 4.4 page in the Web Apps section of the Android developer guides.

Monday, February 9, 2015

Android WebView: Displaying web content in your app

The web has become a ubiquitous medium for information and communication. We use the web to disseminate information about ourselves, our companies, and our thoughts. We consume our news and other information about the world on the web. We purchase our groceries and gifts on the web. We even communicate with our friends, families, and loved ones over the web.

When we think about the web for native mobile applications we tend to think of the web as a data transfer mechanism. We talk about the cloud, JSON, XML, and RSS; all of which are important but often we need the web as more than the backbone of information. There are a couple of reasons you may want to display web content in your app instead of a native view:

  • You want to show external content without leaving your app.
  • You have dynamic content whose structure changes often.
  • You want to display legal pages (terms of service, end user license agreements, etc) directly from your site.

Today I'm going to start a series on the Android WebView. In this post we'll talk about the basic's of setting up a WebView to display web content in your app. In other posts I'll explain how to interact with web content via Javascript from your app as well as interacting with your app from Javascript. Finally I'll go over some gotchas or hurdles that it's important to be aware of when using a web view in your app.

Initializing the WebView


Initializing the WebView should be done when your Activity or Fragment is created. While the default settings the WebView uses will probably work for most basic web pages, you can be more explicit about how your WebView acts. You specify your preferences using the WebSettings object on your WebView.

Obtaining your WebView's settings is as simple as calling getSettings() on the WebView object. Here's an example of changing some of the default WebView settings:

private void initializeWebView(Bundle savedInstanceState)
{
    /** make sure you have a WebView in your layout with the id: browser **/
    WebView browser = (WebView)getView().findViewById(R.id.browser);

    // I like my scroll bars inside the content
    browser.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);

    // allow web pages to execute Javascript.
    browser.getSettings().setJavaScriptEnabled(true);

    // if you know your web pages use a different encoding than utf-8 you can change it.
    browser.getSettings().setDefaultTextEncodingName("utf-8");

    // Start with content zoomed all the way out.
    browser.getSettings().setLoadWithOverviewMode(true);

    // Allow zooming with the default zoom controls
    browser.getSettings().setSupportZoom(true);
    browser.getSettings().setBuiltInZoomControls(true);

    // Change how the web page is laid out.
    // See WebSettings.LayoutAlgorithm for more detail.
    browser.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);

    // Allow the HTML viewport meta tag to specify the width
    browser.getSettings().setUseWideViewPort(true);

    // Used to handle external interactions. More details below.
    browser.setWebChromeClient(this.getWebChromeClient());

    // Used to handle page events. More details below.
    browser.setWebViewClient(this.getWebViewClient());
}

Loading A Web Page


Once you have a WebView added to your app displaying web pages is as simple as calling loadUrl on the WebView. For example:

private void loadWebPage(String url)
{
    /** make sure you have a WebView in your layout with the id: browser **/
    WebView browser = (WebView)getView().findViewById(R.id.browser);
    browser.loadUrl(url);
}

Loading HTML Directly


Loading web data into your page doesn't have to be done using a remote URL. If you already have the HTML content you'd like to display (possibly from an RSS feed or offline content), you can do so very easily by first converting the content to Base64 and then creating a data URI to display the content. For example, you can generate a url for HTML content you already have that the WebView can display as follows:

private String getUrlFromHTML(String html)
{
    String base64Data = Base64.encodeToString(html.getBytes(), Base64.DEFAULT);
    return String.format("data:text/html;charset=utf-8;base64,%s", base64Data);
}

Advanced Browser Features


Handling Web Page Life-cycle and Other Events


There are several events associated with the web page life-cycle. For example there are events associated with web page loading being started/stopped. There are also events for web page errors as well as a login request from the web page. These web page events are handled via a WebViewClient. Your browser should, at a minimum, provide feedback to the user when a web page starts loading and when it stops loading. Here's an example of how you can handle those events:

protected WebViewClient getWebViewClient()
{
    return new WebViewClient()
    {
        @Override
        public void onPageStarted(WebView view, String url, Bitmap favicon)
        {
            /** provide feedback using a spinner or some other dialog to  
the user to let them know that you're doing some work **/
        }

        @Override
        public void onPageFinished(WebView view, String url)
        {

            /** hide the feedback that was provided to the user **/
        }
    };
}


External Browser Interactions


One of the more nuanced aspects of displaying web content in your app is when the web content wants to do something external to the current browser. In order to interact with the browser for some of these more advanced features you need to use a WebChromeClient. For example, playing back HTML5 video fullscreen is done using the WebChromeClient's onShowCustomView method. Other examples of advanced external browser interactions are reporting debug messages to the Javascript console, opening/closing additional browser windows, displaying messages from Javascript, and etc.

It's important to note that there is only one WebChromeClient. Because of this you'll likely want to subclass WebChromeClient if you plan to provide many of these advanced features in your app. Here's an example of creating a WebChromeClient that can update the progress indicator of a Fragments Activity.

private WebChromeClient getWebChromeClient()
{
    return new WebChromeClient()
    {
        public void onProgressChanged(WebView view, int progress)
        {
            if(getActivity() != null)
            {
                getActivity().setProgress(progress * 100);
            }
        }
    };
}

For a more detailed example of integrating a basic web browser into your app, see the BrowserFragment in my open source RSS Reader Android app.

Monday, January 26, 2015

Why is it so difficult to watch live T.V. on a mobile device

The other day I was out to brunch with my wife and I was trying to see if I could catch some of the Dallas vs Greenbay playoff game. We're both pretty big football fans and were very curious about who the Seahawks would be facing in the NFC Conference Championship. I was extremely frustrated by the fact that I could not find the game anywhere.

I understand that there are one off ways to watch football in particular. But what about T.V. in general. I'm curious as to why there's not an equivalent to a cable subscription for mobile. Yes, most cable providers offer some form of internet T.V. but there's two problems.

  • You're required to have a home cable subscription.
  • Not all programs are available, specifically most live sporting events.

It feels like this is an opportunity for big cable (or an upstart trying to break in) to move forward towards the actual future of television. Right now big cable is desperately trying to hold on to the past because it's lucrative. But as Netflix and Amazon Prime Instant Video are showing, people's television consumption habits are changing.

Here's an outline of what what I believe would be wildly successful in regards to the future of television.

  • Provide a streaming cable subscription that DOES NOT require a home account.
    • Make the service work with Android, Fire, iOS, and Windows mobile.
  • Offer ALL the same channels (Network TV, ESPN, Comedy Central, etc) available with classic cable.
  • Offer an upgrade to access the last full season of shows.
    • This will allow people to catch up on missed episodes.
    • This will allow people that prefer to binge watch shows to do so when the season is over.
  • Offer an upgrade to remove the commercials. I'd pay an extra $10 a month for 0 commercials.
  • Partner with Amazon and Netflix to license their original content.
  • Allow people to pay with Amazon payments. (full disclosure... I work for Amazon).
    • Chances are people already have an Amazon account with a credit card on file.
    • Allows a technology company that understands security to handle the important payment processing.
Something like this is bound to happen sooner or later. Why not now?

Monday, January 19, 2015

Don't hate your users or potential users

The other day I had an incredibly frustrating experience with iCloud that was 100% avoidable. I'm not an iCloud user but a buddy of mine is and when he had his first kid he sent out an iCloud link to some pictures. Being out and about at the time I tried to open the link on my phone using the mobile Firefox browser. I was greeted with this not-so-friendly message:



Something I've gotten used to doing on my mobile when web pages act weird is to check the "request desktop version" button and guess what? The page loaded fine but I was left with more animosity towards iCloud and Apple for blatantly breaking an experience that should "just work".

So what can we learn from this?
  • Your customers aren't just the people who bought your products. 
  • Provide "correct" details when things aren't going to work.

Your customers aren't just the people who bought your products

Most businesses view their customers as the who buy their products or use their services. But in a day and age where social media has become a primary mechanism for sharing information your customer base is now extended to those that your customers share their content with.

When you're building a product that allows users to share information via a URL you are essentially making a contract with that user that their content will be accessible by those they share it with on a modern web browser.

In the iCloud case I'm baffled at why Apple would explicitly block Android users. I understand they're trying to get people to use their ecosystem, but they also need to face the reality that people in their ecosystem interact with people on the outside.

How do I know they're explicitly blocking Android users? I took a look at the difference between the request headers that Firefox sends normally on Android and what it sends when you check the "request desktop site". The big difference was in Firefox's user agent string. 

Mobile OnlyMobile: Mozilla/5.0 (Android; Mobile; rv:34.0) Gecko/34.0 Firefox/34.0
With Request Desktop Site CheckedMozilla/5.0 (X11; Linux x86_64; rv:34.0) Gecko/20100101 Firefox/34.0

While I don't think this is the case for iCloud, there may be completely valid reasons they're blocking Android mobile user specifically. For instance they may not have the resources, bandwidth, or experience to test their site fully on enough variations of the Android platform to support the experience. Even if that was the case, which I do not think it is, they need to be more explicit with their users and those trying to use the site that there are known and likely use cases where the experience is not going to be available.

This leads me to the next thing we can learn from this experience.

Provide "correct" details when things aren't going to work

The web is often looked at as an "easier" way to provide a unified experience with broader reach than providing a native experience on multiple platforms. But with that broad reach and unified experience comes more responsibility to provide that reach.

The above screenshot says that my browser isn't supported. That's not true, they don't support my operating system. I tried to open that link in Chrome for Android and the normal Android browser and all three of them had the same experience.

The screenshot also says that I may be able to use iCloud with my phones mail, calendar, and contacts. They know that I'm trying to access a photo album. How is what they're suggesting as a possible fix applicable to my situation? 

Providing your customers (or potential customers) a great experience means being helpful when things don't go as they expect. It's not enough to make the experience work as you expect, you need to make the experience work as THEY expect.

Monday, December 29, 2014

The (Unfortunate) Reality Of Open Source

I've been thinking a lot about open source software recently. It's amazing how much of our lives are run by open source software, how little people are aware of, and the downstream affect it has on our lives. Are you an Android, iPhone, or Kindle user? Do you use Gmail or Google search? If so, you're using open source software. In fact Amazon, Google, Samsung, Apple, Netflix, TiVo, Comcast, Wikipedia, WordPress, and many many many more companies have built core parts of their businesses using open source technologies.

In a way, you not being aware of open source software in your everyday life is a win. It means that open source is overcoming some of the usability problems that plagued the community through most of the 90's and 2000's. The fact that Android is the dominant mobile operating system means that people are learning to build usable graphical user interfaces (GUI) on top of Linux. It also means that by using the products we love we're making products that we aren't even aware of better simply because they're based on the same open source technologies.

Yet in another way, you not being aware of open source software in your everyday life is bad. When people aren't aware of the possibilities of what they're using they don't know what to expect or even demand. Simply using open source software doesn't mean that your favorite app or website or service is interoperable with anything else. In order to make software work together we also need open standards.

Here's an analogy to make this more tangible. What if in order for you to fill up your car with gas you could only go to your manufacturers shop and you couldn't pump gas from a competing manufacturer because the nozzles were different or the cars used different fuel? That would make it very difficult for you because your manufacturers decisions and processes would have a much bigger effect on your everyday life. You'd have a much more rigid schedule for getting gas. But because there are agreed upon ways that gas pumps work you are able to use any number of competing services. Your manufacturer is free to innovate and make their car better but you have flexibility in how you consume and use gas. The competitors have to earn your business.

Now let's use a technology example. Do you use dropbox? What would happen if tomorrow you wanted to move everything in your dropbox to Amazon, Apple, Microsoft or Google's cloud? Would you be able to have that stuff migrated automatically just by choosing a new service? Or would you have to do all the work yourself?  There are open standards for folder sharing like WebDav. There's no reason that you couldn't just enter your other services credentials into Dropbox and click a button to migrate your data other than them not wanting to make that easy for you.

Right now in the software world it's like we're driving around in cars that all use gas but because the nozzles are all different shapes and sizes we can only get gas from one manufacturer. As a society we seem to have accepted owning the burden of interoperability in our technology. So we choose the services that cause the least amount of pain rather than choosing the services that cause the most amount of joy.

It doesn't have to be this way. If we demanded that our data be migratable and we were easily able to move from Brand A to Brand B then both would be forced to earn our trust. Both would have to work hard to provide us with services and support that delight us. Both would have to listen to us when we say that using their products are difficult or unintuitive.

How do you make these demands you may be asking? With your wallet. Buy software and services from companies that use open source and open standards. Don't buy from those that don't. Talk to your friends and family about this problem and educate them. Ask your friend who is computer literate to help you chose products that will remove some of the burden from you.

Monday, December 22, 2014

2014: The year for streaming media

Personally, 2014 was a good year for me. I switched jobs mid-year and have been working on enjoyable projects with really really smart people. My wife and I traveled to Budapest and Prague, which was absolutely amazing as well as San Francisco, Portland, Iowa, and Richmond (VA). We got to see one of my lovely cousins get married and meet the new 2nd cousins. We spent Thanksgiving with my sister-in-law and got to met our new and very beautiful niece Eleanor. I've kept up with blogging once a week which, honestly, I wasn't sure I would actually be able to do. But the best thing that happened in 2014 for me was that my wife and I are found out we are expecting our first child. I'm both terrified and excited at the thought of being a dad, but I can probably predict the future and tell you that having a child is going to be the highlight of 2015.

2014 was an interesting year for technology and there were a lot of new products announced and released. But from the perspective of what affects my everyday life most I've been interested in what's been happening in the streaming media segment.

While Apple has had a media streaming solution for several years their competitors (other than Roku) have struggled to come up with any interesting alternatives. There have been may solutions over the years for home media centers like Windows Media Center, XMBC on a Raspberry Pi (Raspmbc) or XBox to say the least. But in my opinion, none of these solutions other than Apple TV or Roku have had the teeth to take off in the mass market. And, unfortunately, without mass adoption the quality of content is not very good.

2014 saw two entries into the streaming market which I believe will help drive competition and innovation in a category of software and hardware which has been pretty stagnant. Amazon announced it's Fire TV, which was received very well and Google announced the Nexus Player which seems to be a legitimate reboot of their efforts to get into the streaming TV market. Amazon also announced the Fire TV Stick.

We've got a Roku, Chromecast and Fire TV Stick in our household. We don't have an Apple TV because we don't own any Apple mobile products and Apple is a pretty closed ecosystem and I really really really don't want to encourage that. That's not to say they don't make beautiful products because they do.

The Roku is a simple and easy to use device with a pretty decent interface. But to me something just doesn't feel right. When I use the Roku I put down my everyday tech (phone/tablet) and pick up their remote and use their software. I'm aware that I'm using something that isn't customized to me. Their interface doesn't feel like "home" to me like my own tech does. The Roku feels slightly foreign. Also, streaming media from my phone or tablet directly to the Roku has been clunky at best.

Up until December the Chromecast has been my favorite device of 2014. I really like how easy it is to use. I can just open the YouTube app on my phone and start queuing up clips. Or I can open Netflix, WatchESPN, Comedy Central or HBO Go, find what I want to watch and then just fling it to my T.V. Really my biggest complaint with the Chromecast is that the app developers have to integrate it directly into their mobile app and apps have been slow to adopt this additional API.

I say that the Chromecast has been my favorite device up until December. That's because I got my Fire TV Stick this month and so far it's been pretty incredible. The interface is great and very intuitive if you're already familiar with Amazon Instant Video. I downloaded the free remote app and the voice search is very very accurate and fast. One of the things I really liked about the Roku is that the apps are right on the device and the Fire TV Stick followed a similar path. It has all of the apps I use today with my Chromecast except HBO Go. But I can even stream HBO Go directly from my phone to the Fire TV Stick using display mirroring.

While display mirroring is a battery drain on your phone/tablet it's pretty useful for me. Traditionally I've used it to stream to the Chromecast when an app hasn't implemented the Chromecast API. It's been nice being able to use it with the Fire TV Stick out of the box. I think my biggest complaint with it is that I have to enable it on the Fire TV each time I want to mirror.

I have big hopes that 2015 will bring more innovation to streaming media.

Monday, December 1, 2014

Why is getting your data on a new phone so much work?

Recently my wife upgraded her phone after finishing her two year contract with our mobile provider. She transitioned between phones on the same carrier made by the same manufacturer.

For some context, my wife's primary email comes from a standard IMAP server. She gets her calendars from a standard CalDAV enabled server. She gets her contacts from a standard CardDAV enabled server. She downloads her music and files from a standard WebDAV server. She installs her applications from two app stores, Google Play and Amazon Appstore.

It took us over 4 hours to transition everything from her old phone to her new phone. Why in 2014 is this still so cumbersome?

What transferred/setup without any work

  • The applications installed from the Google Play Store.
  • GMail.
  • Home screen background image.

What we had to manually transfer/setup

  • Applications that were NOT installed from Google Play Store.
  • IMAP email.
  • CalDAV calendars.
  • CardDAV contacts.
  • Lock screen background image.
  • Phone PIN.
  • Phone home screens
    • Widgets.
    • Application Shortcuts.
  • Alarms.
  • Application Settings.
  • Her camera pictures.
  • Her downloaded music.
  • Her downloaded files.
  • 3rd party application data (Instagram, Facebook, Pintrest, and etc).

There's nothing on the second list that couldn't have been automatically transferred. I'm not sure what the right solution is to this problem, but I do know this shouldn't be as much work as it was.

As technologists we put way too much on the shoulders of our users. We expect them to do the heavy lifting for things that we can do easily through software. I think part of this problem is that we, as an industry, don't think enough about the import/export scenarios for our mobile products. But that's sad given that most people are on 2 year contracts with their carriers and they have an opportunity to upgrade their phones if they can financially afford it.

In my opinion this is real opportunity lost.

Monday, October 6, 2014

Starting From Scratch: Android - Creating A Release Build

This week we're finishing the Starting From Scratch series with a look at how to create a release build of our app. I'll show you how to create a release key for your app, secure your release key via encryption, and how to integrate the automatic decryption (and clean up) of your encrypted key during the normal Android Ant build process

Creating a release key for your app


Your key is what identifies your app as being published by you. This is what ensures that only official versions of your app can be released. It's ABSOLUTELY important that noone gets access to your key. DO NOT commit this keystore to your source control repo as is.

Create a release keystore
$ keytool -genkey -v -keystore my.release.keystore -alias myalias -keyalg RSA -keysize 2048 -validity 10000

Securing your release key


Not having the keystore in source control doesn't create a pit of success as you have to manage your key separately from your project. Furthermore, anyone with access to your key can sign an app as you. In order to safely create a pit of success we're going to encrypt our keystore and delete the original so it's not lying around anywhere for someone to abuse.

To encrypt the keystore we'll use openssl and DES3 encryption.
$ openssl des3 -salt -in my.release.keystore -out my.release.keystore.encrypted
$ rm my.release.keystore
The next thing you want to do is put your encrypted keystore in the provisioning directory.
$ mkdir provisioning
$ mv my.release.keystore.encrypted provisioning/

Integrating into the Android Ant build process


Now that we have a key that can be used to sign our applicaiton and we've secured that key from unauthorized access we now need to integrate into the standard Android Ant build process.

The first thing we need to do is create an Ant target that will decrypt the keystore. We also want to create a target to clean up the decrypted keystore immediately after the build. Note that the -decrypt-keystore target supports both prompting the builder for the password or getting the password from an Ant property in the case of an automated release build.

Here's what our encryption.xml file looks like. Create this file in the same directory as your projects build.xml file.
<?xml version="1.0" encoding="UTF-8"?>
<project name="encryption">
    <target name="-decrypt-keystore" depends="" if="isRelease">
        <echo>Decrypting keystore</echo>
        <if>
            <condition>
                <and>
                    <isset property="key.store.password"/>
                </and>
            </condition>
            <then>
                <exec executable="openssl">
                    <arg value="des3"/>
                    <arg value="-d"/>
                    <arg value="-salt"/>
                    <arg value="-in"/>
                    <arg value="provisioning/${assets.keystore}"/>
                    <arg value="-out"/>
                    <arg value="provisioning/release.keystore"/>
                    <arg value="-pass"/>
                    <arg value="pass:${key.store.password}"/>
                </exec>
            </then>
            <else>
                <exec executable="openssl">
                    <arg value="des3"/>
                    <arg value="-d"/>
                    <arg value="-salt"/>
                    <arg value="-in"/>
                    <arg value="provisioning/${assets.keystore}"/>
                    <arg value="-out"/>
                    <arg value="provisioning/release.keystore"/>
                </exec>
            </else>
        </if>
    </target>
    <target name="-clean-keystore" depends="">
        <echo>Cleaning up decrypted keystore</echo>
        <delete file="provisioning/release.keystore"/>
    </target>
</project>
In order to support automated release builds we need to add a few Ant properties to our projects local.properties file. DO NOT CHECK THIS IN TO YOUR SOURCE CONTROL. This file should be restricted as much as possible because it contains the password used to decrypt your keystore. You do not have to put your password in this file. If you don't you'll be prompted to enter your password during the release build.
assets.keystore=my.release.keystore.encrypted
key.store=provisioning/release.keystorekey.alias=myalias
key.alias.password=PASSWORD_YOU_USED_WHEN_CREATING_YOUR_KEYSTORE

key.store.password=PASSWORD_YOU_USED_WHEN_CREATING_YOUR_KEYSTORE
The last thing we need to do is wire up the decryption and cleanup of our key into the existing Android Ant build process. To do this we'll implement the -pre-build, -pre-clean, and -post-build build targets in our custom_rules.xml file. Note that we only want our decryption to happen during a release build. So we're going to define an isRelease property. Our -decrypt-keystore target checks for this property before execution.
<?xml version="1.0" encoding="UTF-8"?><project name="custom_rules">
      <condition property="isRelease"><contains string="${ant.project.invoked-targets}" substring="release"/></condition>

    <target name="-pre-build">
        <antcall target="-decrypt-keystore" />

    </target>

    <target name="-pre-clean" depends="-clean-keystore"></target>
    <target name="-post-build" depends="-clean-keystore"></target></project>
Finally, the last thing we need to do is update our projects build.xml file to include our encryption.xml and custom_rules.xml files.  Add the following two import statements ABOVE the existing ant/build.xml import. For example:
<import file="encryption.xml" optional="false" />
<import file="custom_rules.xml" optional="false" />

<import file="${sdk.dir}/tools/ant/build.xml" />
You can now build a signed release version of your app on the command line with the following command.
$ ant release


Monday, September 29, 2014

Starting From Scratch: Android - Action Bar

This week we're continuing the Starting From Scratch series. Today we're going to take a look at the Action Bar. The action bar UI was first defined in Android 3.x (API 11+). It provided a consistent navigation paradigm for applications as well as a convenient way to present context specific calls to action (like sharing, search, and etc).

Because Android only supported the Action Bar APIs on Android 11+ an open source project called ActionBarSherlock provided support for earlier versions of Android.  ActionBarSherlock is a great project and very useful and eventually Android added a similar Appcompat project to it's v7 support library. While I'm a big fan of ActionBarSherlock I'm going to be using Android's Appcompat project in my example code for consistency.

Creating a project that has an Action Bar.


Setup the support appcompat project as a library. Note, for purposes of this series i'm using android-14 as my target version. You'll want this to match whatever Android version your app is targeting.
$ cd /path/to/android-sdk/extras/android/support/v7/appcompat
$ android update project -p . -t android-14
Make sure that the project.properties file has the android library project property set.
android.library=true
Now that we have our pre-requisites complete let's create an app that will use an Action Bar.
$ cd ~
$ mkdir MyActionBarProject
$ cd MyActionBarProject
$ cp /path/to/android-sdk/extras/android/support/v4/android-support-v4.jar libs/
$ android create project -t android-14 -k com.example. myactionbarproject -p . -n MyActionBarProject -a MainActivity
In order to actually use the support library you need to add a reference to the Android Support Appcompat Library in your project.properties. Note that the path is relative NOT absolute.
android.library.reference.1=relative/path/to/android-sdk/extras/android/support/v7/appcompat
At this point you've got your application setup so that it can use an Action Bar, but it's not using one yet. In order to actually use an Action Bar you'll need to update your Android manifest to use a theme with an Action Bar. This can be done by using either Theme.AppCompat.Light or Theme.AppCompat (dark theme) as the theme of your activity in your AndroidManifest.xml. For example:
<activity android:name="MainActivity"
               android:label="@string/app_name"
               android:theme="@style/Theme.AppCompat.Light">
You'll also want to hint Android that you're going to support an older version of Android. You can do that by adding the following in your AndroidManifest.xml
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="19" />
At this point you can compile your app and install it on your test device or emulator and you'll see an Action Bar. Make sure you either have an emulator running or a device attached in debug mode and run:
$ ant clean && ant debug && ant installd

Action Bar Menu


One of the great things about the Action Bar is that you can provide menu items directly in the action bar. This allows you to provide context specific menu options in a place that is convenient and easy for your users to access.

If you are planning on supporting the Action Bar via the Support Library then the first thing to do is to update your Activity to extend ActionBarActivity instead of Activity.

Android provides an Action Bar Icon Pack which you can download and use in your application. Simply copy the resources from the theme your app is using into your res/drawable folders. For this example we'll use the refresh icon.

$ mkdir ./res/drawable-xxhdpi
$ cp /path/to/icons/holo_light/01_core_refresh/drawable-xxhdpi/ic_action_refresh.png ./res/drawable-xxhdpi/
$ cp /path/to/icons/holo_light/01_core_refresh/drawable-xhdpi/ic_action_refresh.png ./res/drawable-xhdpi/
$ cp /path/to/icons/holo_light/01_core_refresh/drawable-hdpi/ic_action_refresh.png ./res/drawable-hdpi/
$ cp /path/to/icons/holo_light/01_core_refresh/drawable-mdpi/ic_action_refresh.png ./res/drawable-mdpi/

The first thing you need to do to add a menu in your Action Bar is to define the menu layout. In my layout I'll be referencing an icon. Here's what our main_activity_menu.xml looks like.

$ mkdir res/menu
$ vim res/menu/main_activity_menu.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:myapp="http://schemas.android.com/apk/res-auto">
    <item android:id="@+id/menu_refresh"
              android:icon="@drawable/ic_action_refresh"
              android:showAsAction="ifRoom"
              myapp:showAsAction="ifRoom" />
</menu>

Now that we've defined the Action Bar menu we need to inflate it into our Action Bar. This is done via the Activity's onCreateOptionsMenu event.
@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.main_activity_menu, menu);
              return super.onCreateOptionsMenu(menu);
          }

When a user selects a menu item the onOptionsItemSelected method will be called. This method is called regardless of which Menu Item was selected. So you'll need to check the id of the item before you handle the action. Here's an example of handling our refresh action.
@Override
public boolean onOptionsItemSelected(MenuItem item) {
    if(item.getItemId() == R.id.menu_refresh) {
        Toast.makeText(this, "Refresh selected", Toast.LENGTH_LONG).show();
        return true;
    }

    return super.onOptionsItemSelected(item);
}
At this point you can compile your app and install it on your test device or emulator and you'll see an Action Bar with a refresh menu button. Selecting the refresh button will display message saying Refresh select.
$ ant clean && ant debug && ant installd

Monday, September 22, 2014

Starting From Scratch: Android - Fragments

This week we're continuing the Starting From Scratch series. Today we're going to take a look at Android Fragments. I'll discuss what a Fragment is, the Fragment life-cycle, creating a Fragments options menu, and finally I'll give a few Fragment tips I've found along the way.

What Is A Fragment

Simply put, a Fragment is a way to encapsulate a piece of your applications UI and UI interactions into a reusable set of resources and code. A Fragment is not tied to any particular Activity, but instead can be used within many Activities.

One example of where I've used this modularity in my applications is with Lists. Lists are common in mobile applications and often only differ by what they show. Using Fragments it would be easy to encapsulate creating, displaying, and interaction with a list into a set of reusable code and resources. You could then display multiple lists in a variety of places throughout your app using the same code and resources.

Fragment Life-cycle

The Fragment life-cycle is very similar to the Activity life-cycle we've already gone through in this series. You still have creating, starting, resuming, pausing, stopping, and destroying life-cycle events. But in addition to those you have life-cycle events that are associated with creating your Fragments view and attaching/detaching from an Activity. I'm not going to go through every Fragment life-cycle method but instead will call out two key life-cycle differences from Activities.

The first big difference is in creating the Fragments view. In an Activity this is done via the onCreate method. In a Fragment this is done in the onCreateView method. The onCreateView method is expected to return the view to use with this Fragment. Creating this view is pretty simple, just inflate your Fragments layout using the LayoutInflator passes into this method.

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
    return inflater.inflate(R.layout.my_first_fragment_layout, container, false);
}

The next difference comes with the onActivityCreated method. The main purpose of this method is to allow the Fragment to restore state. There are two places that state can be stored. The first is internally within the Fragment via a Bundle. The second is in any arguments that were passed into the Fragment by it's composing Activity.

The internal saved state is passed into the onActivityCreated method in the form of a Bundle. The state that was passed in via arguments can be retrieved via a call to the getArguments method. It's important to restore Fragment state using the correct source of information. For instance, if you get the initial state from the Arguments but then update that state and save it in your Fragments Bundle then it's important to have some logic that determines the correct place to get the saved state from.

@Override
public void onActivityCreated(Bundle savedInstanceState)
{
    super.onActivityCreated(savedInstanceState);
    Bundle arguments = this.getArguments();
    if(arguments != null && arguments.size() > 0)
    {
        // set any state that was passed in
        // via the arguements bundle
        this.someVariable = arguments.getString("SomeVariable");
    }
}

Creating Fragments Options Menu

Creating menu items for your Fragment is a four step process. The fist step is declaring your Fragments options menu in an XML file. The second step is declaring that your Fragment has an options menu. The third step is inflating your options menu layout. The last step is handling users selecting an option in your Fragments menu.

First, create res/menu/my_first_fragment_menu.xml. The important thing to call out here is that each menu item needs to have an id. This id is important because there is one method that is called when the user selects a menu item. So we need a way to differentiate the desired action the user wishes to perform.

<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/my_menu_item" android:showAsAction="ifRoom"/>
</menu>

Declaring that your Fragment has an options menu is done via a call to setHasOptionsMenu. This call should be made in the Fragments default constructor.

public MyFirstAndroidFragment()
{
    setHasOptionsMenu(true);
}

Inflating your Fragments options menu is done by overriding the onCreateOptionsMenu method. This is done by passing the MenuInflater's inflate method the id of the menu XML file you created. If you want to use an ActionProvider in your Fragment, like the ShareActionProvider, this is the right time to set that provider up.

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
{
    inflater.inflate(R.menu.my_first_fragment_menu, menu);
    // this is a good time to setup a share action provider
}

Finally, handling the selection of a menu option is done by overriding the onOptionsItemSelected method. This method is called when any menu item is selected. It's a good idea to encapsulate the menu item action into it's own method and just call that method when the item has been selected. It's important to remember to return true in the onOptionsItemSelected method if you did handle the menu item selection.

@Override
public boolean onOptionsItemSelected(MenuItem item)
{
    if (item.getItemId() == R.id.my_menu_item)
    {
        this.handleMyMenuItem();
        return true;
    }
    return false;
}

Fragment Tips

Retaining

One thing that often causes people to stumble is putting a video, web browser, or any other stateful object inside a Fragment. The reason is that when the device changes orientation the Activity (and it's child Fragments) are torn down and recreated. This causes problems when the user isn't expecting it. One way to solve this problem is to tell Android to retain the Fragment instance across Activity re-creation. This is done via a call to setRetainInstance. This call should be made in the Fragments default constructor.

public MyFirstAndroidFragment()
{
    setRetainInstance(true);
}

Cross Fragment Coordination

Cross Fragment coordination is done by declaring an interface in your Fragment for any events you want to allow others Fragments to respond to. The Activities that compose your fragment will implement your Fragments interface and can then dispatch messages to other Fragments that it is composing. This allows you to keep your concerns separated correctly by NOT tightly coupling your Fragment with any other Fragment. It's okay to tightly couple your Activity with your Fragment because your Activity is composing that Fragment.