Adding Photo Quality Settings on Android
Our latest release of Fulcrum for Android (2.10.0), is adding photo quality options to the settings. This option can be used to scale images taken at the device’s full resolution down to a smaller size. Working with smaller images can significantly improve a number of things (storage usage on the device and server, synchronization times) without having a negative impact on your data.
This is how we’ve implemented it: If a user sets ‘Photo Quality’ to ‘Medium (720)’, then when you capture an image with dimensions of 2048x1536, it will be resized down to 720x540. The largest dimension of the original image becomes the setting’s value (720 in this example), and the image is scaled accordingly.
Often on Android, you start up other applications to handle a certain function for you. While you can use the native camera, you can’t control the photo size it should take. This means we have to resize the image after it’s captured. There are lots of ways to resize an image on Android:
- Use Bitmap.decodeFile to get a Bitmap object, then resize that object with Bitmap.createScaledBitmap
- Use an Android library called glide. They have some info on how to do ‘Transformations’.
- Use an Android library called Picasso. If you scroll down their page a bit, the have a specific example of doing an Image Transformation.
- And many others…
So resizing the image wasn’t a problem. After some logic to figure out which parameter was biggest, etc., we do this:
Bitmap bitmap = Picasso.with(mContext).load(args).resize(width, height).get();
Which is basically something like:
Bitmap bitmap = Picasso.with(mContext).load(args).resize(720, 0).get();
resize method allows you to leave one of the parameters 0. This tells it to scale the image to that dimension, but keep the aspect ratio, to avoid distorting the original.
That was the easy part. It turns out the hard part is saving the Bitmap object back to a file without losing the EXIF data. Fulcrum stores the GPS location data for each photo taken in the app, which many people use for verification and tracking for collection activity, so it’s important to retain. A lot of Googling about how to resize/rotate without losing EXIF turns up stuff like this Stack Overflow post, and that post leads to this one. To save others the leg work: we couldn’t find a way to save a Bitmap object to disk without losing the EXIF data.
So our task became extracting the EXIF from the image before performing operations on it, and then saving it to the altered image. We needed a way to handle nearly all of the original image’s EXIF tags because we had no idea which tags ours users might be making use of. We tried and looked at many methods and libraries for reading and writing EXIF to an image on Android:
- Android’s built in ExifInterface allows read and write, but supports a VERY limited number of EXIF tags, and there is no way to retrieve all of an image’s tags without first knowing their keys.
- We tried writing the Bitmap object’s bytes directly to a file. The resulting file couldn’t be read as an image. Instead of hammering on this, we decided to work on other solutions rather than try to make that file save correctly, only to find out the EXIF still wasn’t preserved.
- The metadata-extractor library by Drew Noakes looked mature and pretty thorough, except it wasn’t going to help us write the EXIF back to the image (a combination of this and Android’s ExifInterface to write wouldn’t have worked either).
I kept going back to, and ended up implementing, the solution mentioned in this post.
We tried using the original Sanselan, and after working on this for some time, we ended up having to switch to an Android variant of Sanselan someone had made, sanselan-android. The keys of some of the EXIF tags on Android were different than what the original Sanselan library was looking for. If we were to use the original library, one tag value might overwrite the value of another, or some tags might not be read at all because the library used some of the keys differently than the Android platform.
There are a few projects that have taken sanselan-android and moved it to GitHub or packaged it up to make it easier to use, but we were already a little uncomfortable using an Android port of a package that hadn’t had a new release in quite some time. So we checked out the source and packaged it into a JAR ourselves.
All of that produced the ‘Photo Quality’ setting for Fulcrum on Android. Images scale down accurately, and EXIF tags are preserved. Check out our public GitHub repos, specifically the sanselan-android repo to get your copy of the sanselan-android JAR we built and the samples-android repo to view our code for image resizing.