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.