Android Development. Camera Intent from a fragment, I finally managed to take a photo and store it.

I recently had to take on a rush job as part of my new role, It’s been over two years since I’ve done any java coding so have forgotten a great deal and any development for android I’ve done using appinventor2. I couldn’t use that for this survey project.

So whilst developing my survey project I’d been struggling with taking a photo and storing it in a folder on a phone. I’d been following various posts on various locations including stackoverflow.com I wish I’d gone to the official documentation first, however, it didn’t show up in my searches. I followed the process through from simply taking a photo and showing it on a form through to storing the image onto the device. Which is really quite complicated with setting the authority in the manifest and ensuring that matches the string in FileProvider.getUriForFile in the code. However, I got there and managed to save into the folder I wanted.

My understanding is as follows

  • Add into the Manifest file,AndriodManifest.xml permissions for Storage and Camera

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

  • Add the provider information to the manifest
<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="com.biosplus.fileprovider"  
<!-- Replace the above with what you need to use -->
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths">
    </meta-data>
</provider>
  • Within an onclick event-listener  create a method or methods to do the following
  • At the start of the class create a Request code for the camera e.g.
  • Also, create a String for the full path of the saved image.
static final int CAMERA_REQUEST_CODE = 1;

From my research, I assume this is just a unique identifier and can be whatever value toy

  • Create an intent e.g.
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
  •  Set up the URI for the image file to be saved, I struggled here for a while as I was working from a fragment and not from an activity, it took me a while to work out how to get the context. I was using getActivity() but found that in this particular case below that getContext() Worked for me.
if (cameraIntent.resolveActivity(getContext().getPackageManager())
    != null) {
    File photoFile = null;
    try {
/* This method will create a file with the current timestamp
   Really I feel this try catch is unnecessary 
   as the createImageFile 
   method I created had a try catch in there but 
   AndroidStudio complained.
  
*/


        photoFile = createImageFile(); 

    } catch (IOException e) {
        e.printStackTrace();
    }
    if (photoFile != null) {
        Uri photoURI = FileProvider.getUriForFile(getActivity()
,                      "com.biosplus.fileprovider", photoFile);
        cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);

// This will open the 
        startActivityForResult(cameraIntent, CAMERA_REQUEST_CODE);
    }
} else {

    // This will show in the event of a problem.

    Toast.makeText(getActivity().getBaseContext(),
       "Sorry there is a problem with taking photos.", 
       Toast.LENGTH_SHORT).show();
}

What I found interesting was the fact that the file is created before the photo is taken. It took me a while to get my head around that fact. The createImageFile()method will appear later in this post.

  • Create an onActivityResult method, what took me a while to understand was that the data returned from the activity was null as I’d specified a URI. I thought something had gone wrong! Then use this method to show further process the file.
public void onActivityResult(int requestCode, 
                             int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    //CALL THIS METHOD EVER
    if (requestCode == CAMERA_REQUEST_CODE && resultCode == RESULT_OK) {

        // This will add the photo to the users gallery

        galleryAddPic();
    
        /* This adds the image to the screen.
           For this app this creates an imageview each time a photo
           is taken and adds it to a Flexbox layout
       */
  
        setPic();


        Toast.makeText(getActivity(), "Photo Taken And Saved", 
                       Toast.LENGTH_SHORT).show();
    } else {
        Toast.makeText(getActivity(), "The photo was not saved", 
                       Toast.LENGTH_SHORT).show();
    }
}

The galleryAddPic method is taken from the excellent take photos android documentation where this code is located, I copy pasted and adjusted due to a tight deadline.

https://developer.android.com/training/camera/photobasics

The setPic code is tied to my survey form so I won’t post that here but the core code to save it to an imageView is

// Get the dimensions of the View
/* imgViewWidth & imgViewHeight are two integers I created manually
   as I cannot work out how to get the imageView width and height 
   as it's created on the fly and imageView.getWidth() and .getHeight 
  return 0.
*/

int targetW = imgViewWidth;
int targetH = imgViewHeight;

// Get the dimensions of the bitmap
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(currentPhotoPath, bmOptions);
int photoW = bmOptions.outWidth;
int photoH = bmOptions.outHeight;

// Determine how much to scale down the image
int scaleFactor = Math.min(photoW / targetW, photoH / targetH);

// Decode the image file into a Bitmap sized to fill the View
bmOptions.inJustDecodeBounds = false;
bmOptions.inSampleSize = scaleFactor;
bmOptions.inPurgeable = true;

Bitmap bitmap = BitmapFactory.decodeFile(currentPhotoPath, bmOptions);
photoImageView.setImageBitmap(bitmap);

So that is the basics of taking a photo and storing the taken image. The google docs explain pretty well, I hope this page helps somebody.

Here is the createImageFile method

private File createImageFile() throws IOException {

    // A seperate Method to get the timestamp ina formatted manner
    String timeStamp = getCurrentTimeStamp();
    String imageFileName = "JPEG_" + timeStamp + "_";
    File storageDir = getExternalStoragePublicDirectory(
                      Environment.DIRECTORY_PICTURES);
    String imgStore = storageDir.getAbsolutePath() 
                      + File.separator + "SurveyPhotos";
    storageDir = new File(imgStore);
    if (!storageDir.exists()) {
        storageDir.mkdirs();
    }
    File imageFile = null;
    try {
        imageFile = File.createTempFile(imageFileName, 
                                        ".jpg", storageDir);
    } catch (IOException e) {
        e.printStackTrace();
    }
    currentPhotoPath = imageFile.getAbsolutePath();
    return imageFile;
}

public static String getCurrentTimeStamp() {
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss")
                                           .format(new Date());
    return timeStamp;
}

Any corrections to improve this code are more than welcome, as I’m still trying to get my head around android development. This project has been a good opportunity to learn more about it. My biggest frustration at the moment is Android Studio.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.