Capturing, Handling, Displaying, and Sharing Images in an Android App

With advances in imaging technology and tighter software integration in devices, users are turning to their smartphone or tablet as the primary camera used in their daily workflow.  Some popular actions users are performing are around capturing and sharing images taken with the onboard camera.  Integrating image capture and image sharing actions inside of an app saves the user time and gives a content focused experience that keeps a user in the app.  For example, a restaurant style app could allow users to capture images of their food and share the images with their friends or others in the restaurant using the same app.  This blog shows an example of capturing, handling, displaying, and sharing images in an Android app.

In this example, two activities are used, a MainActivity and ImageDisplayActivity.  The MainActivity captures images from the camera and displays them as thumbnail images inside of a dynamic GridView.  The ImageDisplayActivity is launched when the user touches a thumbnail image and shows a larger size image.  To share images, a sharing button is located in the action bar and will share the image to different apps on the device such as messaging.      

1.  Initializing the GridView
The GridView is initialized with an ImageAdapter class that adds an ImageView element for each image file path contained in a List structure called myList.  When GridView elements are touched, an OnItemClickListener and Intent is setup to start the ImageDisplayActivity.  This Intent passes the current image file path to the ImageDisplayActivity.

public class MainActivity extends Activity
{
      private List<String> myList;  // String list that contains file paths to images 
      private GridView gridview;   
      private String mCurrentPhotoPath;  // File path to the last image captured 
 
      @Override
      protected void onCreate(Bundle savedInstanceState)
      {
         super.onCreate(savedInstanceState);
         setContentView(R.layout. activity_main);
              
         // Initialize GridView
         gridview = (GridView) findViewById(R.id.gridView1);
         gridview.setAdapter(new ImageAdapter(this));
                     
         // Initialize GridView Thumbnail Click Handler 
         gridview.setOnItemClickListener(new AdapterView.OnItemClickListener()
         {
            @Override
            public void onItemClick(AdapterView<?> parent, View v, int position, long id)
            {
                    // Send File Path to Image Display Activity
                    Intent intent = new Intent(getApplicationContext(), ImageDisplayActivity.class);
                    intent.putExtra("path", myList.get(position));
                    startActivity(intent);
            }
           });
      }
public class ImageAdapter extends BaseAdapter
{
      private Context mContext;     
      public ImageAdapter(Context c)
      {
            mContext = c;
      }
      public int getCount()
      {
            return myList.size();
      }
      public Object getItem(int position)
      {
            return null;
      }
      public long getItemId(int position)
      {
            return 0;
      }
      public View getView(int position, View convertView, ViewGroup parent)
      {
            ImageView imageView;
                    
            if (convertView == null)
            {
                  imageView = new ImageView(mContext);
                  imageView.setLayoutParams(new GridView.LayoutParams(85, 85));
                  imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
                  imageView.setPadding(8, 8, 8, 8);
            }
            else
            {
                  imageView = (ImageView) convertView;
            }
                     
            BitmapFactory.Options bmOptions = new BitmapFactory.Options();
            bmOptions. inJustDecodeBounds = false ;
            bmOptions. inSampleSize = 4;
            bmOptions. inPurgeable = true ;
            Bitmap bitmap = BitmapFactory.decodeFile(myList.get(position), bmOptions);
                     
            imageView.setImageBitmap(bitmap);
                     
            return imageView;
      }
}


2.  Capturing the Image
To enable the camera feature and give permission for creating files, the AndroidManifest.xml is updated with the XML below.  

<uses-feature android:name="android.hardware.camera" android:required= "true" />
<uses-permission android:name ="android.permission.WRITE_EXTERNAL_STORAGE" />

To capture an image, a file is created and the Uri path is passed to an Intent that launches the stock camera app onboard the device.

static final int REQUEST_TAKE_PHOTO = 1;
private void takePicture()
{
     Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE );
     if (takePictureIntent.resolveActivity(getPackageManager()) != null)
     {
          File photoFile = null;
          try
          {
               photoFile = createImageFile();
          }
          catch (IOException ex)
          {
               // Error occurred while creating the File
          }
          // Continue only if the File was successfully created
          if (photoFile != null)
          {
               takePictureIntent.putExtra(MediaStore. EXTRA_OUTPUT,
               Uri. fromFile(photoFile));
               startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
          }
     }
}
private File createImageFile() throws IOException
{    
        // Create an image file name    
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format( new Date());
        String imageFileName = "JPEG_" + timeStamp + "_" ;
        File storageDir = Environment.getExternalStoragePublicDirectory(Environment. DIRECTORY_PICTURES);
        File image = File. createTempFile(
                     imageFileName,  /* prefix */
                     ".jpg",         /* suffix */
                     storageDir      /* directory */
        );
              
        // Save a file: path for use with ACTION_VIEW intents
        mCurrentPhotoPath = image.getAbsolutePath();
        return image;
}

  
3.  Handling the Image
After an image is captured and accepted, the handler below is invoked.  The handler adds the image to the gallery, adds the file path to myList, and refreshes the GridView to show the new thumbnail image.

@Override protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
    if (requestCode == REQUEST_TAKE_PHOTO && resultCode == RESULT_OK)
        {
                // Save Image To Gallery
                Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE );
                File f = new File(mCurrentPhotoPath );
                Uri contentUri = Uri.fromFile(f);
                mediaScanIntent.setData(contentUri);
                this.sendBroadcast(mediaScanIntent);
                     
                // Add Image Path To List
                myList.add( mCurrentPhotoPath);
                     
                // Refresh Gridview Image Thumbnails
                gridview.invalidateViews();
         }
}


4.  Displaying the Image
When a user touches a thumbnail image in the GridView, the handler discussed above in Initializing the GridView is invoked.  The handler passes the current image file path and starts the ImageDisplayActivity.  Once the ImageDisplayActivity is started, the JPEG file is decoded into a Bitmap and displayed in an ImageView.

public class ImageDisplayActivity extends Activity
{
        private String path;
       
        @Override
        protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_display1);
              setupActionBar();
              
              // Get Image Path
              path = getIntent().getExtras().getString("path");
              ImageView imageView = (ImageView)findViewById(R.id.imageView1);
              
              // Get Image
              BitmapFactory.Options bmOptions = new BitmapFactory.Options();
              bmOptions. inJustDecodeBounds = false;
              bmOptions. inSampleSize = 4;
              bmOptions. inPurgeable = true ;
              Bitmap bitmap = BitmapFactory.decodeFile(path, bmOptions);
              
              // Display Image
              imageView.setImageBitmap(bitmap);
}

Another approach for displaying an image is using an intent to invoke the stock Android Image Viewer app.  This is useful if the app does not require a custom layout.

5.  Sharing the Image
To add the sharing button on the action bar, the menu/display.xml file is updated with the XML below. 

         <item 
         android:id="@+id/menu_item_share"
         android:showAsAction="ifRoom"
         android:title="@string/share_menu"
         android:actionProviderClass="android.widget.ShareActionProvider" />

When the sharing button is touched, a list of providers on the device is shown.  When the user selects a provider, an intent passes the image to that provider when it starts.

private ShareActionProvider mShareActionProvider;
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.display, menu);
              
        // Locate MenuItem with ShareActionProvider
        MenuItem item = menu.findItem(R.id.menu_item_share);
              
        // Fetch and store ShareActionProvider
        mShareActionProvider = (ShareActionProvider)item.getActionProvider();
                              
        // Issue share intent
        if (mShareActionProvider != null)
        {
                File imageFile = new File(path );
                Uri imageUri = Uri.fromFile(imageFile);
                Intent shareIntent = new Intent();
                shareIntent.setAction(Intent.ACTION_SEND);
                shareIntent.putExtra(Intent.EXTRA_STREAM, imageUri);
                shareIntent.setType("image/jpeg");
                mShareActionProvider.setShareIntent(shareIntent);
         }
         return true;
}

6.  Saving & Restoring the Image List
As many images are captured and the device undergoes normal usage, the app may be started and stopped many times.  To give the user a seamless experience, the list of images contained in myList is saved in onStop() and restored with the additional code added to onCreate() in the MainActivity.

@Override
protected void onStop()
{
    super.onStop();

        SharedPreferences settings = getSharedPreferences(getString(R.string.appSettings), MODE_PRIVATE);
        SharedPreferences.Editor editor = settings.edit();

        // Save Image List Size
        editor.putInt("myList.size()", myList.size());
              
        // Save Image List
        for (int i=0; i < myList.size(); i++)
        {
            editor.putString(String.format("myList[%d]", i), myList.get(i));
        }
        editor.commit();
}


@Override
protected void onCreate(Bundle savedInstanceState)
{
        super.onCreate(savedInstanceState);
        setContentView(R.layout. activity_main);
              
        SharedPreferences settings = getSharedPreferences(getString(R.string.appSettings), MODE_PRIVATE);
              
        // Initialize List
        myList = new ArrayList<String>();
        myList.clear();
              
        // Retrieve Image List Size
        int size = settings.getInt("myList.size()", 0);
       
        // Retrieve Image List
        for (int i=0; i < size; i++)
        {
             String imagePath = settings.getString(String.format("myList[%d]", i),"");
             myList.add(imagePath);
        }

 

To learn more about the topics discussed in this blog, please see the links below:
http://developer.android.com/training/sharing/send.html
http://developer.android.com/reference/android/app/Activity.html
http://developer.android.com/training/sharing/shareaction.html#set-share-intent
http://developer.android.com/training/camera/photobasics.html
http://developer.android.com/guide/topics/ui/layout/gridview.html
http://developer.android.com/training/basics/data-storage/shared-preferences.html

++This sample source code is released under the Intel Sample Source Code License 

For more complete information about compiler optimizations, see our Optimization Notice.

Comments