Brian's Blog
Brian's Blog

Fragmentation

Fragmentation is one of those words I tend to hear a lot lately.  People like to throw out fragmentation as a con against android because of the various OS versions.  Others use fragmentation to refer to that fact Android runs on many different physical devices with different screens and keyboard options.  Others might say fragmentation is what Amazon is doing by taking Android and spinning it into a closed environment for the Kindle Fire.  All of these definitions are good examples of fragmentation in Android but don’t really concern me that much.

My main issue with fragmentation is the fragmentation I did to the users of my Android app - Handicap: Golf Tracker.  Like many mobile developers I had dreams of making that great app that everyone wants and quiting my day job as the cash rolled in.  Well, that hasn’t happened yet.  As I watched my app sales dwindle I did the unthinkable and created a free version that relies on ad revenue.

This single change forced me to make a few compromises and create two versions of my code base.  Gradually I have been able to isolate the business logic in my app to a single code base but still have the two different UI code bases that house most of the display logic.  This has lead me to have a “lead” code base that I implement changes on first then migrate to the other code base as needed.

Over the past year I have been updating both code bases, adding features and making each app stand out.  This has lead to a steady stream of new users.  The new users of the free version has been great and actually makes me feel like my app has been a success.  

As I start the new year, I start to plan changes I want to implement in the next iteration of the app.  This now has me thinking of fragmentation again.  Which features should I make free and which features should I make available only to paid users.  Should I even make that differentiation at all?  Should both versions be identical when it comes to features? Where should I place my effort?  How do I want to further fragment my user base?

It is really annoying.  I wish I just had one user base to build for but I’m not willing to abandons either group.  My paid users are few but they at least paid for my work.  The free users are clearly the majority and are bringing me almost as much revenue as the paid users through ads.  I made my fragmented bed and now I have to sleep in it.  I just wish I had known then what I knew now and started with just the ad supported app.  

Early Galaxy Nexus Impression

My initial impressions of the Galaxy Nexus is that it's a large phone.  The 4.65" screen and 150 grams make this phone's size noticeably larger than my previous phone.  That said the phone does feel good in my hand and is beautiful.  I really like the grey side and back of the phone. The look and feel remind me a more of the Nexus One than the Nexus S.  The phone also feels less plasticy since the back is textured.  The phone does have a slight tear drop design that creates a little bump on the bottom. It might be a bit large for most people but the design is very appealing.

As appealing as the hardware in this phone is, the real innovation is in the software.  Android 4.0, code named Ice Cream Sandwich or ICS, is the latest version of the mobile OS.  The OS has been completely overhauled compared to the last phone version of the OS.  The UI innovations seen in the Android 3.0 for tablets, has finally come to phones.  In the past mobile phone manufactures have “skinned” Android devices to make them more user friendly.  These “skins” would have custom app launchers, widgets and lock screens that hid native Android operations in favor of the specific phone manufacturer.  

That should all change now with ICS because Google has finally built a quality competitor to these “skins”.  The new app drawer in ICS provides the same access to your apps as it did before but now allows you to add widgets to your home screens.  In previous versions of Android this was hidden away and not easy for most users.  Now you can preview the widget and simply drag it to the home screen of your choice.  

As in the Tablet version of Android, widgets are given a lot more flexibility in Ice Cream Sandwich.  Google has built in several nice widgets for ICS making it easier to see pictures, bookmarks and email.  These new widgets are also re-sizable now so you can customize your home screens to show the content you want to see.  

The largest change most people will notice with the Galaxy Nexus is that the physical buttons on the face of the phone have been replaced with software buttons.  The physical Home, Search, Back and Menu buttons are now gone and replaced with Back, Home and Tasks touch screen buttons.  The Back and Home buttons act as one would expect.  The Task button displays a list of your previously opened applications and allows you to easily switch to that application.  

Brace yourself - the Search and Menu buttons are now only available in applications that enable those features.  There is no more hidden menu options not visible on the screen.  If there is a menu, you’ll see the menu button on the screen.  Search is available on each home screen so you don’t have to hunt for it.  Some people are going to absolutely hate this change.  I’m not a fan that search is on every home screen.  This is a radical change that is for the best.

So far I am happy with the button change in Android.  I’m still getting used to the new task feature and the Home button is a little close to the space bar when typing but over time I expect I’ll get used to the change.  If you absolutely hate this design then I suggest you vote with your wallet.  Manufactures are going to be coming out with new phones running ICS and some way still have the physical buttons. You can always buy one of those phones.  Fragmentation equals freedom of choice.  Enjoy.

Overall I’ll have to see how the phone performs on calls, pictures and other items that come up.  I have been very happy with the phone so far.  I expect I’ll be very happy for a long time running the best Android phone available.

Quick Impressions of CQ

My current consulting project is working with Abode's CQ5 Web Content Management (WCM) tool for building content rich web applications.  The product is a collection of Apache open source (Sling, Jackrabbit, Felix) projects bundled together with a JCR implementation, a customized Eclipse IDE and enterprise management tools.  

The people that use CQ today build highly visible marketing applications where content changes are common.  CQ provides an authoring environment that makes it easy for non-technical resources to change out images, text and other content on a website.  The content is centrally managed in the CRX (Content Repository Extreme) and authors can edit pages in a pre-production environment.

From a pure J2EE developer experience, the framework feels like a combination of new and old technologies.  The developer creates JSP templates that define the layout of the web page.  There are JSP components that you can directly reference in the template or make available to the authors to drag and drop on a page.  CQ relies on a RESTful web application container.  CQ uses an OSGi console to add back end java services for handling post requests, accessing business logic and/or integration with external systems.  There is plenty of cool, non-mainstream technologies mixed in with traditional JSPs to provide an interesting product.

So far the framework has a lot of potential.  The administration tools are excellent for authors making it easy to edit content.  The custom Eclipse IDE has a lot to offer but doesn't support plugins.  I can see why this platform is very popular in marketing circles because of it's author friendly experience.  From a J2EE developer standpoint the framework may feel a little constraining and you may want to consider using only CRX for sites that require more dynamic content.

The Backup/Restore Dilemma

One of the changes I've wanted to put in place was the ability to backup a users data in the cloud. At first I was thinking of storing the entire data model in a NoSQL solution. I wasn't sure between Google's or Amazon's implementation I would adopt. I really didn't know much about either option. 

I think that maybe I should just store the XML data file I create today in one of the solutions available. I figured I'd need a unique I'd for each user to make it easier to identify their data.  The problem I've come up with since is that I'm not sure how to keep that unique ID as users move from device to device or reinstall to a device.

I'm tempted to start asking for use of their gmail account but I know that solution has flaws. What if people don't want to allow me access to their account? What if people don't care about their google account and just create a new one when they upgrade. I don't want to force people to provide an account to use my app.

What I've discovered is that there is a simple backup option in Android for users with 2.2 and up phones.  I've successfully generated a UUID for an install and backed it up with the app preferences.  This feature will be something I roll out on the paid version of the app.  

Analytics for Android, Revisited

In my previous post about Google Analytics I demonstrated how to use a parent class and inheritance.  Some applications will not be able to use inheritance so they need to use a helper class.  Here is my example of an example class.

public class AnalyticsHelper {
    static final String TAG = "AnalyticsHelper";
 
    GoogleAnalyticsTracker mTracker;
    private Context mApplicationContext;
 
    private static AnalyticsHelper sInstance;
 
    /**
     * Returns the global {@link AnalyticsUtils} singleton object, creating one
     * if necessary.
     */
    public static AnalyticsHelper getInstance(Context context) {
        if (sInstance == null && context != null) {
            sInstance = new AnalyticsHelper(context);
        }
 
        return sInstance;
    }
 
    private AnalyticsHelper(Context context) {
        if (context == null) {
            // This should only occur for the empty Analytics utils object.
            return;
        }
 
        mApplicationContext = context.getApplicationContext();
        mTracker = GoogleAnalyticsTracker.getInstance();
 
        mTracker.start("UA-CODE", mApplicationContext);
        //mTracker.setDebug(true);
        //mTracker.setDryRun(true);
 
        Log.d(TAG, "Initializing Analytics");
 
         
    }
 
    public void trackError(RuntimeException e, String action, String label, String errorMessage, Context context) {
        trackEvent("exception", action, label, 0);
        Toast.makeText(context, errorMessage, Toast.LENGTH_SHORT).show();
        throw e;
    }
 
    public void trackEvent(final String category, final String action, final String label, final int value) {
        new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... voids) {
                try {
                    mTracker.trackEvent(category, action, label, value);
                } catch (Exception e) {
                    Log.w(TAG, e.getMessage());
                }
                return null;
            }
        }.execute();
    }
 
    public void trackPageView(final String path) {
        new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... voids) {
                try {
                    mTracker.trackPageView(path);
                } catch (Exception e) {
                    Log.w(TAG, e.getMessage());
                }
                return null;
            }
        }.execute();
    }
 
    public void dispatch() {
        //didn't like running as a single thread
        mTracker.dispatch();
    }
     
    public void stop() {
        dispatch();
        mTracker.stop();
    }
}


In the code above I'm creating a singleton of my helper class and providing five methods to make it easy to track events and page views.  The main change here is that I'm calling trackPageView and trackEvent in ASyncTasks so that they don;t run on the UI thread.  I had issues running dispatch off the UI thread.

Here is an easy example of calling trackError through the AnalyticsHelper singleton.  

private void updateHandicap(int golferId) {
    try {
        AsyncTask.Status status = updateGolferTask.getStatus();
        if (AsyncTask.Status.PENDING.equals(status)) {
            updateGolferTask.execute(new String[]{golferId + ""});
        }
    } catch (Exception e) {
        AnalyticsHelper.getInstance(this).trackEvent("exception", "updateHandicap", this.getLocalClassName(), 0);
    }
}


This is how I'm implementing Google Analytics in my app and it's similar to how Google implemented it in their Google IO 2011 android app.  As I work more with this I'll try to keep this blog up to date on how it's working.  I do love having analytics in my android app and wish I could do it with AOP instead of writing this boilerplate code everywhere.

Custom List View using ArrayList

It's a common practice in Android applications to display a list of elements in your Android application.  For the most part you will be displaying this list from your database using a database cursor.  If your data comes from a different source like a web service, data feed or data file, you will need to create a custom view adapter for your ListView.

In a traditional list activity your application will have a layout and an activity class.  To display a custom list view you will need to create a new layout that will represent just one row in the list and a custom ArrayAdapter to map your view layout to your data model.  I've provided code snippets of all of these from my golf application.

First thing you will want to define is the layout of your data.  This custom layout will be used in the main layout of your application to represent the rows of data.  In a custom list view you know you will be working with a collection of objects.  You need to define which elements of the data model you will want to display in each row.  In the code example below I'm defining four data elements: course, teeBox, par and holes.  I will specify these components in the custom ArrayAdapter class I create later.

<?xml version="1.0" encoding="utf-8"?>
<TableLayout
  android:layout_width="fill_parent"
  android:layout_height="50dip"
  android:stretchColumns="*">
  <TableRow>
      <TextView
        android:id="@+id/course_item"
        android:layout_width="140dip"
        android:layout_height="wrap_content"
        style="@style/ListsText"
        android:textColor="@color/title_text"/>
      <TextView
        android:id="@+id/teeBox_item"
        android:layout_width="80dip"
        android:layout_height="wrap_content"
        style="@style/ListsText"
        android:textColor="@color/title_text"/>
      <TextView
        android:id="@+id/par_item"
        android:layout_width="50dip"
        android:layout_height="wrap_content"
        style="@style/ListsText"
        android:textColor="@color/title_text"/>
      <TextView
        android:id="@+id/holes_item"
        android:layout_width="50dip"
        android:layout_height="wrap_content"
        style="@style/ListsText"
        android:textColor="@color/title_text"/>
    </TableRow>
</TableLayout>

In your main activity class for the list screen you will need to define ListView, retrieve the list data and specify a new  Adapter to display the data. The code below shows all of this in one block.  We define a new ListView based on the courseListView layout and we use the courseService to retrieve a java.util.List of Course objects.  Finally we create a new ListAdapter using a custom adapter that specified the detail item's layout and the collection.  The adapter is then set on the ListView to link it to the main layout.

ListView courseListView = (ListView) findViewById(R.id.courseListView);
 
List<Course> courses = courseService.findAll();
         
final ListAdapter adapter = new CourseAdaptor(this, R.layout.course_item, courses);
courseListView.setAdapter(adapter);

The key component to the code above is the custom AttayAdapter.  To display the data in the list exactly the way you want it to look, you need to create a custom adapter for the data model.  For this example we will create a constructor that takes a context, resource id and an array of objects.  

The getView method needs to be overridden to return a new view that will represent a row of data.  In the getView method, we will get references to the TextViews defined in the custom layout we defined earlier.  Since we know the row id or position we are in the list, we get the associated row from the collection and map that data to the view.  Below is an example of that code.

public class CourseAdaptor extends ArrayAdapter<Course> {
    private int resource;
     
    public CourseAdaptor(Context context, int resource, List<Course> objects) {
        super(context, resource, objects);
        this.resource = resource;
    }
 
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        LinearLayout layout;
         
        Course course = getItem(position);
        String name = course.getName();
        String teebox = course.getTeeBox();
        String par = course.getPar()+"";
        String holes = course.getHoles()+"";
         
        if (convertView == null) {
            layout = new LinearLayout(getContext());
            String inflater = Context.LAYOUT_INFLATER_SERVICE;
            LayoutInflater vi = (LayoutInflater)getContext().getSystemService(inflater);
            vi.inflate(resource, layout, true);
        } else {
            layout = (LinearLayout)convertView;
        }
         
        TextView nameView = (TextView)layout.findViewById(R.id.course_item);
        TextView teeboxView = (TextView)layout.findViewById(R.id.teeBox_item);
        TextView parView = (TextView)layout.findViewById(R.id.par_item);
        TextView holesView = (TextView)layout.findViewById(R.id.holes_item);
         
        nameView.setText(name);
        teeboxView.setText(teebox);
        parView.setText(par);
        holesView.setText(holes);
         
        return layout;
    }
}


Finally all we need to do is include the ListView in our main layout.  This is a fairly simple component to add to your main layout since all the details are defined in the included layout.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical">
    <ListView
    android:id="@+id/courseListView" 
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:cacheColorHint="@color/background2"
    />
</LinearLayout>


I hope this use case was clear enough for people to understand.  In the long run this is probably a very common pattern in Android and a very simple one to execute.  The use case does require an Activity class, an implementation of ArrayAdapter and a pair of layout xml files but most of the hard stuff is abstracted away for you.  Please comment or email me if you have any questions.

Getting Paid

In the mobile space there is an arms race, its a race to get the most developers supporting a specific platform. The current platforms of choice are Apple's iOS and Google's Android OS for mobile platforms. Apps are the life blood of the mobile business because Apple and Google make more revenue off an app bought in their respective store than by selling the actual phones. Having a large user base leads to more app sales and  more continuous revenue for the platform owner.

The article linked below from Fortune is a bit of a FUD piece about app sales on the Android market.  It is very clear that the Android market places a little more emphasis on free apps over paid apps.  Consumers rather have a free app and there are more free apps in the Android market.  Apple supporters may say that this means there are more lower quality apps in the Android market because of the disparity.  Fact is developers are learning a free app can make you as much money as a paid app.

The only real difference with Google and Apple in the mobile space is that Google makes money by sells ads.  Google provides tools to developers that allow then to display ads in your iPhone, Android and Windows phone. So for Google, a free app is an opportunity to be a app supported by Google ads. This is a significant difference between Apple and Google when it comes to revenue from apps. Apple and Google makes revenue on each sale of a paid app from their marketplaces but in a free app Google can make money on each ad displayed for as long as that app is in use.  It makes you wonder, will you make more revenue on that single sale or on that reoccurring ad revenue.

That is the question I ask myself a lot lately. Would I benefit from merging my app into one free app on Android and building a free version on Windows and iPhone platforms? More free apps with ads would mean more revenue to me, the developer. I tend to believe that no one developer is getting rich building apps anymore.  It's a corporate world now offering free apps that drive you to their website. 

Source:
http://www.intomobile.com/2011/05/27/itunes-app-store-more-paidapp-friendly-than-android-market/

Event Tracking with Analytics

Now that you have Analytics setup in your Android app, you might want to track some specific user actions.  You might have some features that don't run on a screen or maybe you want to see how often a specific menu option is selected.  Google Analytics has the ability to track specific events in side your application.

The event tracking is very easy to setup within your app assuming you've setup Analytics already.  All you need to do is call the track() method on the GoogleAnalyticsTracker instance you have running in your Activity class.  The track method takes four paramaters: a category, action, label and a value.  The Analytics web site will sort the events by category then action or label.  I have all my button clicks setup as 'UI_ACTION' category then each button name is the action and the label is the local class name of the activity.  This allows me to see how often a button is clicked on a specific screen and how often a button is clicked overall.

public void onHomeClick(View v) {
    tracker.trackEvent("ui_interaction", "onHomeClick", this.getLocalClassName(), 0);
    if (this instanceof DashboardActivity) {
        return;
    }
 
    final Intent intent = new Intent(this, DashboardActivity.class);
    intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    this.startActivity(intent);
}


So far I've found this helpful to see what functionality is actually being used in my app.  I've found out how users are navigating throughout the application.  It's find of nice to see which features are being under utilized as well.  Gathering this information will help you see which features need improvement and which ones you might want to eliminate.


Starting a mobile business

Over the past year I've been starting my own side business developing mobile applications. Over the last year I've done a lot to make it work and I've made a few mistakes. I'm going to use this blog post to talk about what I didn't know I then that I know now.

Identity. This is extremely important. You need to create a seperate identity for your business. That means a web site shouldn't be your personal blog. Your company email account shouldn't be your gmail account. You should setup an identity just for your business. Google and Amazon are places you've already mist likely have accounts setup. Do you want to be a one person company? If not then you probably shouldn't tie your personal gmail account to your app market account unless you want your employees to see your market purchases like apps, books and movie rentals. Think up a company name then create a gmail account, twitter account, facebook page and web site. You'd be surprised how annoying it is to see comment spam emails from your business web site in your personal email account. I know I'm happy all the app store spam emails go to my business email account.

Have a plan. Seems simple and not even a tip but it is important. The plan should be simple and easy to track. I have several... seriously. I use an app to track all my crazy ideas. I list out improvements I want to make then cross them out. I even sometimes give tasks complete by dates. I used to always fill out these binders with notes and features. Now I use a note app and use tags to organize them. I find this best to help manage my ideas. Some ideas are larger than others and just require more time to mature into a viable solution.

Beta.  I'm a big fan of rolling out your app to a small audiance first. My advice is roll out a beta with its own beta package name to make sure it works and you don't get the dredded one star review because you never tried your app in landscape. Test the app in a temporary package name then when you are ready release under a new name. You can notify users of the new version and get them to migrate over to the final version.

Tracking. Get an analytics tool up front. I love my google analytics. It shows me the features users love and the ones they ignore. No need to spend time improving a feature no one ever uses. Its fun to see people use a feature I thought wouldn't be of value. Knowledge is power. Track your web site as well as your app to see what's used.

Communicate. I have a blog and a twitter account I use to announce updates. Links to the blog are in the market so people can view research the developer. I try to communicate new features and market updates on the blog and via twitter. I've gotten emails and comments on the blog asking for help and for new features. I try to reply immediately and be as helpful as possible.

Innovate. The goal is to be always looking for ways to I improve. Over the past year I've pushed possibly ten or twelve updates of my app. The only screens that haven't changed are the list screens for courses and rounds. I think the app is vastly improved and hope people enjoy the new features. There are always more apps coming out and its wise to make improvements to continue to compete.

Analytics for Android

Every implement some new functionality in your app but wonder if your users are actually using that feature?  Ever add menu items and wonder if your users are aware they are there?  These are a couple of the questions I was asking myself on my android app.  I had spent 40 hours implmenting a new feature that I wasn't sure anyone would actually use.  I was also tempted to eliminate a few menu options because I wasn't sure they were being used.  To answer these questions I added Google Analytics to my android app.

I wasn't really aware that Google Analytics had hooks into Android until I saw it mentioned in a Google IO 2001 presentation about growing a mobile business.  I've used Google Analytics in the past on my blog to track articles read, unique visitors and see where traffic was originateing from.  I ended up watching the IO session 'Optimizing Android Apps with Google Analytics' on YouTube.  This session will show you everything you need to add analytics tracking into your Android application.

For simplicity I'll go over what I did to setup analytics and answer soem of the gotchas that I expereienced.  To get started, visit the Google Code page Analytics to get the SDK and jars you'll need to add to your project.  Your app will have to add two new permissions: android.permission.INTERNET and android.permission.ACCESS_NETWORK_STATE.  Both of these are necessary to push that data gathered to the Analytics servers.

To start gathering data you will need to create an instance of GoogleAnalyticsTracker in your Activity class.  They recommend starting the tracker in your onCreate() method by calling tracker.start("YOUR ANALYTICS ID", context).  This will start the data tracking for page views of this activity.  You will need to call the stop() method on the tracker in your onDestroy() method.  Now you have created the mechanism to capture the data but you still need to send it to Anaylicts manually.

To send data manually you will need to call  the dispatch() method on GoogleAnalyticsTracker.  The start() method does have an option paramater to dispatch your data at a set interval but sending data too often will only hurt the battery life of the phone.  You don't want your app killing the phone battery, leave that to the Google applications.  What I recommend is dispatching only on a specific button click or within the onDestry() method.  For testing you can specify an interval but once you know it's working you probably don't want it dispatching that often.  

How I implemented this was by creating my own parent Activity class that implement the onCreate() and onDestroy() methods.  I then extend this class in all of my activities.  This allows the Analytics code to be in one place and allows me to reference the GoogleAnalyticsTracker in the child classes.

public class TrackedActivity extends Activity {
 
    protected GoogleAnalyticsTracker tracker;
     
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.dashboard);
         
        tracker = GoogleAnalyticsTracker.getInstance();
        tracker.start("UA-YOUR-ACCOUNT-HERE", this);
//      tracker.setDebug(true);
//      tracker.setDryRun(true);
    }
 
    @Override
    protected void onResume() {
        super.onResume();
        tracker.trackPageView("/" + this.getLocalClassName());
    }
 
    @Override
    protected void onDestroy() {
        super.onDestroy();
        tracker.dispatch();
        tracker.stop();
    }
}


Hopefully now if you are slightly interested in gathering information about how your application is used, you'll be able to easily add Google Analytics.  I found this amazingly easy to implement in my own applicaion.  It's great to see what screens are getting the most views and which parts of the app are seldomly used.  I can now see that no one has goen to my Help screen yet for instance.  I can also see that my app is being used in South Korea, Ireland and Argentina.  That is sorta  cool.