Oct 22, 2012

Open sourcing my Android App. Go, grab it, and do something good with it.

If you've been following my blog, you already know that some time ago I was working on an Android application. Its main goal was satisfying a personal need: I was super-frustrated that there was nothing for the Android platform that let me take a quick glance at the stock market. Personally I'm a stock junkie, and I love to check stocks 20 times a day. Not having a simple app to do it was really painful.

So I did it myself. Just like I wanted. When I published it, I called it "Stocktile", but internally "Longhorn" was its name.

And God knows I loved the result! It was quick, easy to use, and beautiful for my taste. I made it rock-solid and took special care of every detail I could imagine. Since day one I used it 20 times a day until I bought an iPhone 5... iOS comes with a pre-installed stock application so I won't be working on an iOS version of mine any time soon.

So now I'm not going to use Longhorn anymore. The application is already in Google Play and the Amazon Marketplace, but I felt like I wasn't done with it. I really like it and I'd like a better life for it.

So that's why I decided to open source it. Click below to go to my GitHub account and grab it:

Download Stocktile (Longhorn) source code here.

Yup, you can download the code, re-compile it, change it, and do whatever you want with it (well, at least whatever the Apache License version 2 let's you do with it).

To give you a preview of what cool stuff you'll find:
  • Drag and drop (for Android 4.0+)
  • Search autocomplete 
  • Talk to search
  • Automatic market updates
  • Battery optimization techniques (Read more about this here)
  • Support all the way up from Android 2.2
  • A very nice UI layout
  • Gracefully handling device orientations 
  • And way more stuff... 
For me it was a real pleasure to work on this app. I hope you can use the code to make something amazing. If that's the case, let me know. I'd love to hear about it.

Aug 7, 2012

Where do I want my phone to be in 5 years?

In the past 3 years my phone went from a dumb-down Nokia I-can't-remember-the-model to an iPhone 4S and a Galaxy Nexus running Jelly Bean (Nokia N95, Blackberry World Edition, iPhone 3GS, and Nexus One along the way). This goes fast, so before the future gets here, here are the 10 main things I want my phone to be / do in the next 5 years.
  1. Crazy good battery life. I'm talking about days of battery life, not hours. I just don't want to think about charging my phone anymore. I want wireless charging so just by getting closer to a power station my phone will charge itself without me even noticing.

  2. Incredible lighter and thiner. They still feel bulky sometimes. A lot of progress has been made in this regard but so much can still be done. Imagine a phone that fits in your wallet (and I don't know why I would want to put it there, but you get the point).

  3. 100% voice controlled. I hate buttons and complicated interfaces. Voice is the natural replacement of these. Humans communicate using voice and we have seen already pretty compelling voice interfaces (read Siri and Google Now). They are good, but not great. I want to tell whatever I want to my phone and I want it to do it. No questions asked.

  4. Full-featured payment mechanism. No more plastic. At all. Bye. I want to pay everything and everywhere with my phone. It's with me all the time and I shouldn't have to carry anything else in my pockets. Google Wallet et. al. are doing a good job, but we are still not quite there.

  5. Again, no plastic. Following on the previous point, I don't want any plastic, including my ID. Any form of identification should be built into our phones. If I open my wallet right now, I can't find anything that can't be easily discarded in favor of an electronic replacement.

  6. Key replacement. My car keys? Nope, my phone should open my car. My home keys? Same thing. Office? Mailbox? Yup. A bluetooth-type key-less system would be awesome: get close enough and the "thing" will open without having you to say / do anything.

  7. An electronic identity. I didn't have a better name for this one. Basically, I'd like the phone to identify me everywhere I go (if I choose to, of course). At the bank the clerk will know about me without asking 20 questions. The cops won't have to ask for your "registration and license" anymore, and we'll save ton of time just by electronically answering / filling every form we have to. All this by just pressing a button.

  8. No more paper. Plane tickets? Nope. Movie tickets? Concert tickets? Invitations? Checks? Receipts? You name it. Answer is no for all of them. Everything that requieres you to carry something should be replaced. The phone should take care. Magically.

  9. Better shopping experience. Imagine going to a store, scanning (or touching?) the products you want and have someone instantly packing them at the front door for you to pick them up when you are done. No payment processing, no carrying carts around. Or real targeted ads showing only what I care about (yes, as seen on Minority Report).

  10. Easily reemplazable and secure. Since our life will be so tightly coupled to our phone, we'll have to make them easily reemplazable and bullet-proof secure. Loosing your phone shouldn't be a pain other than having to buy a new one.
Think about it. We are not that far. I can even say we are almost there. Is more about the entire ecosystem evolving than the phones themselves.

Aug 6, 2012

If you make your product interfaces obvious, everyone wins.

Key-less interfaces. This is where technology is going, and I love it. However, some people still don't get it and they still make horribly complex interfaces. The notion that a product is better if it has 100+ options is totally wrong, and the market its proving it. Make a simpler product, and you will destroy your competition.
  • My laundry machine has a speed-deal type control that I still don't know how to properly use after 2 years washing my clothes. I don't care either. It's fixed in one of the steps and that's what I've been using. Remove that thing and add one "start" button. I'm sold.

  • I bought a blender yesterday with 2 possible options: start and pulse. I paid $30+ bucks over other models with 20 non-sense buttons. I love it and I will gladly buy it over again next time. Do you know how much pain it removes from my life? I know how to use it without having to read any manual!

  • My iPhone has one button. Love it. My Galaxy Nexus has three. Why? I have to think every time before pressing one of them. I usually screw it by pressing the wrong one (I might be an idiot). I've never being confused with my iPhone, but I need to complain about my Android. Some phone makers re-order these buttons, so every phone could be different adding more confusion. I've also seen some models that make things worse by turning off the backlit making the buttons invisible to the user! You have to guess-press them to realize that you fucked it up and pressed the wrong one. Nice touch!

  • I used to hate those Windows keyboards with ~200 keys. Somebody thought that it was nice to add a key for every Windows pre-installed application on your hard drive. So you had a key for pulling up Notepad, one for IE, one for the calculator, and a huge painful bulky and confusing keyboard sitting on your desk. Not for me.

  • My former Honda Accord panel looked like a piano. You can easily get killed yourself trying to find the right button while driving. My new Audi A4 has 20 more features and uses less than half of the buttons. Needless to say how better it is and how much I like it.

  • Guess why I moved from Microsoft Office to Google Docs... Well, at least that's one of the reasons.
The list goes on and on.

Looking around I can find hundreds of examples where technology is getting simpler for users. Every button (option) counts and adds certain stress to the person that needs to use it. Complexity it's never welcomed and people recognize it by going more and more with the easiest product. I consider myself a pretty technical person, and nothing bugs me more than having to deal with a manual before using something. Make it simple and obvious and you'll save tons of paper!

I think that's the future. Key-less interfaces where products are easier to use and work without requiring so many configuration and input instructions. Look at Siri on iOS and the new Google Now on Jelly Bean. We are moving there very quickly.

And for you... please go ahead and rethink whatever is what you're doing. It probably can be simpler. It probably doesn't need so many options. Make us (and yourself) a favor and trim it down until you get rid of every "needed" instruction.

If you make it obvious, everyone wins.

Aug 2, 2012

Access to protected and default (package) members in Java

Regarding visibility, protected is one of the most misunderstood modifiers in Java. It's always tricky to get it right, and when combined with the default (or package) visibility, it can become really confusing. Here is my attempt to cover every single possibility of these two access levels. Let's start with some code:
package com.svpino.snippets.white;

public class Parent {
    void method0() {
        System.out.println("Parent::method0");
    }
 
    protected void method1() {
        System.out.println("Parent::method1");
    }
}
Here we are defining two methods, method0 has default visibility (since no modifier is specified), and method1 is protected. So far, so good. Let's see now the other class:
package com.svpino.snippets.white;

public class Child1 extends Parent {
 
    public void method2() {
        System.out.println("Child1::method2");
        method0();
        method1();
    }
}
The class Child1 is inheriting from Parent and its located on the same package. This means, that lines 7 and 8 are perfectly valid. Line 7 can be executed because method0 has package visibility and Child1 is located on the same com.svpino.snippets.white package as Parent. Line 8 is valid because method1 is protected so Child1 can access it through inheritance. But, what if we try these methods from a different package? Let's see:
package com.svpino.snippets.black;

import com.svpino.snippets.white.Parent;

public class Child2 extends Parent {
 
    public void method2() {
        System.out.println("Child2::method2");
        method0(); // Error! This doesn't compile!
        method1();
    }
}
Now in this case, Line 9 is not valid anymore. We can't call method0 because we are on a different package than Parent. However, Line 10 is still valid since we are accessing a protected member through inheritance. Now let's spice things up a little bit:
package com.svpino.snippets.white;

import com.svpino.snippets.black.Child2;

public class Neighbor1 {

    public void method3() {
        Parent parent = new Parent();
        parent.method0();
        parent.method1();

        Child1 child1 = new Child1();
        child1.method0();
        child1.method1();
        child1.method2();

        Child2 child2 = new Child2();
        child2.method0(); // Error! This doesn't compile!
        ((Parent) child2).method0();
        child2.method1();
        child2.method2();
    }
}
Line 9 has no problem to compile, since we are on the same package than Parent. Line 10 also compiles (and this is interesting) since the protected visibility in Java works the same as the package visibility. Keep that in mind all the time: A protected member has essentially package-level or default access to all classes except for subclasses. Moving to the next block, Line 13 and Line 14 will also compile for the same reasons Line 9 and 10 compile respectively. Line 14 is calling a public method from Child1 so it will also compile. Nothing fancy here. Let's see what happens in the last block. Line 18 doesn't compile because we are trying to access method0 through the Child2 class. If you go back to that class you'll see it's defined in a different package than Neighbor1, and because method0 has default visibility, it won't be visible at this point. However, casting child2 to a Parent will let us access method0. The compiler will validate that we do have access to everything defined with package visibility in the Parent class, so Line 19 won't be a problem. Now, think about Line 20. It will compile, but why? It was kind of shocking for me discovering this. Supposedly, method1 should behave exactly as method0 since package visibility acts like default visibility, however there's another subtle difference between them. I had to check the Java specification to find the key:
6.6.1 (...) if the member or constructor is declared protected, then access is permitted only when one of the following is true: * Access to the member or constructor occurs from within the package containing the class in which the protected member or constructor is declared. (...)
Now compare that to the following about default access:
6.6.1 (...) Otherwise, we say there is default access, which is permitted only when the access occurs from within the package in which the type is declared.(...)
So the difference is that a protected member will be always accessible when the access occurs from within the package where the class containing the member was declared, meanwhile default access is only possible when the access occurs from the same package where the type (the type of the object we are using to access the member) was declared. In our case, the member method1 was declared in the package com.svpino.snippets.white, and we are accessing it from Neighbor1 which happens to be on the same package, so the access will be allowed even though we are using Child2 (the type) to do it. It sounds complicated, and I had to read it more than once myself to make sense out of it. But if you pay careful attention to the wording, you'll see it. Finally, it's not surprising than Line 21 will compile since method2 has public access. The code for our last test is as follows:
package com.svpino.snippets.black;

import com.svpino.snippets.white.Child1;
import com.svpino.snippets.white.Parent;

public class Neighbor2 {

    public void method3() {
        Parent parent = new Parent();
        parent.method0(); // Error! This doesn't compile!
        parent.method1(); // Error! This doesn't compile!

        Child1 child1 = new Child1();
        child1.method0(); // Error! This doesn't compile!
        child1.method1(); // Error! This doesn't compile!
        child1.method2();

        Child2 child2 = new Child2();
        child2.method0(); // Error! This doesn't compile!
        ((Parent) child2).method0(); // Error! This doesn't compile!
        child2.method1(); // Error! This doesn't compile!
        child2.method2();
    }
}
Lines 10 and 11 will not compile since we are accessing them from outside the package where they were defined. Same thing happens with lines 14 and 15, meanwhile line 16 is a simple public access. Now, line 19 is trying to access a default member that was defined in a different package, so it won't compile. The cast in line 20 won't compile because Parent still belongs to a different package, and look how interesting is line 21. We can't access method1 even though it is inherited by Child2 from Parent. The reason is because once a subclass outside the package inherits a protected member, that member becomes private to any code outside the subclass with the exception of subclasses of the subclass. Yes, another tongue-twister, so you might want to read it again. Basically, as soon as method1 is inherited, its visibility becomes private to any outsider (like our Neighbor2 class). So that's about it. Impressive how tricky these modifiers can be, specially the protected access. It took some time to wrap my head around all these concepts, but now I have it pretty clear. Hope it helps.

Jul 26, 2012

If you really want to get something done, you have to stand up and make it happen

Not surprising, but still I'm amazed how easy is to loose track of things and let time pass by without accomplishing anything. Too bad the default setting for people is to drag their feet on everything they don't like to do (or care for).

Probably you have to deal with this every day. Maybe you are on the lazy side most of the time (I've been there too!). That's the moment when somebody else will show up and embarrass you by getting done 90 percent of the work in the time it took you to get through the initial 10 percent.

If you want to make a difference, go and start showing everyone else how it should be done. It's so easy to do it differently, to do it better. It's so worth it and so well compensated. Still, very few people seem to care.

There hasn't been a better time to shine. Go!

Jul 16, 2012

Connectivity and Battery Life in Android. Making them play nicely.

Last week I announced Stocktile, a stock market application to make my life easier and enjoy checking on my tickers even more. At the end of that post, I wrote the biggest lesson I learnt while I was developing the app:
Main challenge: The web is always online. Mobile applications are not. It's an entire layer of complexity added to our applications.
Indeed. Not having a reliable and fast Internet connection all the time is something traditional developers are not used to. Mobile imposes this restriction, and we have to deal with it if we want to make our applications solid options for consumers.

But it's not just about connectivity

There's another challenge though that we don't need to worry about outside the mobile ecosystem: battery life. In such small devices, there's a very small battery and dozen of applications competing to use a pice of it. Unfortunately, technology is still not where we can stop worrying about how much power our application sucks, so we need to use it carefully or face the anger of our users.

So connectivity and battery life are the main two players. They need to play seamlessly and safe. Unfortunately is difficult to get it right, so a lot of developers don't pay attention to these details. That's mainly why tons of Android apps are battery and data-plans killers. They just don't care.

On the Android developers site there are two awesome articles explaining the ins and outs of developing and application that properly takes care of connectivity and battery life. I'm not going to reproduce here everything said, but I strongly recommend any developer to read those training sessions. I want to focus instead in the code that powers Stocktile, and how I combined different techniques to make the application a good citizen in Android-land.

Setting our rules

First of all, take into account that every application is different and requires a different level of thinking. What works for me, might not work for a news application or any other category. However, the approach is very similar, so I hope to help more than one out there.

Stocktile downloads the market information for every stock ticker added to your dashboard. You can have 3 tickers, or hundreds of them. Not matter how many, you surely want them to display the latest market information available all the time. That's the point, and that was my goal. However, because I'm feeding my app using the Yahoo! Finance API, there's going to be a minimum delay of 15 minutes between market updates. That's the first constraint I have to play with: no updates will be requested if the latest one was less than 15 minutes ago.

But what happens if the user is not using the application? Do we want to keep updating it? Remember that every update will impact the user's data-plan and the battery life of the device, so the answer is no. If the users don't care about our app, why bothering them with updates they are not going to see? So that's the second rule: the application will be updated only if users are actively using it.

However, downloading information from the network takes time, and we don't want our users waiting from fresh data to come when they open our app. So we should rethink our updates policies: what about updating automatically even if the user is not actively using the app but only when the device is plugged to a power source and connected to a WiFi network? That sounds better!

There are two more use cases that I had to take care of: when the user adds a new ticker to the dashboard, we want to update it immediately. And, if during an update an error occurs, we need to re-schedule the next update as soon as possible. However, it might be the case that something is wrong with the server so a continuous update will further damage the battery life of the device. To solve this, we need to employ a back-off pattern as explained on the Android training classes.

So let's put everything together:
  1. The minimun time between updates will be 15 minutes.
  2. If the user is not using the application, new information is going to be downloaded only when the device is plugged to a power source and running on a WiFi network.
  3. If the user adds a new ticker, an update will be performed immediately only for that ticker.
  4. If an error occurs while updating, a new back-off update will be scheduled as soon as possible.

The BatteryBroadcastReceiver

Let's take a look to all the code pieces that make up these guidelines. Let's start with our second rule above. For that one, we need to create two different BroadcastReceivers, one for monitoring the battery and the other to monitoring connectivity changes. Look at the following lines in our manifest file:
<receiver
    android:name=".BatteryBroadcastReceiver"
    android:exported="false" >
    <intent-filter>
        <action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
        <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
    </intent-filter>
</receiver>
Then our Java class:
public class BatteryBroadcastReceiver extends BroadcastReceiver {

    private final static String LOG_TAG = BatteryBroadcastReceiver.class.getName();

    @Override
    public void onReceive(Context context, Intent intent) {
        if (isTheBatteryPluggedIn(context)) {
            if (areWeUsingWiFi(context)) {
                Log.d(LOG_TAG, "On WiFi and charging, let's update");

                Intent intent = new Intent(context, MarketCollectionReceiver.class);
                intent.setAction(Constants.SCHEDULE_BACKGROUND);
                PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 
                    PendingIntent.FLAG_UPDATE_CURRENT);

                ((AlarmManager) context.getSystemService(Context.ALARM_SERVICE))
                    .setInexactRepeating(AlarmManager.RTC_WAKEUP,
                        System.currentTimeMillis(),
                        AlarmManager.INTERVAL_HALF_HOUR,
                        pendingIntent);
            }
   
            ComponentName componentName = new ComponentName(
                context,
                ConnectivityBroadcastReceiver.class);          
            context.getPackageManager()
                .setComponentEnabledSetting(componentName,
                 PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
                 PackageManager.DONT_KILL_APP);
        }
        else {
            Log.d(LOG_TAG, "Not charging anymore. Hold off in any new updates");
                
            Intent intent = new Intent(context, MarketCollectionReceiver.class);
            intent.setAction(Constants.SCHEDULE_BACKGROUND);
            PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 
                PendingIntent.FLAG_UPDATE_CURRENT);

            ((AlarmManager) context.getSystemService(Context.ALARM_SERVICE))
                .cancel(pendingIntent);
   
            SharedPreferences pref = context.getSharedPreferences(
                Constants.PREFERENCES, Context.MODE_PRIVATE);

            boolean areWeWaitingForConnectivity = 
                pref.getBoolean(Constants.PREFERENCE_STATUS_WAITING_FOR_CONNECTIVITY, false);
   
            if (!areWeWaitingForConnectivity) {
                ComponentName componentName = new ComponentName(context, 
                    ConnectivityBroadcastReceiver.class);
                context.getPackageManager().setComponentEnabledSetting(
                   componentName, 
                   PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 
                   PackageManager.DONT_KILL_APP);
            }
        }
    }
}
Looks more complicated than what really is. Let's go step by step. The very first line asks whether the battery is plugged in or not. Here is that code:
public boolean isTheBatteryPluggedIn(Context context) {
    Intent batteryIntent = context.getApplicationContext()
        .registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
 
    int plugged = batteryIntent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
    return plugged == BatteryManager.BATTERY_PLUGGED_AC 
        || plugged == BatteryManager.BATTERY_PLUGGED_USB;
}
Then we ask whether we are using WiFi:
public static boolean areWeUsingWiFi(Context context) {
    ConnectivityManager connectivityManager = 
        (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
  
    NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();

    boolean isConnected = networkInfo != null ? networkInfo.isConnected() : false;
    boolean isWiFi = isConnected 
        ? networkInfo.getType() == ConnectivityManager.TYPE_WIFI : false;

    return isWiFi;
}
In case both conditions are true updates are safe, so we can schedule a regular update using the AlarmManager service. Note how these updates will be performed every 30 minutes since the user is not necessarily using the application.

After the update is scheduled, another important thing is done in the code: a BroadcastReceiver for connectivity updates is enabled. Why? Because we need to be notified as soon as the user is not longer connected to the WiFi network. Since connectivity updates are very frequent, we don't want to keep the BroadcastReceiver enabled all the time, so we enable and disable it as needed.

Now, what happens if the battery is not plugged in? Well, we need to hold off in any new updates, so we go ahead and cancel any pending schedule. Also, if we are not actively waiting for the device to go back online, we can also disable the connectivity BroadcastReceiver until we get back plugged in to the wall. Pay special attention to the areWeWaitingForConnectivity flag. This is going to be true in case an error occurred while updating and we are waiting for the connectivity to come back to re-run the update.

The ConnectivityBroadcastReceiver

First, we need to declare our receiver in the manifest file. Note how we want it to be disabled by default. Our code will take care of enabling it only when necessary:
 <receiver
    android:name=".receivers.ConnectivityBroadcastReceiver"
    android:enabled="false"
    android:exported="false" >
    <intent-filter>
        <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
        </intent-filter>
</receiver>
Then the Java class:
public class ConnectivityBroadcastReceiver extends BroadcastReceiver {
    private final static String LOG_TAG = ConnectivityBroadcastReceiver.class.getName();

    @Override
    public void onReceive(Context context, Intent intent) {
        if (areWeOnline(context)) {
            SharedPreferences pref = context.getSharedPreferences(Constants.PREFERENCES, 
                Context.MODE_PRIVATE);
            boolean wereWeWaitingForConnectivity = sharedPreferences.getBoolean(
                Constants.PREFERENCE_STATUS_WAITING_FOR_CONNECTIVITY, false);

            if (wereWeWaitingForConnectivity) {
                Log.d(LOG_TAG, "Connectivity was just re-established, let's update.");
            
                DataProvider.startStockQuoteCollectorService(context, null);

                ComponentName componentName = new ComponentName(
                    context, ConnectivityBroadcastReceiver.class);
                context.getPackageManager().setComponentEnabledSetting(
                   componentName, 
                   PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 
                   PackageManager.DONT_KILL_APP);
            }       
            else {
                if (areWeUsingWiFi(context)) {
                    if (isTheBatteryPluggedIn(context)) {
                        Log.d(LOG_TAG, "We are on WiFi and charging, so let's update");

                        Intent intent = new Intent(context, MarketCollectionReceiver.class);
                        intent.setAction(Constants.SCHEDULE_BACKGROUND);
                        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, 
                            intent, 
                            PendingIntent.FLAG_UPDATE_CURRENT);

                        ((AlarmManager) context.getSystemService(Context.ALARM_SERVICE))
                            .setInexactRepeating(AlarmManager.RTC_WAKEUP,
                                System.currentTimeMillis(),
                                AlarmManager.INTERVAL_HALF_HOUR,
                                pendingIntent);
                   }
               }
               else {
                   Log.d(LOG_TAG, "We aren't using WiFi, so don't update");

                   Intent intent = new Intent(context, MarketCollectionReceiver.class);
                   intent.setAction(Constants.SCHEDULE_BACKGROUND);
                   PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, 
                       intent, 
                       PendingIntent.FLAG_UPDATE_CURRENT);

                   ((AlarmManager) context.getSystemService(Context.ALARM_SERVICE))
                       .cancel(pendingIntent);
               }
           }
       }
       else {
           Log.d(LOG_TAG, "We aren't online so don't update.");

           Intent intent = new Intent(context, MarketCollectionReceiver.class);
           intent.setAction(Constants.SCHEDULE_BACKGROUND);
           PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 
               PendingIntent.FLAG_UPDATE_CURRENT);

           ((AlarmManager) context.getSystemService(Context.ALARM_SERVICE))
               .cancel(pendingIntent);
        }
    }
}
Very similar to the BatteryBroadcastReceiver, but in this case we start asking if we are online (not only on a WiFi, but with any Internet access):
public boolean areWeOnline(Context context) {
    ConnectivityManager connectivityManager = (ConnectivityManager)     
        context.getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();

    return networkInfo != null ? networkInfo.isConnected() : false;
}
If we are, and we were waiting for the connectivity to come back (an error occurred while updating), then we run an update immediately and disable the BroadcastReceiver. If we don't need an immediate update, then we ask whether the battery is plugged in, and we are on a WiFi connection. From there, everything is pretty much the same to the BatteryBroadcastReceiver.

Receiving alarms and firing updates

These two BroadcastReceivers work pretty well in conjunction to schedule our background updates, but there's another piece they need to fire up the updates: another BroadcastReceiver that's going to be kicked off by the scheduled alarms. Round of applauses for our MarketCollectionReceiver:
public class MarketCollectionReceiver extends BroadcastReceiver {
    private final static String LOG_TAG = MarketCollectionReceiver.class.getName();

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(Constants.SCHEDULE_RETRY)) {
            SharedPreferences pref = context.getSharedPreferences(
                Constants.PREFERENCES, Context.MODE_PRIVATE);

            boolean retrying = pref.getBoolean(
                Constants.PREFERENCE_COLLECTOR_RETRYING, false);

            if (retrying) {
                Log.d(LOG_TAG, "Retrying update...");
                DataProvider.startStockQuoteCollectorService(context, null);
            }
            else {
                Log.d(LOG_TAG, "Update was already successfully performed");
            }
        }
        else if (intent.getAction().equals(Constants.SCHEDULE_BACKGROUND)) {
            if (isTheBatteryPluggedIn(context) && areWeUsingWiFi(context)) {
                Log.d(LOG_TAG, "Performing background update...");
                DataProvider.startStockQuoteCollectorService(context, null);
            }
            else {
                Log.d(LOG_TAG, "We are not longer able to perform background updates");
                Intent intent = new Intent(context, MarketCollectionReceiver.class);
                intent.setAction(Constants.SCHEDULE_BACKGROUND);
                PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, 
                    intent, 
                    PendingIntent.FLAG_UPDATE_CURRENT);

                ((AlarmManager) context.getSystemService(Context.ALARM_SERVICE))
                    .cancel(pendingIntent);
            }
        }
        else if (intent.getAction().equals(Constants.SCHEDULE_AUTOMATIC)) {
            Log.d(LOG_TAG, "Performing automatic update");
            DataProvider.startStockQuoteCollectorService(context, null);
        }
    }
}
If you take a closer look to the code above, you'll notice that this BroadcastReceiver is using the action of the passed Intent to determine what kind of update was scheduled. In our Battery and Connectivity broadcast receivers we only use "BACKGROUND" updates, but the application uses three different types: BACKGROUND, RETRY, and AUTOMATIC. When the device is plugged in, and is on WiFi, we fire a BACKGROUND update. If an error occurs while updating, we fire a RETRY update. If the user is actively using the application or a new ticker is added, we fire an AUTOMATIC update. The rest of the code, should be self-explanatory.

Now, what about our back-off updates?

That happens when the update occurs. I'm not going to post the entire Service that takes care of the updates, but just the relevant sections:
...
int retries = sharedPreferences.getInt(Constants.PREFERENCE_COLLECTOR_RETRIES, 0);
...                 

if (retrying || wereWeWaitingForConnectivity || areWeUpdatingOnlyOneTicker 
    || (!areWeUpdatingOnlyOneTicker 
        && currentTime - lastUpdate > Constants.COLLECTOR_MIN_REFRESH_INTERVAL)) {
    try {
        update(...);
 Log.d(LOG_TAG, "Update was successfully completed");
 ...
    }
    catch (Exception e) {
        Log.e(LOG_TAG, "Update failed.", e);
    
 if (areWeOnline(this)) {
     Log.d(LOG_TAG, "Scheduling an alarm for retrying the update...");

            retries++;

            Editor editor = sharedPreferences.edit();
            editor.putBoolean(Constants.PREFERENCE_COLLECTOR_RETRYING, true);
            editor.putInt(Constants.PREFERENCE_COLLECTOR_RETRIES, retries);
            editor.commit();

            long interval = Constants.COLLECTOR_MIN_RETRY_INTERVAL * retries;
            if (interval > Constants.COLLECTOR_MAX_REFRESH_INTERVAL) {
                interval = Constants.COLLECTOR_MAX_REFRESH_INTERVAL;
            }                     
  
            Intent intent = new Intent(context, MarketCollectionReceiver.class);
            intent.setAction(Constants.SCHEDULE_RETRY);
            PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, 
                intent, 
                PendingIntent.FLAG_UPDATE_CURRENT);
     
            ((AlarmManager) getSystemService(Context.ALARM_SERVICE))
                .set(AlarmManager.RTC, 
                    System.currentTimeMillis() + interval, 
                    pendingIntent);
        }
 else {
     Log.d(LOG_TAG, "We are not online.");
     
            Editor editor = sharedPreferences.edit();
     editor.putBoolean(
                Constants.PREFERENCE_STATUS_WAITING_FOR_CONNECTIVITY, true);
            editor.commit();
     
            ComponentName componentName = 
                new ComponentName(this, ConnectivityBroadcastReceiver.class);
            getPackageManager().setComponentEnabledSetting(
                componentName, 
                PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
                PackageManager.DONT_KILL_APP);
        }
    }
} 
First, note the retries variable. Every time an error occurs, we increment a counter and save it in our preferences. The next update will use this value to delay every update and avoid retries when the server is continuously failing. Note how in this case we set up the alarm with a RETRY action. In case we are online when the error occurs, then we set the flag WAITING_FOR_CONNECTIVITY and enable our ConnectivityBroadcastReceiver to get notified as soon as we are back online.

The other bit of code to pay attention is the flag areWeUpdatingOnlyOneTicker. Although I didn't post the section where this flag gets initialized, it means than the user added a new ticker to the dashboard and we need to run an update just for it.

It's a lot, I know

Yes, it gets tricky, verbose, and it's easy to loose track of every component. By reading back what I just wrote I'm realizing how painful is for developers to take care of so many details. Some day this will get done for us under the hood, or we won't need to worry anymore when technology gets to a point where battery and connectivity are not longer a concern. Unfortunately we are not there yet, and this topic is really important if we want to develop an application that doesn't kill our devices.

Read closely. Try for yourself, and ask if you get lost.

Jul 12, 2012

Stocktile sees the light

Finally! Stocktile is out!
After 4 weeks of long nights in front of my computer, Stocktile is live on Google Play in two different versions: Lite and HD. Mostly, I'm glad because now I get to use an application that does exactly what I want... nothing less, nothing more. If you want to see some screenshots, follow any of the links above.

What I've learnt has been huge. Lots and lots of things to share about the Android platform. Let's see what can I put together when I get my strength back from this past month.

Before going to bed, I do want to share the main lesson from these days. Copied from a tweet I posted 4 days ago:
Main challenge: The web is always online. Mobile applications are not. It's an entire layer of complexity added to our applications.
Think about it.