I am building an Android application and there are five Activity
classes or if you're familiar with the MVC pattern they would usually be the Controller
classes.
Specifically a User
will enter one of these 5 Activity
classes (by navigating throughout the app) and sometimes they might upload a photo. Now the code for uploading a photo follows a very similar pattern. Please note all this code is repeated 5 times in all 5 classes (YUCK).
Global Variables:
/*
Tracking
*/
private static final int TAKE_PHOTO_REQUEST = 1;
private static final int GET_FROM_GALLERY = 2;
private Uri mUri;
private String mCurrentPhotoPath;
private File mFile;
private TypedFile mTypedFile; // For Retrofit
User hits Photo Upload Button, and a AlertDialog pops up:
private void showFileOptions() {
new AlertDialog.Builder(this)
.setItems(R.array.uploadOptions, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case 0:
dispatchTakePicture();
break;
case 1:
dispatchUploadFromGallery();
break;
}
}
})
.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
})
.show();
}
dispatchTakePicture:
/*
Take picture from your camera
*/
private void dispatchTakePicture() {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// Make sure that there is a camera activity to handle the intent
if (intent.resolveActivity(getPackageManager()) != null) {
// Create the File where the mTypedFile would go
File picFile = null;
try {
picFile = createImageFile();
mFile = picFile;
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
}
// Continue only if the file was successfully created
if (picFile != null) {
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(picFile));
startActivityForResult(intent, TAKE_PHOTO_REQUEST);
}
}
}
dispatchUploadFromGallery:
/*
Take a mTypedFile from your gallery
*/
private void dispatchUploadFromGallery() {
// Launch gallery intent
startActivityForResult(new Intent(Intent.ACTION_PICK, MediaStore
.Images.Media.INTERNAL_CONTENT_URI), GET_FROM_GALLERY);
}
Please note that startActivityForResult
gets called in both of these methods. Next up is the createImageFile()
method if the user wants to take a picture from the Camera
API:
private File createImageFile() throws IOException {
// Create the Image File name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).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 the file, path for ACTION_VIEW intents
mCurrentPhotoPath = "file:" + image.getAbsolutePath();
mUri = Uri.fromFile(image);
return image;
}
Now finally our startActivityForResult(...)
method:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == TAKE_PHOTO_REQUEST && resultCode == RESULT_OK) {
startUploadProgress();
showContainer();
mTypedFile = new TypedFile("image/*", mFile);
RotatePictureHelper.rotatePicture(mFile, ExampleActivity.this, mAttachment); // Helper class to rotate pictures
mBus.post(new LoadUploadFileEvent(mTypedFile));
} else if (requestCode == GET_FROM_GALLERY && resultCode == RESULT_OK) {
startUploadProgress();
showContainer();
mUri = data.getData();
mTypedFile = UriHelper.handleUri(mUri, this); // Helper class to handle bitmap manipulation
mFile = mTypedFile.file();
mBus.post(new LoadUploadFileEvent(mTypedFile));
} else if (resultCode != Activity.RESULT_CANCELED) {
Toast.makeText(this, R.string.generalError, Toast.LENGTH_LONG).show();
}
}
Note that I have already created helper classes to handle bitmap manipulation and picture rotation issues.
STILL, this is very ugly ugly code, and to have this repeated in 5 classes.
I have a few ideas in mind right now:
- Create a service and pass in needed variables to that service to handle this.
- Moving
AlertDialog
options to a helper class and call differentAlertDialogs
based oninstanceOf
whateverActivity
is calling it. - Should I create a parent
Activity
class that has these methods and then extend the 5Activity
child classes and call these methods?