FirstClown

firstclown at firstclown.us

Adding Icons to Your Android Application

After seeing how easy it is to add an icon to a contact in my new G1 phone, I decided to try and do it in an application of my own. I've sense been asked about it a little bit, so I figured I'd post how to do it on my blog. It's surprisingly easy, but there's quite a few steps involved.

BTW, none if this is original, but I haven't seen anyone really write a up a complete post on how to do it. I reverse engineered the concept and the steps from the great open source project apps-for-android.

UI

To set up the interface for adding the icon, you'll want to add an ImageButton to the edit screen for your items. You can then set the image with:

    protected void showIconButton(String iconUri){
        if(iconUri != null){
            ImageButton iconField = (ImageButton) findViewById(R.id.iconButton);
            iconField.setImageURI(null);
            iconField.setImageURI(Uri.parse(iconUri));
            iconField.invalidate();
        }
    }

You'll need the extra iconField.setImageURI(null); call in there since Android ignores a call to setImageURI that has the same URI is it currently holds. Since I save the new icon image to the same place as the old one, that can be a problem.

You'll also want to have the button set up to select a new image when it's clicked. That will be a simple call to iconField.setOnClickListener(cmdIconListener); in the onCreate method of the Activity.

Button Listener

Now we can create the action the button should take when it's clicked.

    protected OnClickListener cmdIconListener = new OnClickListener() {
        public void onClick(View arg0) {
            Intent i = new Intent("android.intent.action.GET_CONTENT");
            i.setType("image/*"); // We just want images.
 
            //ADD_ICON is a unique integer code for the intent response.
            startActivityForResult(i, AddTea.ADD_ICON); 
        }
    };

You can see we're firing a very simple Intent to get an image. This will allow the user to select an image from their picture gallery, just like the contact application does. It also keeps it generic enough that another application can act on this Intent, too.

Crop the Image

This Intent will return an image URI that we then want to crop:

    protected void onActivityResult(int requestCode, int resultCode, Intent data){
        // See which child activity is calling us back.
        switch (requestCode) {
        case AddTea.ADD_ICON:
            // This is the standard resultCode that is sent back if the
            // activity crashed or didn't doesn't supply an explicit result.
            if (resultCode != RESULT_CANCELED){
                Intent i = new Intent("com.android.camera.action.CROP");
                i.setClassName("com.android.camera", "com.android.camera.CropImage");
                i.setData(data.getData());
                Log.d(TAG, "path: " + data.getData().getPath());
                i.putExtra("noFaceDetection", true);
                i.putExtra("outputX", iconWidth);
                i.putExtra("outputY", iconHeight);
                i.putExtra("aspectX", iconWidth);
                i.putExtra("aspectY", iconHeight);
                i.putExtra("scale", true);
 
                if(iconUri == null){
                    ContentValues values = new ContentValues();
                    values.put(android.provider.MediaStore.Images.Media.TITLE, name + " Icon");
                    values.put(android.provider.MediaStore.Images.Media.BUCKET_ID, "STeaP_Tea_Timer_Icons");
                    values.put(android.provider.MediaStore.Images.Media.BUCKET_DISPLAY_NAME, "STeaP Tea Timer Icons");
                    values.put(android.provider.MediaStore.Images.Media.IS_PRIVATE, 1);
                    iconUri = getContentResolver().insert(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values).toString();
                }
                i.putExtra("output", Uri.parse(iconUri));
                startActivityForResult(i, CROP_ICON);
            }
            break;

We're basically just sending another Intent after getting the first one back. Here we are sending the Intent to a specific Activity, the com.android.camera.CropImage Activity. This Activity will crop the image and save the image, so we need to pass all the information we want the image saved with. The important ones are:

  • noFaceDetection - Unless you're looking for people, you'll want this to be true.
  • outputX - The width of the resulting image. In pixels.
  • outputY - The height of the resulting image. In pixels.
  • aspectX, aspectY - The aspect ratio of the crop square. Don't set these if you want the user to pick whatever size they want, otherwise set them to what you want.
  • scale - Whether you want the image scaled to the dimensions you set. Almost always should be true.
  • android.provider.MediaStore.Images.Media.IS_PRIVATE - Set to true if you don't want all of your little icons clogging up the Android Pictures app.

The insert command on the ContentProvider will hand you back the images URI so you can store it with the rest of your item's data. In my example, if I already have a URI for the icon, I just overwrite it with the new cropped image, no need to create another one.

Now we just need to handle the result from the Crop Intent:

case AddTea.CROP_ICON:
            if (resultCode != RESULT_CANCELED) {
                Log.d(TAG, "Data String: " + iconUri);
                showIconButton(iconUri);
            }
        default:
            break;

And now you've got your icon stored in the ContentProvider and the URI in your application. Easy as that!


Related posts:

  1. My Hope for Android Over the past two weeks, I've been getting deep into...
  2. Mapping the Virtual World to the Physical World I'm starting to see a very interesting trend lately with...

Tags:

  • Here's the whole class (hopefully it formats properly):

    package us.firstclown.android.steap.tea_timer.activity;

    import us.firstclown.android.steap.tea_timer.R;
    import us.firstclown.android.steap.tea_timer.TeaDAO;
    import android.app.Activity;
    import android.content.ContentValues;
    import android.content.Intent;
    import android.net.Uri;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.Button;
    import android.widget.EditText;
    import android.widget.ImageButton;
    import android.widget.TextView;

    public class AddTea extends Activity {
    private TeaDAO dao;
    private long id = 0;
    private String iconUri;
    private String name;
    private int iconWidth = 50;
    private int iconHeight = 50;
    private static final String TAG = "AddTea";
    public static final int CREATE_TEA = 1;
    public static final int EDIT_TEA = 2;
    public static final int ADD_ICON = 3;
    public static final int CROP_ICON = 4;

    /*
    * (non-Javadoc)
    *
    * @see android.app.Activity#onCreate(android.os.Bundle)
    */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.add_tea);
    dao = new TeaDAO(this);

    Bundle extras = getIntent().getExtras();
    if (extras != null) {
    id = extras.getLong("id");
    ContentValues values = dao.fetchTea(id);
    iconUri = values.getAsString("iconUri");
    name = values.getAsString("name");
    String type = values.getAsString("type");
    String time = values.getAsString("time");

    ImageButton iconField = (ImageButton) findViewById(R.id.iconButton);
    showIconButton(iconUri);
    iconField.setOnClickListener(cmdIconListener);

    TextView nameField = (TextView) findViewById(R.id.nameField);
    nameField.setText(name);

    TextView typeField = (TextView) findViewById(R.id.typeField);
    typeField.setText(type);

    TextView timeField = (TextView) findViewById(R.id.timeField);
    timeField.setText(time);
    }

    Button saveButton = (Button) findViewById(R.id.saveButton);
    Button cancelButton = (Button) findViewById(R.id.cancelButton);

    saveButton.setOnClickListener(cmdSaveListener);
    cancelButton.setOnClickListener(cmdCancelListener);
    }

    protected void showIconButton(String iconUri) {
    if (iconUri != null) {
    ImageButton iconField = (ImageButton) findViewById(R.id.iconButton);
    Log.d(TAG, "iconUri: " + iconUri);
    iconField.setImageURI(null);
    iconField.setImageURI(Uri.parse(iconUri));
    Log.d(TAG, "Cache is "
    + (iconField.getDrawingCache() == null ? "" : "not")
    + " null");
    iconField.invalidate();
    }
    }

    protected OnClickListener cmdSaveListener = new OnClickListener() {
    // @Override
    public void onClick(View arg0) {
    String name = ((EditText) findViewById(R.id.nameField)).getText()
    .toString();
    String type = ((EditText) findViewById(R.id.typeField)).getText()
    .toString();
    String time = ((EditText) findViewById(R.id.timeField)).getText()
    .toString();

    if (id != 0) {
    dao.updateTea(id, iconUri, name, type, time);
    } else {
    dao.createTea(iconUri, name, type, time);
    }

    Intent i = new Intent();
    setResult(RESULT_OK, i);
    finish();
    }
    };

    protected OnClickListener cmdCancelListener = new OnClickListener() {
    // @Override
    public void onClick(View arg0) {
    Intent i = new Intent();
    setResult(RESULT_OK, i);
    finish();
    }
    };

    protected OnClickListener cmdIconListener = new OnClickListener() {
    // @Override
    public void onClick(View arg0) {
    Intent i = new Intent("android.intent.action.GET_CONTENT");
    i.setType("image/*");
    startActivityForResult(i, AddTea.ADD_ICON);
    }
    };

    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    // See which child activity is calling us back.
    switch (requestCode) {
    case AddTea.ADD_ICON:
    // This is the standard resultCode that is sent back if the
    // activity crashed or didn't doesn't supply an explicit result.
    if (resultCode != RESULT_CANCELED) {
    Intent i = new Intent("com.android.camera.action.CROP");
    i.setClassName("com.android.camera",
    "com.android.camera.CropImage");
    i.setData(data.getData());
    Log.d(TAG, "path: " + data.getData().getPath());
    i.putExtra("noFaceDetection", true);
    i.putExtra("outputX", iconWidth);
    i.putExtra("outputY", iconHeight);
    i.putExtra("aspectX", iconWidth);
    i.putExtra("aspectY", iconHeight);
    i.putExtra("scale", true);

    if (iconUri == null) {
    ContentValues values = new ContentValues();
    values.put(android.provider.MediaStore.Images.Media.TITLE,
    name + " Icon");
    values.put(
    android.provider.MediaStore.Images.Media.BUCKET_ID,
    "STeaP_Tea_Timer_Icons");
    values
    .put(
    android.provider.MediaStore.Images.Media.BUCKET_DISPLAY_NAME,
    "STeaP Tea Timer Icons");
    values
    .put(
    android.provider.MediaStore.Images.Media.IS_PRIVATE,
    1);
    iconUri = getContentResolver()
    .insert(
    android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
    values).toString();
    }
    i.putExtra("output", Uri.parse(iconUri));
    startActivityForResult(i, CROP_ICON);
    }
    break;
    case AddTea.CROP_ICON:
    if (resultCode != RESULT_CANCELED) {
    Log.d(TAG, "Data String: " + iconUri);
    showIconButton(iconUri);
    }
    default:
    break;
    }

    }
    }
  • bala_eventurers
    very nice and useful code i used the scrop coding only it works but its through error after scrop so pls send or upload the AddTea class file.
  • bala_eventurers
    pls upload the AddTea class file i got error when i scrop and save the image it wont set as icon
blog comments powered by Disqus

FirstClown is powered by WordPress
Entries (RSS) and Comments (RSS).