Archive for the ‘Android’ Category

The Joys of Screen Scraping

Knucklehead Up and Running Again



For about a week there my MMA fighter app for Android, Knucklehead, has been out of commission. The reason is that the site that I scrape for data, Sherdog was completely redesigned. I needed to rework a lot of my PHP-based screen scraping code to make it work. My PHP is rusty, but fortunately, my regular expression chops are always sharp. In any case, it’s back to working order now.

Maybe with a little luck I can get people to re-rate those 1-star ratings I got while it was down. If not, perhaps I can guilt people into giving me good ratings by saying that it’s my newborn son’s fault I didn’t get to it sooner. I mean, really, are you going to give the father of this child a hard time? Don’t be so heartless. :)

Screen Scraping Tips


As we all know, screen scraping is pretty much the most volatile way to supply your apps with data. While no amount of planning can permanently future-proof your screen scraper, there’s some things you can do to make it easier.

  • Define a data model for what you are scraping. What made my project relatively easy to get working again was that I had a clearly defined data model shared by both the backend and frontend. In my case, this is a LAMP stack and distributed Android app respectively. Now my users don’t need to download a new version of my app in order to get the fix. It just works now.
  • Code the scraper on a backend proxy, not in the app itself. That way, when your scraper inevitably gets broken at some point (after a year in my case), you only need to change the code in one place. This goes hand-in-hand with defining a data model.
  • Keep it simple stupid. Don’t over-architect a screen scraper. Most data processing code will change over time, but a screen scraper may need a total rewrite at some point. Put all your software engineering skills into controlling what leaves the scraper, but play it pretty loose with what the scraper is going to be processing.
  • Using JSON? You better be using jsonlint.com too. jsonlint.com can save you a lot of time and aggravation when it comes to creating and processing your scraped data. Not only does it validate your JSON, but it will also pretty print it so it’s easy to read through yourself.

That’s just a couple things that made fixing my scraper a lot less painful. You guys out there have any tips? I’d love to hear them. All things being equal, I hope I never have to screen scrape for data ever again. But since that’s pretty unlikely, it’s good to be prepared for the inevitable obstacles that scraping will present.

A Whole Mess of Android Helpers

Overview


In my work on my MMA fighter Android app Knuckle Head I used a lot of Android functionality that I’m sure I’ll use again. Things like JSON processing, asynchronous tasks with ProgressDialogs, and single instance Toasts are all things that will inevitably show up in future work. Because I’m just so generous, I decided to refactor them out of the Knuckle Head specific code and turn them into a Java package anyone could use.

* Clone or download

 

The Classes


Its a small collection so far, but sure to grow very quickly. Here’s a list of the classes included so far:

  • – A collection of JSON handling functions. Includes Retrieving JSON from a remote URL.
  • – A generic key/value pait class. Useful for populating Android Spinners.
  • – A collection of static classes for operations I don’t have a better home for.
  • – I hate how Toasts in Android are shown sequentially. To prevent this I operate on a single instance of a Toast throughout my apps.
  • – A class that utilizes ToastSingleton.java and delivers Toasts with convenient static functions.
  • – This is a custom AsyncTask for a very common Android problem. I very often need to retrieve JSON from a remote URL, display a ProgressDialog while this occurs, operate on the data when it is returned, and then close the ProgressDialog. With this class that work is reduced down to just a few lines of code.
 

UrlJsonAsyncTask Example


If you had only have time for a quick look, be sure to check . Using this class in side my Activity code reduced the line count drastically. Since I was lazy about documentation for any of this, here’s an example to reward my blog reader’s. This is straight from Knuckle Head code. You’ll notice a few other classes mentioned above are also included.

UrlJsonAsyncTask is not abstract like AsyncTask, most of the functionality has been implemented. You only need to implement onPostExecute(JSONObject) if you want to do something with the returned JSON. In order for this to work, your JSON needs to return an object formatted like this:

{
    "success":true|false,
    "info":"error or warning message from server",
    "data": {}
}
  • success is a boolean indication from your JSON source whether or not the request was successful
  • info on error or warning, will contain an app user friendly message that can be delivered via Toast
  • data This is the actual JSON object or array that you will operate on given a successful request
 

And this is how you would define your own UrlJsonAsyncTask. In this case I am querying my server to get a list of fighter, or the profile of one particular fighter. In either scenario, as stated in the JSON description above, the “data” attribute of my JSON is what I will pass to the target intent.

private class FighterSearchTask extends UrlJsonAsyncTask {
    public FighterSearchTask(Context context) {
        super(context);
    }
 
    @Override
    protected void onPostExecute(JSONObject json) {
        try {
            this.validateJson(json); // throws IOException, JSONException
 
            // These 4 lines represent the actual post execute logic
            Class> intentClass = json.getString("info").equals("list") ? FighterListActivity.class : FighterTabActivity.class;
            Intent intent = new Intent(context, intentClass);
            intent.putExtra("json", json.getString("data").toString());
            startActivity(intent);
        } catch (IOException e) {
            // IOExceptions are custom and return JSON "info" in getMessage()
            Toaster.toast(this.context, e.getMessage());
        } catch (JSONException e) {
            // Lazy.Ex.getStackTrace(Exception) is just a simple one liner for turning 
            // a stack trace into a String.
            Log.e("YourTagName", Lazy.Ex.getStackTrace(e));
 
            // JSONExceptions here are generally because your JSON is not formatted as 
            // per the description in the comments above. We don't want to show these 
            // errors to the user, so we give them a pre-defined error message to let 
            // them know something went wrong.
            Toaster.toast(context, this.getMessageError());
        } finally { 
            super.onPostExecute(json); // Must be called to close the ProgressDialog
        }
    }
}

After you have created your subclass of UrlJsonAsyncTask, you can execute these following few lines of code anywhere to fire your task and have a ProgressDialog pop up until it either finishes successfully or or returns an error/warning message. There’s tons of setters to customize your task. Check them out in the .

FighterSearchTask task = new FighterSearchTask(SearchActivity.this);
task.setMessageLoading("Searching for fighters...");
task.setConnectionParams(2000, 5000, 3); // connection timeout(ms), read timeout(ms), number of retries
task.execute(url);

And voila, you got JSON from a remote URL with a ProgressDialog assuring your users that the data is in fact loading. All of this is non-blocking so you are able to continue other operations or update the UI if necessary.

Summary


So yeah, it’s just another collection of a developer’s reusable tools for getting stuff done, this time for Android. If you do happen to peruse, or use, the code, let me know what you think. Suggestions,criticisms, and of course contributions are more than welcome.

Knuckle Head: My MMA Fighter App for Android

Get the App


* Click here to download “Knuckle Head” on the Android Market!

Overview


Knuckle Head is an app for the true Mixed Martial Arts fan. It allows you to search a database to find details on any fighter in the world, not just the UFC. In just a few touches you can search by name, nickname, or weight class to find every detail about your favorite fighters. This is not just another MMA news app. Inside you’ll find fighter images, detailed fight records, vital statistics, and other MMA analysis.

Oh, and I love feedback, so tell me what you want next!

Screenshots


In Case You Were Interested…


I’ll have some technical blog posts regarding this app up very soon. There’ll be lots of native Android development goodies to be gleaned. In addition I’m likely gonna post up some Android/Java convenience classes I’ve been using for various tasks like JSON handling, AdMob, ProgressDialogs, and more to my . Stay tuned.

Handling Global Configuration in Android

The Problem

You have configuration variables and data that you want to share globally in your Android app. You want all Activities to be able to access and edit the same instance of application specific data.

Generic Solution

You could create a Configuration class loaded with static members or as a Singleton. Now you’ve got a shared, single instance of your configuration that can be accessed anywhere in your application. But there are 2 minor drawbacks to this method in the context of Android development.

  1. You now have to initialize your configuration in a separate class, in this case, your startup Activity.
  2. You’ll need to carry this new Configuration class around in all your projects.

Neither of these problems are critical, but let’s see if we can do better.

Android Solution

In Android it is possible to extend the Application class in order to store additional application specific information… like say configuration data. These additional members are then accessible in any Activity via the getApplicationContext() method.

The first step in this process is extending your app’s Application object. Android gives your app a generic Application object by default, so you may have never even had to look at this class directly. But as you’ll see below, its pretty simple to extend.

SampleApplication.java

public class SampleApplication extends Application {
    private String mStringValue;
 
    @Override
    public void onCreate() {
        // Here you could pull values from a config file in res/raw or somewhere else
        // but for simplicity's sake, we'll just hardcode values
        mStringValue = "SavageLook.com";
        super.onCreate();
    }
 
    public String getStringValue() {
        return mStringValue;
    }
 
    public void setStringValue(String value) {
        mStringValue = value;
    }
}

As you can see above, extending the Application class as SampleApplication is quite simple. You add class members for each bit of configuration data you would like to share throughout your application. In this example, we are simply saving a String value (mStringValue) that can then be accessed and modified in any Activity in SampleApplication.

After you’ve added your members and any necessary getters/setters, you need to override the onCreate() method. In the onCreate() method you can then initialize your class members with your configuration data. You can do this any way you want. To keep the example simple, I hardcoded the configuration value (mStringValue), but I commonly store configuration data in JSON files in res/raw and load them into my extended Application object. I won’t get into those details, though, as that will likely be a whole other blog post. Make sure you call super.onCreate() when you are finished.

NOTE: It’s not necessary to initialize your members in the onCreate(). For application-wide configuration data you would likely always do it in onCreate(), but you are technically free to manipulate the extended Application object anywhere in your app code.

AndroidManifest.xml

 version="1.0" encoding="utf-8"?>
 xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.yourdomain"
      android:versionCode="1"
      android:versionName="1.0">
 
    
 
     
        android:icon="@drawable/icon" 
        android:label="@string/app_name"
        android:name=".SampleApplication">
         android:name=".SampleActivity">
            >
                 android:name="android.intent.action.MAIN" />
                 android:name="android.intent.category.LAUNCHER" />
            >
        >
    >
>

The final modification to make our Android app use the new SampleApplication class is setting it in the element of the AndroidManifest.xml file. The only change we make to AndroidManifest.xml is adding the android:name attribute to the element. Just add this attribute and set it equal to the name of your extended Application object, in this case SampleApplication.

OK, so now your app is using your new SampleApplication class. How do we access the stored data in its members? See the basic usage below.

Sample usage in any Activity

public class SampleActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
 
        SampleApplication sampleApp = (SampleApplication)getApplicationContext();
        String localStr = sampleApp.getStringValue();    
        // perform operations on localStr
        sampleApp.setStringValue(localStr);
 
        // The rest of your Activity code...
    }
}

All you have to do is call getApplicationContext() and cast it to the type of your new Application object (SampleApplication). The resulting SampleApplication object can now be used the get/set any of the members you defined for it in SampleApplication.java.

Summary

Now you have a solid, Android native way to share data globally in your app code. While I’m a big fan of portable solutions, I feel this Android specific one is worth to have a clear distinction between the onCreate() of my Application versus my Activities. Hopefully this walkthrough was clear because this is something you will likely do very frequently when developing Android apps.

One final note. I can already envision some lazy developers’ minds churning, “I can use this instead of Intent extras!”… Don’t! Intent extras have a specific interaction context which is important not only to functionality, but also to readability of your code. Don’t undo this by just feeding them into the global Application object. Extending your Application object is a complement to Intent extras, not a replacement.

Android Quick Tip: EditText with DONE Button That Closes the Keyboard

Android Quick Tip

Close the keyboard when you click ‘DONE’… seems like a simple task, right? Unfortunately, clicking ‘DONE’ or the ‘RETURN’ button in even a single line Android EditText will not drop focus and close the soft keyboard. In most cases it will just generate a new line of text. So what do you have to do if you want to close the soft keyboard by clicking ‘DONE’?

First you need to set the android:imeOptions attribute equal to “actionDone” for your target EditText as seen below. That will change your ‘RETURN’ button in your EditText’s soft keyboard to a ‘DONE’ button.

* example_edittext.xml

 
    android:id="@+id/edittext_done"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:hint="Enter some text"
    android:imeOptions="actionDone"
    />

Now we need to create a custom OnEditorActionListener for the target EditText that will recognize when the ‘DONE’ button has been clicked. In it we override the onEditorAction() method, get an instance of the InputMethodManager, and use it to close the soft keyboard. Here’s the code for the custom OnEditorActionListener class:

* DoneOnEditorActionListener.java

class DoneOnEditorActionListener implements OnEditorActionListener {
    @Override
    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
        if (actionId == EditorInfo.IME_ACTION_DONE) {
            InputMethodManager imm = (InputMethodManager)v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
            imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
            return true;   
        }
        return false;
    }
}

Now go to the onCreate() method of the Activity that contains the target EditText. We’ll call it SampleActivity here. In here you will assign the new DoneOnEditorActionListener to the target EditText via the setOnEditorActionListener() method.

* SampleActivity.java

public class SampleActivity extends Activity {      
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.sample_activity_layout); // sample_activity_layout contains our target EditText, target_edittext
 
        EditText targetEditText = (EditText)findViewById(R.id.target_edittext); 
        targetEditText.setOnEditorActionListener(new DoneOnEditorActionListener());
 
        // The rest of the onCreate() code
   }
}

And there you go, your target EditText field will now close the soft keyboard whenever you click the ‘DONE’ button. Very handy and not too difficult. Enjoy.

Android Quick Tip: Tiling a Background Image

Android Quick Tip

Often times you’ll want to tile, or repeat, a small image as the background of an Android view or layout. This is just like using the ‘background-image’ and ‘background-repeat’ CSS options, and almost as easy. First and most importantly, you’ll need an image to tile. If you don’t have one of your own, use the one below (right click and “Save Image”).

carbon_fibre.gif

Carbon Fiber tile

Now, in your project’s res/drawable path, create a file named tile_background.xml. Fill the file with the following XML:

tile_background.xml

 version="1.0" encoding="UTF-8" ?>
 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:src="@drawable/carbon_fibre" 
    android:tileMode="repeat" 
    />

Now all you have to do is set the android:background attribute of your target view or layout to the tile_background.xml drawable id, like so:

sample_layout.xml

 version="1.0" encoding="utf-8"?>
 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@drawable/tile_background"
    >
>

And if sample_layout.xml was the assigned content view for your main activity, you would see this when you started your app:

tiled

Android Quick Tip: Remove ListView Dividers

Quick Tip

If you want to remove the dividers from an Android ListView, execute the following code on your ListView object.

ListView listView = (ListView)findViewById(R.id.your_listview_id);
listView.setDivider(null);
listView.setDividerHeight(0);

or if you are trying to remove dividers from a ListActivity, do this in your onCreate() method:

this.getListView.setDivider(null);
this.getListView.setDividerHeight(0);

And there you go, no more dividers. Enjoy.

Display Images from the Internet in Android

The Overview

Android ImageVIew
I’ll make my usual yammering quick so you can get straight to the good stuff. This is just a snippet to show you how you can display an image from the Internet in your native Android app via ImageView. This is ideal in a few situations, namely when you need to keep app size and initial load time down, or if it isn’t feasible to locally store all the images your app will use.

The Code

package com.savagelook;
 
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
 
import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.widget.ImageView;
 
public class ShowImageActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
 
        // YOUR_LAYOUT is the name of your layout resource
        // IMAGEVIEW_ID is your ImageView in YOUR_LAYOUT
        setContentView(R.layout.YOUR_LAYOUT);
        ImageView imageView = (ImageView)findViewById(R.id.IMAGEVIEW_ID); 
 
        imageView.setImageDrawable(createDrawableFromURL("https://savagelook.com/misc/sl_drop2.png"));
    }
 
    private Drawable createDrawableFromURL(String urlString) {
        Drawable image = null;
        try {
            URL url = new URL(urlString);
            InputStream is = (InputStream)url.getContent();
            image = Drawable.createFromStream(is, "src");
        } catch (MalformedURLException e) {
            // handle URL exception
            image = null;
        } catch (IOException e) {
            // handle InputStream exception
            image = null;
        }
 
        return image;
    }
}

Swipes, or “Flings”, for Navigation in Android

Download the Project

  • Eclipse project and full source for “Fling”

Overview

I’ll keep the narrative on this one brief and get you right to the good stuff. I wanted to be able to navigate through the activities in an Android app just like Tweetdeck. One swipe of the finger and you quickly transition to the next column. Take a look at how it looks when you swipe your finger from right to left.


pre-fling

during fling

post-fling

In Android, they call this a “fling”. Think of a fling as a touch and drag with a specific direction and speed threshold. Once that speed threshold is surpassed, the fling is triggered. Here’s the bare bones code necessary to get fling gestures working in your Android app. Also, I use custo animations based on the ones from the Android SDK to determine how my activities will transition. A left to right fling should obviously have the opposite appearance as a right to left fling. Big thanks to this post as it makes up a majority of the logic and code in this sample.

src/MainActivity.java

This is where the heavy lifting is done. The important things to note are the use of a custom gesture listener to handle our fling event and the use of overridePendingTransition() to apply our custom transition animations to the activity we just started.

package com.savagelook;
 
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.View;
import android.view.MotionEvent;
 
public class MainActivity extends Activity {
    private static final int SWIPE_MIN_DISTANCE = 120;
    private static final int SWIPE_MAX_OFF_PATH = 250;
    private static final int SWIPE_THRESHOLD_VELOCITY = 200;
    private GestureDetector gestureDetector;
 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
 
        gestureDetector = new GestureDetector(new MyGestureDetector());
        View mainview = (View) findViewById(R.id.mainView);
 
        // Set the touch listener for the main view to be our custom gesture listener
        mainview.setOnTouchListener(new View.OnTouchListener() {
            public boolean onTouch(View v, MotionEvent event) {
                if (gestureDetector.onTouchEvent(event)) {
                    return true;
                }
                return false;
            }
        });
    }
 
    class MyGestureDetector extends SimpleOnGestureListener {
        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            Intent intent = new Intent(MainActivity.this.getBaseContext(), MainActivity.class);
 
            if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH) {
                return false;
            }
 
            // right to left swipe
            if(e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
                startActivity(intent);
                MainActivity.this.overridePendingTransition(
                        R.anim.slide_in_right,
                        R.anim.slide_out_left
                );
            // right to left swipe
            }  else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
                startActivity(intent);
                MainActivity.this.overridePendingTransition(
                        R.anim.slide_in_left, 
                        R.anim.slide_out_right
                );
            }
 
            return false;
        }
 
        // It is necessary to return true from onDown for the onFling event to register
        @Override
        public boolean onDown(MotionEvent e) {
                        return true;
        }
    }
}

res/layout/main.xml

A standard main.xml. The only change you’ll find here is that we’ve given the main view, our LinearLayout, an ID so it is accessible in MainActivity.java.

 version="1.0" encoding="utf-8"?>
 xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:id="@+id/mainView"
    > 
          
      android:layout_width="fill_parent" 
            android:layout_height="wrap_content" 
      android:text="@string/hello"
  />
>

AndroidManifest.xml

Again, a standard manifest with one small change. I added android:noHistory=”true” so that as we load new activities with each fling, the view stack doesn’t grow out of control. You can remove this if you would like to go back to a previous activity after each fling using the back button.

 version="1.0" encoding="utf-8"?>
 xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.savagelook"
      android:versionCode="1"
      android:versionName="1.0">
 
     android:icon="@drawable/icon" android:label="@string/app_name">
         android:name=".MainActivity"
                  android:label="@string/app_name"
                  android:noHistory="true">
            >
                 android:name="android.intent.action.MAIN" />
                 android:name="android.intent.category.LAUNCHER" />
            >
        >
    >
>

res/anim/slide_in_right.xml

This along with the next 3 animations are the custom transition animations used to move our activities in and out with each fling. They are just copies of the animations in the Android SDK. In my case, they were found in ANDROID_SDK/platforms/android-8/data/res/anim. Create your own anim folder under res in your project and add these there.

 xmlns:android="http://schemas.android.com/apk/res/android">
         android:fromXDelta="50%p" android:toXDelta="0"
            android:duration="@android:integer/config_mediumAnimTime"/>
         android:fromAlpha="0.0" android:toAlpha="1.0"
            android:duration="@android:integer/config_mediumAnimTime" />
>

res/anim/slide_out_right.xml

 xmlns:android="http://schemas.android.com/apk/res/android">
         android:fromXDelta="0" android:toXDelta="50%p"
            android:duration="@android:integer/config_mediumAnimTime"/>
         android:fromAlpha="1.0" android:toAlpha="0.0"
            android:duration="@android:integer/config_mediumAnimTime" />
>

res/anim/slide_in_left.xml

 xmlns:android="http://schemas.android.com/apk/res/android">
         android:fromXDelta="-50%p" android:toXDelta="0"
            android:duration="@android:integer/config_mediumAnimTime"/>
         android:fromAlpha="0.0" android:toAlpha="1.0"
            android:duration="@android:integer/config_mediumAnimTime" />
>

res/anim/slide_out_left.xml

 xmlns:android="http://schemas.android.com/apk/res/android">
         android:fromXDelta="0" android:toXDelta="-50%p"
            android:duration="@android:integer/config_mediumAnimTime"/>
         android:fromAlpha="1.0" android:toAlpha="0.0"
            android:duration="@android:integer/config_mediumAnimTime" />
>

Summary

Its a little more work than you might expect for this functionality, but you do get a lot of opportunities to tinker with it. You can change the speed threshold for when flings occur. You can change both the current and incoming transition animations. You can even get creative and make transitions based on vertical or diagonal flings as well. Thats the beauty, and sometimes curse, of Android: near infinite flexibility.

Mobile Developer’s Icon & Image Checklist

Overview

If there’s one thing I’ve learned from delving into iOS, Android, and Blackberry app development, it’s that there’s a lot more to creating a mobile app than just coding it. One of the things that can catch you off guard, especially if you are devoid of design ability like me, is the amount of icons and images necessary to deploy your apps. This becomes even more daunting when you intend to deploy to multiple platforms.

The other concern is that it isn’t always evident from the development tools how many different graphics you need to account for all scenarios. A new iOS developer will likely be unaware that you need a 58×58 pixel icon for iPhone 4 Spotlight and Settings. To attempt to alleviate some of this confusion, I put together these charts to detail what I know so far about the graphics required for submitting mobile apps to the various Android, iOS, and Blackberry markets.

Icons

Android iOS Blackberry Playbook Notes
29×29 iPhone Settings and Spotlight, iPad Settings
36×36 low pixel density icon
48×48 medium pixel density icon
50×50 iPad Spotlight. iOS will trim 1 pixel off each side and add a drop shadow, making it 48×48
57×57 standard iPhone icon
58×58 iPhone 4 Settings and Spotlight
64×64 optional small custom document icon
72×72 Android high pixel density icon, iPad icon
86×86 standard Playbook icon. It will trim 5 pixels off each side, making it 76×76
96×96 Potential icon size if iPad gets a high ppi screen
114×114 standard iPhone 4 icon
144×144 Potential icon size if iPad gets a high ppi screen
320×320 optional large custom document icon

Distribution Images

Android Market Apple App Store Blackberry App World Amazon App Store Notes
screenshot sizes 320×480, 480×800, 480×854, 1280×800 iPhone: 320×480, 480×320, 320×460, 480×300 iPhone 4: 640×960, 960×640 iPad: 768×1024, 1024×768, 748×1024, 1004×768 640×640 or smaller 480×854, 854×480 required # of screenshots:
Android: at least 2
Apple: at least 1
Blackberry: 1-50
Amazon: 3-10
114×114 device icon
180×120 Promotional graphic (no alpha)
480×480 Product icon that should match your 86×86 icon
512×512 Large/High resolution product icon
1024×500 Feature graphic for market

Additional Notes

For Android, your 36×36, 48×48, and 72×72 pixel icons should be placed in the drawable-ldpi, drawable-mdpi, and drawable-hdpi folders of your Android project respectively. Be sure to specify the name of the file you will be using in the android:icon attribute of the element in your AndroidManifest.xml file. Android also allows you to include a promotional video.

For iOS you simply need to add your appropriately sized and named icons to your Resources directory in your XCode project. Here’s a mapping of the icon sizes to the required name of the icon file. NOTE: There is purposely no extension on the 512×512 iTunesArtwork file.

  • 29×29 – Icon-Small.png
  • 50×50 – Icon-Small-50.png
  • 57×57 – Icon.png
  • 58×58 –
  • 72×72 – Icon-72.png
  • 114×114 –
  • 512×512 – iTunesArtwork

For Blackberry Playbook, you need to specify the name of your 86×86 application icon in your application’s blackberry-tablet.xml file. Below is a sample of how you would set that up.

>
    >
        >86x86.png>
    >
    >My Company>
    >core.games>
    >my_splashscreen.jpg>
>

For the Amazon App Store you can also submit promotional images of various sizes and up to 5 promotional videos.

Summary

So as you can see, you’ve got a lot to consider when it comes to presenting your mobile application to the masses. As a developer, I’m looking for the simple solution here. For me, the easiest thing to do was use this 512×512 iOS icon template, create my icon, and scale it down to all the required sizes. If you happen to be reading this post and know a better/easier workflow, please let me know.

Here’s a few references to check out to get some additional information on mobile app icons and market images: