/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cap.shot;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
* Lolcat builder activity.
*
* Instructions:
* (1) Take photo of cat using Camera
* (2) Run LolcatActivity
* (3) Pick photo
* (4) Add caption(s)
* (5) Save and share
*
* See README.txt for a list of currently-missing features and known bugs.
*/
public class LolcatActivity extends Activity
implements View.OnClickListener {
private static final String TAG = "LolcatActivity";
// Location on the SD card for saving lolcat images
private static final String LOLCAT_SAVE_DIRECTORY = "lolcats/";
// Mime type / format / extension we use (must be self-consistent!)
private static final String SAVED_IMAGE_EXTENSION = ".png";
private static final Bitmap.CompressFormat SAVED_IMAGE_COMPRESS_FORMAT =
Bitmap.CompressFormat.PNG;
private static final String SAVED_IMAGE_MIME_TYPE = "image/png";
// UI Elements
private Button mPickButton;
private Button mCaptionButton;
private Button mSaveButton;
private Button mClearCaptionButton;
private Button mClearPhotoButton;
private LolcatView mLolcatView;
private AlertDialog mCaptionDialog;
private ProgressDialog mSaveProgressDialog;
private AlertDialog mSaveSuccessDialog;
private Handler mHandler;
private Uri mPhotoUri;
private String mSavedImageFilename;
private Uri mSavedImageUri;
private MediaScannerConnection mMediaScannerConnection;
// Request codes used with startActivityForResult()
private static final int PHOTO_PICKED = 1;
// Dialog IDs
private static final int DIALOG_CAPTION = 1;
private static final int DIALOG_SAVE_PROGRESS = 2;
private static final int DIALOG_SAVE_SUCCESS = 3;
// Keys used with onSaveInstanceState()
private static final String PHOTO_URI_KEY = "photo_uri";
private static final String SAVED_IMAGE_FILENAME_KEY = "saved_image_filename";
private static final String SAVED_IMAGE_URI_KEY = "saved_image_uri";
private static final String TOP_CAPTION_KEY = "top_caption";
private static final String BOTTOM_CAPTION_KEY = "bottom_caption";
private static final String CAPTION_POSITIONS_KEY = "caption_positions";
@Override
protected void onCreate(Bundle icicle) {
Log.i(TAG, "onCreate()... icicle = " + icicle);
super.onCreate(icicle);
setContentView(R.layout.lolcat_activity);
// Look up various UI elements
mPickButton = (Button) findViewById(R.id.pick_button);
mPickButton.setOnClickListener(this);
mCaptionButton = (Button) findViewById(R.id.caption_button);
mCaptionButton.setOnClickListener(this);
mSaveButton = (Button) findViewById(R.id.save_button);
mSaveButton.setOnClickListener(this);
mClearCaptionButton = (Button) findViewById(R.id.clear_caption_button);
// This button doesn't exist in portrait mode.
if (mClearCaptionButton != null) mClearCaptionButton.setOnClickListener(this);
mClearPhotoButton = (Button) findViewById(R.id.clear_photo_button);
mClearPhotoButton.setOnClickListener(this);
mLolcatView = (LolcatView) findViewById(R.id.main_image);
// Need one of these to call back to the UI thread
// (and run AlertDialog.show(), for that matter)
mHandler = new Handler();
mMediaScannerConnection = new MediaScannerConnection(this, mMediaScanConnClient);
if (icicle != null) {
Log.i(TAG, "- reloading state from icicle!");
restoreStateFromIcicle(icicle);
}
}
@Override
protected void onResume() {
Log.i(TAG, "onResume()...");
super.onResume();
updateButtons();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
Log.i(TAG, "onSaveInstanceState()...");
super.onSaveInstanceState(outState);
// State from the Activity:
outState.putParcelable(PHOTO_URI_KEY, mPhotoUri);
outState.putString(SAVED_IMAGE_FILENAME_KEY, mSavedImageFilename);
outState.putParcelable(SAVED_IMAGE_URI_KEY, mSavedImageUri);
// State from the LolcatView:
// (TODO: Consider making Caption objects, or even the LolcatView
// itself, Parcelable? Probably overkill, though...)
outState.putString(TOP_CAPTION_KEY, mLolcatView.getTopCaption());
outState.putString(BOTTOM_CAPTION_KEY, mLolcatView.getBottomCaption());
outState.putIntArray(CAPTION_POSITIONS_KEY, mLolcatView.getCaptionPositions());
}
/**
* Restores the activity state from the specified icicle.
* @see onCreate()
* @see onSaveInstanceState()
*/
private void restoreStateFromIcicle(Bundle icicle) {
Log.i(TAG, "restoreStateFromIcicle()...");
// State of the Activity:
Uri photoUri = icicle.getParcelable(PHOTO_URI_KEY);
Log.i(TAG, " - photoUri: " + photoUri);
if (photoUri != null) {
loadPhoto(photoUri);
}
mSavedImageFilename = icicle.getString(SAVED_IMAGE_FILENAME_KEY);
mSavedImageUri = icicle.getParcelable(SAVED_IMAGE_URI_KEY);
// State of the LolcatView:
String topCaption = icicle.getString(TOP_CAPTION_KEY);
String bottomCaption = icicle.getString(BOTTOM_CAPTION_KEY);
int[] captionPositions = icicle.getIntArray(CAPTION_POSITIONS_KEY);
Log.i(TAG, " - captions: '" + topCaption + "', '" + bottomCaption + "'");
if (!TextUtils.isEmpty(topCaption) || !TextUtils.isEmpty(bottomCaption)) {
mLolcatView.setCaptions(topCaption, bottomCaption);
mLolcatView.setCaptionPositions(captionPositions);
}
}
@Override
protected void onDestroy() {
Log.i(TAG, "onDestroy()...");
super.onDestroy();
clearPhoto(); // Free up some resources, and force a GC
}
// View.OnClickListener implementation
public void onClick(View view) {
int id = view.getId();
Log.i(TAG, "onClick(View " + view + ", id " + id + ")...");
switch (id) {
case R.id.pick_button:
Log.i(TAG, "onClick: pick_button...");
Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
intent.setType("image/*");
// Note: we could have the "crop" UI come up here by
// default by doing this:
// intent.putExtra("crop", "true");
// (But watch out: if you do that, the Intent that comes
// back to onActivityResult() will have the URI (of the
// cropped image) in the "action" field, not the "data"
// field!)
startActivityForResult(intent, PHOTO_PICKED);
break;
case R.id.caption_button:
Log.i(TAG, "onClick: caption_button...");
showCaptionDialog();
break;
case R.id.save_button:
Log.i(TAG, "onClick: save_button...");
saveImage();
break;
case R.id.clear_caption_button:
Log.i(TAG, "onClick: clear_caption_button...");
clearCaptions();
updateButtons();
break;
case R.id.clear_photo_button:
Log.i(TAG, "onClick: clear_photo_button...");
clearPhoto(); // Also does clearCaptions()
updateButtons();
break;
default:
Log.w(TAG, "Click from unexpected source: " + view + ", id " + id);
break;
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.i(TAG, "onActivityResult(request " + requestCode
+ ", result " + resultCode + ", data " + data + ")...");
if (resultCode != RESULT_OK) {
Log.i(TAG, "==> result " + resultCode + " from subactivity! Ignoring...");
Toast t = Toast.makeText(this, R.string.lolcat_nothing_picked, Toast.LENGTH_SHORT);
t.show();
return;
}
if (resultCode == RESULT_CANCELED) {
Toast t = Toast.makeText(this, R.string.cancelled, Toast.LENGTH_SHORT);
t.show();
return;
}
if (requestCode == PHOTO_PICKED) {
// "data" is an Intent containing (presumably) a URI like
// "content://media/external/images/media/3".
if (data == null) {
Log.w(TAG, "Null data, but RESULT_OK, from image picker!");
Toast t = Toast.makeText(this, R.string.lolcat_nothing_picked,
Toast.LENGTH_SHORT);
t.show();
return;
}
if (data.getData() == null) {
Log.w(TAG, "'data' intent from image picker contained no data!");
Toast t = Toast.makeText(this, R.string.lolcat_nothing_picked,
Toast.LENGTH_SHORT);
t.show();
return;
}
loadPhoto(data.getData());
updateButtons();
}
}
/**
* Updates the enabled/disabled state of the onscreen buttons.
*/
private void updateButtons() {
Log.i(TAG, "updateButtons()...");
// mPickButton is always enabled.
// Do we have a valid photo and/or caption(s) yet?
Drawable d = mLolcatView.getDrawable();
// Log.i(TAG, "===> current mLolcatView drawable: " + d);
boolean validPhoto = (d != null);
boolean validCaption = mLolcatView.hasValidCaption();
mCaptionButton.setText(validCaption
? R.string.lolcat_change_captions : R.string.lolcat_add_captions);
mCaptionButton.setEnabled(validPhoto);
mSaveButton.setEnabled(validPhoto && validCaption);
if (mClearCaptionButton != null) {
mClearCaptionButton.setEnabled(validPhoto && validCaption);
}
mClearPhotoButton.setEnabled(validPhoto);
}
/**
* Clears out any already-entered captions for this lolcat.
*/
private void clearCaptions() {
mLolcatView.clearCaptions();
// Clear the text fields in the caption dialog too.
if (mCaptionDialog != null) {
EditText topText = (EditText) mCaptionDialog.findViewById(R.id.top_edittext);
topText.setText("");
EditText bottomText = (EditText) mCaptionDialog.findViewById(R.id.bottom_edittext);
bottomText.setText("");
topText.requestFocus();
}
// This also invalidates any image we've previously written to the
// SD card...
mSavedImageFilename = null;
mSavedImageUri = null;
}
/**
* Completely resets the UI to its initial state, with no photo
* loaded, and no captions.
*/
private void clearPhoto() {
mLolcatView.clear();
mPhotoUri = null;
mSavedImageFilename = null;
mSavedImageUri = null;
clearCaptions();
// Force a gc (to be sure to reclaim the memory used by our
// potentially huge bitmap):
System.gc();
}
/**
* Loads the image with the specified Uri into the UI.
*/
private void loadPhoto(Uri uri) {
Log.i(TAG, "loadPhoto: uri = " + uri);
clearPhoto(); // Be sure to release the previous bitmap
// before creating another one
mPhotoUri = uri;
// A new photo always starts out uncaptioned.
clearCaptions();
// Load the selected photo into our ImageView.
mLolcatView.loadFromUri(mPhotoUri);
}
private void showCaptionDialog() {
// If the dialog already exists, always reset focus to the top
// item each time it comes up.
if (mCaptionDialog != null) {
EditText topText = (EditText) mCaptionDialog.findViewById(R.id.top_edittext);
topText.requestFocus();
}
showDialog(DIALOG_CAPTION);
}
private void showSaveSuccessDialog() {
// If the dialog already exists, update the body text based on the
// current values of mSavedImageFilename and mSavedImageUri
// (otherwise the dialog will still have the body text from when
// it was first created!)
if (mSaveSuccessDialog != null) {
updateSaveSuccessDialogBody();
}
showDialog(DIALOG_SAVE_SUCCESS);
}
private void updateSaveSuccessDialogBody() {
if (mSaveSuccessDialog == null) {
throw new IllegalStateException(
"updateSaveSuccessDialogBody: mSaveSuccessDialog hasn't been created yet");
}
String dialogBody = String.format(
getResources().getString(R.string.lolcat_save_succeeded_dialog_body_format),
mSavedImageFilename, mSavedImageUri);
mSaveSuccessDialog.setMessage(dialogBody);
}
@Override
protected Dialog onCreateDialog(int id) {
Log.i(TAG, "onCreateDialog(id " + id + ")...");
// This is only run once (per dialog), the very first time
// a given dialog needs to be shown.
switch (id) {
case DIALOG_CAPTION:
LayoutInflater factory = LayoutInflater.from(this);
final View textEntryView = factory.inflate(R.layout.lolcat_caption_dialog, null);
mCaptionDialog = new AlertDialog.Builder(this)
.setTitle(R.string.lolcat_caption_dialog_title)
.setIcon(0)
.setView(textEntryView)
.setPositiveButton(
R.string.lolcat_caption_dialog_ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
Log.i(TAG, "Caption dialog: OK...");
updateCaptionsFromDialog();
}
})
.setNegativeButton(
R.string.lolcat_caption_dialog_cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
Log.i(TAG, "Caption dialog: CANCEL...");
// Nothing to do here (for now at least)
}
})
.create();
return mCaptionDialog;
case DIALOG_SAVE_PROGRESS:
mSaveProgressDialog = new ProgressDialog(this);
mSaveProgressDialog.setMessage(getResources().getString(R.string.lolcat_saving));
mSaveProgressDialog.setIndeterminate(true);
mSaveProgressDialog.setCancelable(false);
return mSaveProgressDialog;
case DIALOG_SAVE_SUCCESS:
mSaveSuccessDialog = new AlertDialog.Builder(this)
.setTitle(R.string.lolcat_save_succeeded_dialog_title)
.setIcon(0)
.setPositiveButton(
R.string.lolcat_save_succeeded_dialog_view,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
Log.i(TAG, "Save dialog: View...");
viewSavedImage();
}
})
.setNeutralButton(
R.string.lolcat_save_succeeded_dialog_share,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
Log.i(TAG, "Save dialog: Share...");
shareSavedImage();
}
})
.setNegativeButton(
R.string.lolcat_save_succeeded_dialog_cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
Log.i(TAG, "Save dialog: CANCEL...");
// Nothing to do here...
}
})
.create();
updateSaveSuccessDialogBody();
return mSaveSuccessDialog;
default:
Log.w(TAG, "Request for unexpected dialog id: " + id);
break;
}
return null;
}
private void updateCaptionsFromDialog() {
Log.i(TAG, "updateCaptionsFromDialog()...");
if (mCaptionDialog == null) {
Log.w(TAG, "updateCaptionsFromDialog: null mCaptionDialog!");
return;
}
// Get the two caption strings:
EditText topText = (EditText) mCaptionDialog.findViewById(R.id.top_edittext);
Log.i(TAG, "- Top editText: " + topText);
String topString = topText.getText().toString();
Log.i(TAG, " - String: '" + topString + "'");
EditText bottomText = (EditText) mCaptionDialog.findViewById(R.id.bottom_edittext);
Log.i(TAG, "- Bottom editText: " + bottomText);
String bottomString = bottomText.getText().toString();
Log.i(TAG, " - String: '" + bottomString + "'");
mLolcatView.setCaptions(topString, bottomString);
updateButtons();
}
/**
* Kicks off the process of saving the LolcatView's working Bitmap to
* the SD card, in preparation for viewing it later and/or sharing it.
*/
private void saveImage() {
Log.i(TAG, "saveImage()...");
// First of all, bring up a progress dialog.
showDialog(DIALOG_SAVE_PROGRESS);
// We now need to save the bitmap to the SD card, and then ask the
// MediaScanner to scan it. Do the actual work of all this in a
// helper thread, since it's fairly slow (and will occasionally
// ANR if we do it here in the UI thread.)
Thread t = new Thread() {
public void run() {
Log.i(TAG, "Running worker thread...");
saveImageInternal();
}
};
t.start();
// Next steps:
// - saveImageInternal()
// - onMediaScannerConnected()
// - onScanCompleted
}
/**
* Saves the LolcatView's working Bitmap to the SD card, in
* preparation for viewing it later and/or sharing it.
*
* The bitmap will be saved as a new file in the directory
* LOLCAT_SAVE_DIRECTORY, with an automatically-generated filename
* based on the current time. It also connects to the
* MediaScanner service, since we'll need to scan that new file (in
* order to get a Uri we can then VIEW or share.)
*
* This method is run in a worker thread; @see saveImage().
*/
private void saveImageInternal() {
Log.i(TAG, "saveImageInternal()...");
// TODO: Currently we save the bitmap to a file on the sdcard,
// then ask the MediaScanner to scan it (which gives us a Uri we
// can then do an ACTION_VIEW on.) But rather than doing these
// separate steps, maybe there's some easy way (given an
// OutputStream) to directly talk to the MediaProvider
// (i.e. com.android.provider.MediaStore) and say "here's an
// image, please save it somwhere and return the URI to me"...
// Save the bitmap to a file on the sdcard.
// (Based on similar code in MusicUtils.java.)
// TODO: Make this filename more human-readable? Maybe "Lolcat-YYYY-MM-DD-HHMMSS.png"?
String filename = Environment.getExternalStorageDirectory()
+ "/" + LOLCAT_SAVE_DIRECTORY
+ String.valueOf(System.currentTimeMillis() + SAVED_IMAGE_EXTENSION);
Log.i(TAG, "- filename: '" + filename + "'");
if (ensureFileExists(filename)) {
try {
OutputStream outstream = new FileOutputStream(filename);
Bitmap bitmap = mLolcatView.getWorkingBitmap();
boolean success = bitmap.compress(SAVED_IMAGE_COMPRESS_FORMAT,
100, outstream);
Log.i(TAG, "- success code from Bitmap.compress: " + success);
outstream.close();
if (success) {
Log.i(TAG, "- Saved! filename = " + filename);
mSavedImageFilename = filename;
// Ok, now we need to get the MediaScanner to scan the
// file we just wrote. Step 1 is to get our
// MediaScannerConnection object to connect to the
// MediaScanner service.
mMediaScannerConnection.connect();
// See onMediaScannerConnected() for the next step
} else {
Log.w(TAG, "Bitmap.compress failed: bitmap " + bitmap
+ ", filename '" + filename + "'");
onSaveFailed(R.string.lolcat_save_failed);
}
} catch (FileNotFoundException e) {
Log.w(TAG, "error creating file", e);
onSaveFailed(R.string.lolcat_save_failed);
} catch (IOException e) {
Log.w(TAG, "error creating file", e);
onSaveFailed(R.string.lolcat_save_failed);
}
} else {
Log.w(TAG, "ensureFileExists failed for filename '" + filename + "'");
onSaveFailed(R.string.lolcat_save_failed);
}
}
//
// MediaScanner-related code
//
/**
* android.media.MediaScannerConnection.MediaScannerConnectionClient implementation.
*/
private MediaScannerConnection.MediaScannerConnectionClient mMediaScanConnClient =
new MediaScannerConnection.MediaScannerConnectionClient() {
/**
* Called when a connection to the MediaScanner service has been established.
*/
public void onMediaScannerConnected() {
Log.i(TAG, "MediaScannerConnectionClient.onMediaScannerConnected...");
// The next step happens in the UI thread:
mHandler.post(new Runnable() {
public void run() {
LolcatActivity.this.onMediaScannerConnected();
}
});
}
/**
* Called when the media scanner has finished scanning a file.
* @param path the path to the file that has been scanned.
* @param uri the Uri for the file if the scanning operation succeeded
* and the file was added to the media database, or null if scanning failed.
*/
public void onScanCompleted(final String path, final Uri uri) {
Log.i(TAG, "MediaScannerConnectionClient.onScanCompleted: path "
+ path + ", uri " + uri);
// Just run the "real" onScanCompleted() method in the UI thread:
mHandler.post(new Runnable() {
public void run() {
LolcatActivity.this.onScanCompleted(path, uri);
}
});
}
};
/**
* This method is called when our MediaScannerConnection successfully
* connects to the MediaScanner service. At that point we fire off a
* request to scan the lolcat image we just saved.
*
* This needs to run in the UI thread, so it's called from
* mMediaScanConnClient's onMediaScannerConnected() method via our Handler.
*/
private void onMediaScannerConnected() {
Log.i(TAG, "onMediaScannerConnected()...");
// Update the message in the progress dialog...
mSaveProgressDialog.setMessage(getResources().getString(R.string.lolcat_scanning));
// Fire off a request to the MediaScanner service to scan this
// file; we'll get notified when the scan completes.
Log.i(TAG, "- Requesting scan for file: " + mSavedImageFilename);
mMediaScannerConnection.scanFile(mSavedImageFilename,
null /* mimeType */);
// Next step: mMediaScanConnClient will get an onScanCompleted() callback,
// which calls our own onScanCompleted() method via our Handler.
}
/**
* Updates the UI after the media scanner finishes the scanFile()
* request we issued from onMediaScannerConnected().
*
* This needs to run in the UI thread, so it's called from
* mMediaScanConnClient's onScanCompleted() method via our Handler.
*/
private void onScanCompleted(String path, final Uri uri) {
Log.i(TAG, "onScanCompleted: path " + path + ", uri " + uri);
mMediaScannerConnection.disconnect();
if (uri == null) {
Log.w(TAG, "onScanCompleted: scan failed.");
mSavedImageUri = null;
onSaveFailed(R.string.lolcat_scan_failed);
return;
}
// Success!
dismissDialog(DIALOG_SAVE_PROGRESS);
// We can now access the saved lolcat image using the specified Uri.
mSavedImageUri = uri;
// Bring up a success dialog, giving the user the option to go to
// the pictures app (so you can share the image).
showSaveSuccessDialog();
}
//
// Other misc utility methods
//
/**
* Ensure that the specified file exists on the SD card, creating it
* if necessary.
*
* Copied from MediaProvider / MusicUtils.
*
* @return true if the file already exists, or we
* successfully created it.
*/
private static boolean ensureFileExists(String path) {
File file = new File(path);
if (file.exists()) {
return true;
} else {
// we will not attempt to create the first directory in the path
// (for example, do not create /sdcard if the SD card is not mounted)
int secondSlash = path.indexOf('/', 1);
if (secondSlash < 1) return false;
String directoryPath = path.substring(0, secondSlash);
File directory = new File(directoryPath);
if (!directory.exists())
return false;
file.getParentFile().mkdirs();
try {
return file.createNewFile();
} catch (IOException ioe) {
Log.w(TAG, "File creation failed", ioe);
}
return false;
}
}
/**
* Updates the UI after a failure anywhere in the bitmap saving / scanning
* sequence.
*/
private void onSaveFailed(int errorMessageResId) {
Looper.prepare();
dismissDialog(DIALOG_SAVE_PROGRESS);
Toast.makeText(this, errorMessageResId, Toast.LENGTH_SHORT).show();
}
/**
* Goes to the Pictures app for the specified URI.
*/
private void viewSavedImage(Uri uri) {
Log.i(TAG, "viewSavedImage(" + uri + ")...");
if (uri == null) {
Log.w(TAG, "viewSavedImage: null uri!");
return;
}
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
Log.i(TAG, "- running startActivity... Intent = " + intent);
startActivity(intent);
}
private void viewSavedImage() {
viewSavedImage(mSavedImageUri);
}
/**
* Shares the image with the specified URI.
*/
private void shareSavedImage(Uri uri) {
Log.i(TAG, "shareSavedImage(" + uri + ")...");
if (uri == null) {
Log.w(TAG, "shareSavedImage: null uri!");
return;
}
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
intent.setType(SAVED_IMAGE_MIME_TYPE);
intent.putExtra(Intent.EXTRA_STREAM, uri);
try {
startActivity(
Intent.createChooser(
intent,
getResources().getString(R.string.lolcat_sendImage_label)));
} catch (android.content.ActivityNotFoundException ex) {
Log.w(TAG, "shareSavedImage: startActivity failed", ex);
Toast.makeText(this, R.string.lolcat_share_failed, Toast.LENGTH_SHORT).show();
}
}
private void shareSavedImage() {
shareSavedImage(mSavedImageUri);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.mainmenu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
switch (item.getItemId()) {
case R.id.donate:
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.mithex.com/donate.html"));
startActivity(browserIntent);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}