In this article you will learn, how images from gallery can be load into your activity no matter if they are local or stored remotely (i.e. in Picasa).
If you are interested in how to load image from gallery to your activity, you almost certainly know, that you have to use
Intent.ACTION_PICK and
startActivityForResult. You probably know, that the result intent, which you will obtain by
onActivityResult contains Uri of the selected image and you can get the Uri by the
getData method. Maybe you have learned, that Uri got by the
getData is not Uri of actual file, but only a kind of "database key".
Commonly used (
wrong) approach uses
ContentResolver query, which is supposed to return cursor, that holds the image file path in the
MediaStore.Images.Media.DATA column.
This method will work well for local files. For files stored remotely, i.e. in Picasa gallery this method will work only on some (older) versions of Android. I have learned, that my Android 2.3 on my LG P350 downloads remote images which I select in gallery to Downloads folder, so they become local. But Android 3.2 on my PackardBell tablet does not do any download automatically and therefore above mentioned approach fails and I get Null pointer error when I try to work with remotely stored images.
Following code shows another approach, which seems to work with both local and remote images well. It uses
ContentProviderClient and
ParcelFileDescriptor. It loads Image to temp file and works with this local copy. Note, thet
_path[0] holds Uri got by
getData method from the result Intent.
@Override
protected Drawable doInBackground(Uri... _path) {
file = null;
// This is the key line. Content provider client gives us access to
// file no matter if it is a local or a remote one
ContentProviderClient client = getContentResolver()
.acquireContentProviderClient(_path[0]);
try {
// Here we save copy of the file to temporary
ParcelFileDescriptor descriptor = client.openFile(_path[0], "r");
AutoCloseInputStream is = new AutoCloseInputStream(descriptor);
file = File.createTempFile("image", ".jpg", getDir(null, MODE_PRIVATE));
OutputStream outS = new FileOutputStream(file);
byte[] buf = new byte[1024];
int len = 0;
while ((len = is.read(buf)) > 0) {
outS.write(buf, 0, len);
}
is.close();
outS.close();
Complete code of test activity follows. To make it work, you have to create activity_galery_get.xml which will provide ImageView and Button.
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/previewFrame"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/imagePreview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="Image to share" >
</ImageView>
<Button
android:id="@+id/bFromGallery"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:onClick="handleFromGallery"
android:text="Select image from gallery" />
</FrameLayout>
Here is the complete activity code:
public class GaleryGet extends Activity {
private final String LOGTAG = getClass().getName();
private final int REQUEST_IMAGE = 33;
private ProgressDialog progressDialog;
private File file = null;
private ImageView imagePreview;
private Context mContext;
/**
* Usual onCreate - nothing special there
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext=this;
setContentView(R.layout.activity_galery_get);
imagePreview = (ImageView) findViewById(R.id.imagePreview);
}
/**
* Button handler. Also very common one. Just create Intent and run it.
*/
public void handleFromGallery(View _view) {
Intent i = new Intent(Intent.ACTION_PICK,
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(i, REQUEST_IMAGE);
}
/**
* Get result from gallery. Well we extract URI from intent and run
* ImageDisplayer
*/
@Override
public void onActivityResult(int requestCode, int resultCode, Intent _intent) {
if (resultCode == Activity.RESULT_OK && requestCode == REQUEST_IMAGE) {
ImageDisplayer displayer = new ImageDisplayer();
if(_intent.getData()!=null){
displayer.execute(_intent.getData());
}
}
}
/**
* This is it. here we can see, how to process Uri from gallery result
* in a proper way to get image displayed.
*
*/
private class ImageDisplayer extends AsyncTask<Uri, Drawable,Drawable> {
@Override
protected void onPreExecute(){
progressDialog = ProgressDialog.show(mContext, "Loading", "");
}
@Override
protected Drawable doInBackground(Uri... _path) {
file = null;
// This is the key line. Content provider client gives us access to
// file no matter if it is a local or a remote one
ContentProviderClient client = getContentResolver()
.acquireContentProviderClient(_path[0]);
try {
// Here we save copy of the file to temporary
ParcelFileDescriptor descriptor = client.openFile(_path[0], "r");
AutoCloseInputStream is = new AutoCloseInputStream(descriptor);
file = File.createTempFile("image", ".jpg", getDir(null, MODE_PRIVATE));
OutputStream outS = new FileOutputStream(file);
byte[] buf = new byte[1024];
int len = 0;
while ((len = is.read(buf)) > 0) {
outS.write(buf, 0, len);
}
is.close();
outS.close();
} catch (RemoteException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (FileNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// And finally, we display the image
String filePath = file.getAbsolutePath();
return Drawable.createFromPath(filePath);
}
@Override
protected void onPostExecute(Drawable _drawable) {
imagePreview.setImageDrawable(_drawable);
progressDialog.dismiss();
}
}
}
Hope this will work for you.