I could really use another set of eyes on this. No one else in the office has a lot of experience working with Android, so I'm on my own with this one.
The goal of this class is to encapsulate an image which can be sourced from a camera or file so that it can be uploaded to one of our servers using a POST request. Just looking at the code, it feels overly-complicated, and I'm afraid that this is super inefficient.
package com.package.android;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.Parcel;
import android.os.Parcelable;
import android.provider.MediaStore;
import android.util.Log;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.package.R;
class UploadableImage implements Parcelable {
public static final String UPLOADABLE_IMAGE = "UPLOADABLE_IMAGE";
public static final Parcelable.Creator<UploadableImage> CREATOR
= new Parcelable.Creator<UploadableImage>() {
public UploadableImage createFromParcel(Parcel in) {
return new UploadableImage(in);
}
public UploadableImage[] newArray(int size) {
return new UploadableImage[size];
}
};
private static final String JPEG_FILE_PREFIX = "IMG_";
private static final String JPEG_FILE_SUFFIX = ".jpg";
private final String TAG = "UploadableImage";
private File file;
private String filepath;
private Uri uri;
private Bitmap bmp;
private Context context;
private AlbumStorageDirFactory mAlbumStorageDirFactory = null;
private int fileSize;
private String descriptionText;
private String nameText;
private String emailText;
public UploadableImage(Context context) {
this.context = context;
file = null;
filepath = null;
uri = null;
bmp = null;
fileSize = -1;
descriptionText = null;
nameText = null;
emailText = null;
setupStorageDirFactory();
}
public UploadableImage(Parcel in) {
readFromParcel(in);
setupStorageDirFactory();
}
static String filePathFromUri(Context context, Uri uri) {
if (uri == null) {
return null;
}
//see http://stackoverflow.com/a/16511111/940217
String filepath = null;
String[] projection = {MediaStore.MediaColumns.DATA};
Context ctx = context.getApplicationContext();
if (ctx != null) {
Cursor cursor = ctx.getContentResolver().query(uri, projection, null, null, null);
if (cursor != null) {
// Here you will get a NPE if cursor is null.
// This can be if you used OI File Manager (or other 3rd party) for picking the media
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA);
filepath = cursor.getString(columnIndex);
cursor.close();
}
} else {
filepath = uri.getPath(); // For OI/Astro/Dropbox, etc.
}
return filepath;
}
private void setupStorageDirFactory() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) {
mAlbumStorageDirFactory = new FroyoAlbumDirFactory();
} else {
mAlbumStorageDirFactory = new BaseAlbumDirFactory();
}
}
public void setContext(Context context) {
this.context = context;
}
public boolean valid() {
return (context != null && this.getFilepath() != null);
}
Uri getUri() {
if (this.uri != null) {
return this.uri;
}
if (this.file != null) {
this.uri = Uri.fromFile(this.file);
return this.uri;
}
if (this.bmp == null || context == null) {
return null;
}
if (this.filepath != null) {
this.uri = Uri.fromFile(new File(this.filepath));
return this.uri;
}
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
this.bmp.compress(Bitmap.CompressFormat.JPEG, 100, bytes);
Context ctx = context.getApplicationContext();
if (ctx == null) {
return uri;
}
String path = MediaStore.Images.Media.insertImage(ctx.getContentResolver(), this.bmp, "Title", null);
this.uri = Uri.parse(path);
return uri;
}
public void setUri(Uri uri) {
Log.d(TAG, "Setting URI to " + uri.toString());
this.uri = uri;
}
private void setBitmapFromUri(Uri uri) {
Uri tempUri = uri;
Bitmap tempBmp = null;
if (this.uri != null && uri == null) {
tempUri = this.uri;
}
try {
tempBmp = MediaStore.Images.Media.getBitmap(context.getContentResolver(), tempUri);
} catch (IOException e) {
Log.e(TAG, "IOException: tried getting the bitmap from the MediaStore using the URI and failed. The URI given was: " + tempUri.toString());
e.printStackTrace();
}
this.bmp = tempBmp;
}
public File createFile() throws IOException {
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = JPEG_FILE_PREFIX + timeStamp + "_";
File albumF = getAlbumDir();
this.file = File.createTempFile(imageFileName, JPEG_FILE_SUFFIX, albumF);
return this.file;
}
public boolean deleteFile() {
return this.file.delete();
}
/* Photo album for this application */
private String getAlbumName() {
return context.getString(R.string.album_name);
}
private File getAlbumDir() {
File storageDir = null;
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
storageDir = mAlbumStorageDirFactory.getAlbumStorageDir(getAlbumName());
if (storageDir != null) {
if (!storageDir.mkdirs()) {
if (!storageDir.exists()) {
Log.w(TAG, "failed to create directory");
return null;
}
}
}
} else {
Log.w(TAG, "External storage is not mounted READ/WRITE.");
}
return storageDir;
}
public File getFile() {
return file;
}
public void setFile(File file) {
this.file = file;
this.filepath = this.file.getAbsolutePath();
}
public Bitmap getBmp() {
if (this.bmp != null) {
return this.bmp;
}
Bitmap resultBitmap = null;
Uri tempUri = this.getUri();
if (tempUri != null) {
setBitmapFromUri(tempUri);
}
if (this.bmp != null) {
resultBitmap = this.bmp;
}
this.bmp = resultBitmap;
return resultBitmap;
}
public void setBmp(Bitmap bmp) {
this.bmp = bmp;
}
private byte[] getBmpAsByteArray(Bitmap data) {
if (data == null) {
return null;
}
ByteArrayOutputStream bos = new ByteArrayOutputStream();
data.compress(Bitmap.CompressFormat.JPEG, 100 /*ignored for PNG*/, bos);
return bos.toByteArray();
}
private void setBitmapFromByteArray(byte[] data) {
this.bmp = BitmapFactory.decodeByteArray(data, 0, data.length);
}
public void updateFilepath() {
if (this.uri != null) {
setFilepath(filePathFromUri(this.context, uri));
}
getFilepath(null);
}
void invalidate() {
if (getFileSize() == 0) {
deleteFile();
}
file = null;
bmp = null;
filepath = null;
fileSize = -1;
uri = null;
}
String getFilepath() {
return getFilepath(null);
}
public void setFilepath(String filepath) {
this.filepath = filepath;
}
String getFilepath(Uri uri) {
if (this.filepath != null) {
return this.filepath;
}
if (this.file != null) {
return this.file.getAbsolutePath();
}
if (uri == null) {
uri = getUri();
}
return filePathFromUri(this.context, uri);
}
public Bitmap getScaledBitmap(int width, int height) {
Bitmap scaledBmp = null;
InputStream is;
Bitmap oBmp = this.getBmp();
if (oBmp != null) {
Log.d(TAG, "scaled bitmap was able to use a bitmap");
return Bitmap.createScaledBitmap(oBmp, width, height, false);
}
Log.d(TAG, "scaled bitmap was not able to use a bitmap and will try to get it from a stream.");
try {
is = getInputStream();
/* Get the size of the image */
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
BitmapFactory.decodeStream(is, null, bmOptions);
int photoW = bmOptions.outWidth;
int photoH = bmOptions.outHeight;
is.close();
is = getInputStream();
/* Figure out which way needs to be reduced less */
int scaleFactor = 1;
if ((width > 0) || (height > 0)) {
scaleFactor = Math.min(photoW / width, photoH / height);
}
/* Set bitmap options to scale the image decode target */
bmOptions.inJustDecodeBounds = false;
bmOptions.inSampleSize = scaleFactor;
bmOptions.inPurgeable = true;
/* Decode the JPEG file into a Bitmap */
scaledBmp = BitmapFactory.decodeStream(is, null, bmOptions);
is.close();
} catch (FileNotFoundException e) {
Log.e(TAG, "Could not resize the bitmap - File Not Found Exception");
e.printStackTrace();
} catch (IOException e) {
Log.e(TAG, "IO Exception while trying to resize the bitmap");
e.printStackTrace();
}
return scaledBmp;
}
protected int sizeOf(Bitmap data) {
/*
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
return data.getAllocationByteCount();
} else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB_MR1) {
return data.getRowBytes() * data.getHeight();
} else {
return data.getByteCount();
}
// NONE OF THE ABOVE RETURN ACCURATE RESULTS!
// A Bitmap, when stored as a file takes up more room because it represents
// full pixel data and is not compressed on disk.
*/
byte[] bitmapdata = getBmpAsByteArray(data);
return (bitmapdata == null) ? 0 : bitmapdata.length;
}
public InputStream getInputStream() {
InputStream is = null;
Bitmap bmp = this.getBmp();
if (this.file == null || this.bmp == null) {
loadResources();
}
if (bmp != null) {
Log.i(TAG, "getting input stream from bitmap");
byte[] bitmapdata = getBmpAsByteArray(bmp);
this.fileSize = bitmapdata.length;
is = new ByteArrayInputStream(bitmapdata);
} else {
Log.i(TAG, "getting input stream from file");
try {
is = new FileInputStream(this.file);
} catch (FileNotFoundException e) {
Log.e(TAG, "could not get an input stream from file with path: " + getFilepath());
e.printStackTrace();
}
}
return is;
}
private void loadResources() {
Uri tempUri = getUri();
if (tempUri != null) {
this.file = new File(tempUri.getPath());
}
try {
this.bmp = MediaStore.Images.Media.getBitmap(context.getContentResolver(), tempUri);
} catch (IOException e) {
Log.e(TAG, "IOException when trying to load the bitmap from URI: " + tempUri);
}
}
public long getFileSize() {
if (this.fileSize != -1) {
return this.fileSize;
}
this.fileSize = sizeOf(getBmp());
return this.fileSize;
}
public String getDescriptionText() {
return descriptionText;
}
public void setDescriptionText(String descriptionText) {
this.descriptionText = descriptionText;
}
public String getNameText() {
return nameText;
}
public void setNameText(String nameText) {
this.nameText = nameText;
}
public String getEmailText() {
return emailText;
}
public void setEmailText(String emailText) {
this.emailText = emailText;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(filepath);
dest.writeString(uri.toString());
dest.writeInt(fileSize);
if (fileSize >= 0) {
dest.writeByteArray(getBmpAsByteArray(getBmp()));
}
dest.writeString(descriptionText);
dest.writeString(nameText);
dest.writeString(emailText);
}
private void readFromParcel(Parcel in) {
file = null;
filepath = in.readString();
uri = Uri.parse(in.readString());
fileSize = in.readInt();
if (fileSize >= 0) {
byte[] bmpData = new byte[fileSize];
in.readByteArray(bmpData);
setBitmapFromByteArray(bmpData);
}
descriptionText = in.readString();
nameText = in.readString();
emailText = in.readString();
}
}