Mobility

Browse Android's media gallery via intents

Developer William J. Francis digs a little deeper into the Android intent mechanism and shows how to browse, select, and display an existing image on your SD card.

A few weeks ago I wrote a post on taking advantage of the built-in camera on an Android device to capture photos inside your app. I thought it was a pretty nifty trick (my cat was less impressed), but it leads to the obvious question: What if you just want to browse your device for an existing image and work with it inside your activity?

Never fear! Through the wonders of Android's powerful intent registration system it's easy to comb the SD card for images. And best of all, the source of those images isn't important. That means whether you are looking for photos from the camera, clip art downloaded in the browser, or graphs loaded to your phone directly from a PC, all of those images are accessible to and by your app.

This week's demo will allow you to browse, select, and display an existing image on your SD card. You can follow along with the step-by-step tutorial, or download the entire project and import it directly into Eclipse.

1. Create a new Android project in Eclipse. Target Android 1.6 or greater. Be sure to rename your startup file to Main.java.

2. In the /res/layout folder, create a new main.xml layout file. For this project, we will use a linear layout that has a simple text view, a single button, and an image view. For now, let's point the image view source to the application's icon file.

main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:orientation="vertical">
    <TextView
        android:layout_width="fill_parent"android:layout_height="wrap_content"
        android:gravity="center"

android:layout_margin="10dip"
android:text="Gallery Browser Demo"/>
    <Button

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="Find a Photo" android:layout_gravity="center"

android:layout_margin="10dip"

android:id="@+id/browse_button" />
    <ImageView
        android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:gravity="center"

android:scaleType="centerInside"

android:src="@drawable/ic_launcher" android:id="@+id/image_holder"/>
</LinearLayout>

3. Move to our /src file and begin working in the Main.java file. To start, we will define a couple of static constants and wire up our browse button.

Main.java
package com.authorwjf.pic_viewer;
import java.io.InputStream;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

public class Main extends Activity implements OnClickListener {

        private static final int REQUEST_ID = 1;
        private static final int HALF = 2;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

setContentView(R.layout.main);

findViewById(R.id.browse_button).setOnClickListener(this); }
}

4. Let's override the on click handler. Here we are using Android's versatile intent mechanism to allow the user to select any application that has registered with the operating system as capable of displaying image files. We use the start activity for result method so the gallery application can return to us the URI of the selected image.

@Override
public void onClick(View v) {
Intent intent = new Intent();

intent.setAction(Intent.ACTION_GET_CONTENT);

intent.addCategory(Intent.CATEGORY_OPENABLE);

intent.setType("image/*");

startActivityForResult(intent, REQUEST_ID);

}
5. The final step is the trickiest. In the on activity result callback we not only load a bitmap, but before assigning it to the widget, the image is scaled down by 50%. A word of warning: You don't have to scale the bitmap by half; however, if the bitmap is too large (and many of the photos I tried were), the application fails silently. It was very frustrating, and I spent a lot of time spinning my wheels trying to debug the issue. Usually the Android framework engineers do a great job between throwing exceptions and spitting out log cat messages letting developers know what's up. In this case, someone dropped the ball.
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
InputStream stream = null;
        if (requestCode == REQUEST_ID && resultCode == Activity.RESULT_OK) {
                try {

stream = getContentResolver().openInputStream(data.getData());

Bitmap original = BitmapFactory.decodeStream(stream);
        ((ImageView)findViewById(R.id.image_holder)).setImageBitmap(Bitmap.createScaledBitmap(original,
original.getWidth()/HALF, original.getHeight()/HALF, true));
} catch (Exception e) {

e.printStackTrace();

}

if (stream != null) { try {

stream.close();

} catch (Exception e) {

e.printStackTrace();

}

}

}

}
With the scaling issue behind us, we are ready to launch our app. Note: There are compatibility issues with some instances of the emulator, so I recommend running the demo on a physical device.

I hope you enjoyed digging a little deeper into the Android intent mechanism. If you haven't yet done so, I encourage you to read all the gory details on Google's official Android developer page.

About

William J Francis began programming computers at age eleven. Specializing in embedded and mobile platforms, he has more than 20 years of professional software engineering under his belt, including a four year stint in the US Army's Military Intellige...

Editor's Picks

Free Newsletters, In your Inbox