Want more features on Pastebin? Sign Up, it's FREE!
Guest

Untitled

By: a guest on Nov 26th, 2012  |  syntax: None  |  size: 31.48 KB  |  views: 223  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. /*
  2.  * Copyright (C) 2008 Google Inc.
  3.  *
  4.  * Licensed under the Apache License, Version 2.0 (the "License");
  5.  * you may not use this file except in compliance with the License.
  6.  * You may obtain a copy of the License at
  7.  *
  8.  *      http://www.apache.org/licenses/LICENSE-2.0
  9.  *
  10.  * Unless required by applicable law or agreed to in writing, software
  11.  * distributed under the License is distributed on an "AS IS" BASIS,
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  * See the License for the specific language governing permissions and
  14.  * limitations under the License.
  15.  */
  16.  
  17. package cap.shot;
  18.  
  19. import android.app.Activity;
  20. import android.app.AlertDialog;
  21. import android.app.Dialog;
  22. import android.app.ProgressDialog;
  23. import android.content.DialogInterface;
  24. import android.content.Intent;
  25. import android.graphics.Bitmap;
  26. import android.graphics.drawable.Drawable;
  27. import android.media.MediaScannerConnection;
  28. import android.net.Uri;
  29. import android.os.Bundle;
  30. import android.os.Environment;
  31. import android.os.Handler;
  32. import android.os.Looper;
  33. import android.text.TextUtils;
  34. import android.util.Log;
  35. import android.view.LayoutInflater;
  36. import android.view.Menu;
  37. import android.view.MenuInflater;
  38. import android.view.MenuItem;
  39. import android.view.View;
  40. import android.widget.Button;
  41. import android.widget.EditText;
  42. import android.widget.Toast;
  43.  
  44. import java.io.File;
  45. import java.io.FileNotFoundException;
  46. import java.io.FileOutputStream;
  47. import java.io.IOException;
  48. import java.io.OutputStream;
  49.  
  50. /**
  51.  * Lolcat builder activity.
  52.  *
  53.  * Instructions:
  54.  * (1) Take photo of cat using Camera
  55.  * (2) Run LolcatActivity
  56.  * (3) Pick photo
  57.  * (4) Add caption(s)
  58.  * (5) Save and share
  59.  *
  60.  * See README.txt for a list of currently-missing features and known bugs.
  61.  */
  62. public class LolcatActivity extends Activity
  63.         implements View.OnClickListener {
  64.     private static final String TAG = "LolcatActivity";
  65.  
  66.     // Location on the SD card for saving lolcat images
  67.     private static final String LOLCAT_SAVE_DIRECTORY = "lolcats/";
  68.  
  69.     // Mime type / format / extension we use (must be self-consistent!)
  70.     private static final String SAVED_IMAGE_EXTENSION = ".png";
  71.     private static final Bitmap.CompressFormat SAVED_IMAGE_COMPRESS_FORMAT =
  72.             Bitmap.CompressFormat.PNG;
  73.     private static final String SAVED_IMAGE_MIME_TYPE = "image/png";
  74.  
  75.     // UI Elements
  76.     private Button mPickButton;
  77.     private Button mCaptionButton;
  78.     private Button mSaveButton;
  79.     private Button mClearCaptionButton;
  80.     private Button mClearPhotoButton;
  81.     private LolcatView mLolcatView;
  82.  
  83.     private AlertDialog mCaptionDialog;
  84.     private ProgressDialog mSaveProgressDialog;
  85.     private AlertDialog mSaveSuccessDialog;
  86.  
  87.     private Handler mHandler;
  88.  
  89.     private Uri mPhotoUri;
  90.  
  91.     private String mSavedImageFilename;
  92.     private Uri mSavedImageUri;
  93.  
  94.     private MediaScannerConnection mMediaScannerConnection;
  95.  
  96.     // Request codes used with startActivityForResult()
  97.     private static final int PHOTO_PICKED = 1;
  98.  
  99.     // Dialog IDs
  100.     private static final int DIALOG_CAPTION = 1;
  101.     private static final int DIALOG_SAVE_PROGRESS = 2;
  102.     private static final int DIALOG_SAVE_SUCCESS = 3;
  103.  
  104.     // Keys used with onSaveInstanceState()
  105.     private static final String PHOTO_URI_KEY = "photo_uri";
  106.     private static final String SAVED_IMAGE_FILENAME_KEY = "saved_image_filename";
  107.     private static final String SAVED_IMAGE_URI_KEY = "saved_image_uri";
  108.     private static final String TOP_CAPTION_KEY = "top_caption";
  109.     private static final String BOTTOM_CAPTION_KEY = "bottom_caption";
  110.     private static final String CAPTION_POSITIONS_KEY = "caption_positions";
  111.  
  112.  
  113.     @Override
  114.     protected void onCreate(Bundle icicle) {
  115.         Log.i(TAG, "onCreate()...  icicle = " + icicle);
  116.  
  117.         super.onCreate(icicle);
  118.  
  119.         setContentView(R.layout.lolcat_activity);
  120.  
  121.         // Look up various UI elements
  122.  
  123.         mPickButton = (Button) findViewById(R.id.pick_button);
  124.         mPickButton.setOnClickListener(this);
  125.  
  126.         mCaptionButton = (Button) findViewById(R.id.caption_button);
  127.         mCaptionButton.setOnClickListener(this);
  128.  
  129.         mSaveButton = (Button) findViewById(R.id.save_button);
  130.         mSaveButton.setOnClickListener(this);
  131.  
  132.         mClearCaptionButton = (Button) findViewById(R.id.clear_caption_button);
  133.         // This button doesn't exist in portrait mode.
  134.         if (mClearCaptionButton != null) mClearCaptionButton.setOnClickListener(this);
  135.  
  136.         mClearPhotoButton = (Button) findViewById(R.id.clear_photo_button);
  137.         mClearPhotoButton.setOnClickListener(this);
  138.  
  139.         mLolcatView = (LolcatView) findViewById(R.id.main_image);
  140.  
  141.         // Need one of these to call back to the UI thread
  142.         // (and run AlertDialog.show(), for that matter)
  143.         mHandler = new Handler();
  144.  
  145.         mMediaScannerConnection = new MediaScannerConnection(this, mMediaScanConnClient);
  146.  
  147.         if (icicle != null) {
  148.             Log.i(TAG, "- reloading state from icicle!");
  149.             restoreStateFromIcicle(icicle);
  150.         }
  151.     }
  152.  
  153.     @Override
  154.     protected void onResume() {
  155.         Log.i(TAG, "onResume()...");
  156.         super.onResume();
  157.  
  158.         updateButtons();
  159.     }
  160.  
  161.     @Override
  162.     protected void onSaveInstanceState(Bundle outState) {
  163.         Log.i(TAG, "onSaveInstanceState()...");
  164.         super.onSaveInstanceState(outState);
  165.  
  166.         // State from the Activity:
  167.         outState.putParcelable(PHOTO_URI_KEY, mPhotoUri);
  168.         outState.putString(SAVED_IMAGE_FILENAME_KEY, mSavedImageFilename);
  169.         outState.putParcelable(SAVED_IMAGE_URI_KEY, mSavedImageUri);
  170.  
  171.         // State from the LolcatView:
  172.         // (TODO: Consider making Caption objects, or even the LolcatView
  173.         // itself, Parcelable?  Probably overkill, though...)
  174.         outState.putString(TOP_CAPTION_KEY, mLolcatView.getTopCaption());
  175.         outState.putString(BOTTOM_CAPTION_KEY, mLolcatView.getBottomCaption());
  176.         outState.putIntArray(CAPTION_POSITIONS_KEY, mLolcatView.getCaptionPositions());
  177.     }
  178.  
  179.     /**
  180.      * Restores the activity state from the specified icicle.
  181.      * @see onCreate()
  182.      * @see onSaveInstanceState()
  183.      */
  184.     private void restoreStateFromIcicle(Bundle icicle) {
  185.         Log.i(TAG, "restoreStateFromIcicle()...");
  186.  
  187.         // State of the Activity:
  188.  
  189.         Uri photoUri = icicle.getParcelable(PHOTO_URI_KEY);
  190.         Log.i(TAG, "  - photoUri: " + photoUri);
  191.         if (photoUri != null) {
  192.             loadPhoto(photoUri);
  193.         }
  194.  
  195.         mSavedImageFilename = icicle.getString(SAVED_IMAGE_FILENAME_KEY);
  196.         mSavedImageUri = icicle.getParcelable(SAVED_IMAGE_URI_KEY);
  197.  
  198.         // State of the LolcatView:
  199.  
  200.         String topCaption = icicle.getString(TOP_CAPTION_KEY);
  201.         String bottomCaption = icicle.getString(BOTTOM_CAPTION_KEY);
  202.         int[] captionPositions = icicle.getIntArray(CAPTION_POSITIONS_KEY);
  203.         Log.i(TAG, "  - captions: '" + topCaption + "', '" + bottomCaption + "'");
  204.         if (!TextUtils.isEmpty(topCaption) || !TextUtils.isEmpty(bottomCaption)) {
  205.             mLolcatView.setCaptions(topCaption, bottomCaption);
  206.             mLolcatView.setCaptionPositions(captionPositions);
  207.         }
  208.     }
  209.  
  210.     @Override
  211.     protected void onDestroy() {
  212.         Log.i(TAG, "onDestroy()...");
  213.         super.onDestroy();
  214.         clearPhoto();  // Free up some resources, and force a GC
  215.     }
  216.  
  217.     // View.OnClickListener implementation
  218.     public void onClick(View view) {
  219.         int id = view.getId();
  220.         Log.i(TAG, "onClick(View " + view + ", id " + id + ")...");
  221.  
  222.         switch (id) {
  223.             case R.id.pick_button:
  224.                 Log.i(TAG, "onClick: pick_button...");
  225.                 Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
  226.                 intent.setType("image/*");
  227.  
  228.                 // Note: we could have the "crop" UI come up here by
  229.                 // default by doing this:
  230.                 //   intent.putExtra("crop", "true");
  231.                 // (But watch out: if you do that, the Intent that comes
  232.                 // back to onActivityResult() will have the URI (of the
  233.                 // cropped image) in the "action" field, not the "data"
  234.                 // field!)
  235.  
  236.                 startActivityForResult(intent, PHOTO_PICKED);
  237.                 break;
  238.  
  239.             case R.id.caption_button:
  240.                 Log.i(TAG, "onClick: caption_button...");
  241.                 showCaptionDialog();
  242.                 break;
  243.  
  244.             case R.id.save_button:
  245.                 Log.i(TAG, "onClick: save_button...");
  246.                 saveImage();
  247.                 break;
  248.  
  249.             case R.id.clear_caption_button:
  250.                 Log.i(TAG, "onClick: clear_caption_button...");
  251.                 clearCaptions();
  252.                 updateButtons();
  253.                 break;
  254.  
  255.             case R.id.clear_photo_button:
  256.                 Log.i(TAG, "onClick: clear_photo_button...");
  257.                 clearPhoto();  // Also does clearCaptions()
  258.                 updateButtons();
  259.                 break;
  260.  
  261.             default:
  262.                 Log.w(TAG, "Click from unexpected source: " + view + ", id " + id);
  263.                 break;
  264.         }
  265.     }
  266.  
  267.     @Override
  268.     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  269.         Log.i(TAG, "onActivityResult(request " + requestCode
  270.               + ", result " + resultCode + ", data " + data + ")...");
  271.  
  272.         if (resultCode != RESULT_OK) {
  273.             Log.i(TAG, "==> result " + resultCode + " from subactivity!  Ignoring...");
  274.             Toast t = Toast.makeText(this, R.string.lolcat_nothing_picked, Toast.LENGTH_SHORT);
  275.             t.show();
  276.             return;
  277.         }
  278.  
  279.         if (resultCode == RESULT_CANCELED) {
  280.                 Toast t = Toast.makeText(this, R.string.cancelled, Toast.LENGTH_SHORT);
  281.                 t.show();
  282.                 return;
  283.         }
  284.      
  285.         if (requestCode == PHOTO_PICKED) {
  286.             // "data" is an Intent containing (presumably) a URI like
  287.             // "content://media/external/images/media/3".
  288.  
  289.             if (data == null) {
  290.                 Log.w(TAG, "Null data, but RESULT_OK, from image picker!");
  291.                 Toast t = Toast.makeText(this, R.string.lolcat_nothing_picked,
  292.                                          Toast.LENGTH_SHORT);
  293.                 t.show();
  294.                 return;
  295.             }
  296.  
  297.             if (data.getData() == null) {
  298.                 Log.w(TAG, "'data' intent from image picker contained no data!");
  299.                 Toast t = Toast.makeText(this, R.string.lolcat_nothing_picked,
  300.                                          Toast.LENGTH_SHORT);
  301.                 t.show();
  302.                 return;
  303.             }
  304.  
  305.             loadPhoto(data.getData());
  306.             updateButtons();
  307.         }
  308.     }
  309.  
  310.     /**
  311.      * Updates the enabled/disabled state of the onscreen buttons.
  312.      */
  313.     private void updateButtons() {
  314.         Log.i(TAG, "updateButtons()...");
  315.  
  316.         // mPickButton is always enabled.
  317.  
  318.         // Do we have a valid photo and/or caption(s) yet?
  319.         Drawable d = mLolcatView.getDrawable();
  320.         // Log.i(TAG, "===> current mLolcatView drawable: " + d);
  321.         boolean validPhoto = (d != null);
  322.         boolean validCaption = mLolcatView.hasValidCaption();
  323.  
  324.         mCaptionButton.setText(validCaption
  325.                                ? R.string.lolcat_change_captions : R.string.lolcat_add_captions);
  326.         mCaptionButton.setEnabled(validPhoto);
  327.  
  328.         mSaveButton.setEnabled(validPhoto && validCaption);
  329.  
  330.         if (mClearCaptionButton != null) {
  331.             mClearCaptionButton.setEnabled(validPhoto && validCaption);
  332.         }
  333.  
  334.         mClearPhotoButton.setEnabled(validPhoto);
  335.     }
  336.  
  337.     /**
  338.      * Clears out any already-entered captions for this lolcat.
  339.      */
  340.     private void clearCaptions() {
  341.         mLolcatView.clearCaptions();
  342.  
  343.         // Clear the text fields in the caption dialog too.
  344.         if (mCaptionDialog != null) {
  345.             EditText topText = (EditText) mCaptionDialog.findViewById(R.id.top_edittext);
  346.             topText.setText("");
  347.             EditText bottomText = (EditText) mCaptionDialog.findViewById(R.id.bottom_edittext);
  348.             bottomText.setText("");
  349.             topText.requestFocus();
  350.         }
  351.  
  352.         // This also invalidates any image we've previously written to the
  353.         // SD card...
  354.         mSavedImageFilename = null;
  355.         mSavedImageUri = null;
  356.     }
  357.  
  358.     /**
  359.      * Completely resets the UI to its initial state, with no photo
  360.      * loaded, and no captions.
  361.      */
  362.     private void clearPhoto() {
  363.         mLolcatView.clear();
  364.  
  365.         mPhotoUri = null;
  366.         mSavedImageFilename = null;
  367.         mSavedImageUri = null;
  368.  
  369.         clearCaptions();
  370.  
  371.         // Force a gc (to be sure to reclaim the memory used by our
  372.         // potentially huge bitmap):
  373.         System.gc();
  374.     }
  375.  
  376.     /**
  377.      * Loads the image with the specified Uri into the UI.
  378.      */
  379.     private void loadPhoto(Uri uri) {
  380.         Log.i(TAG, "loadPhoto: uri = " + uri);
  381.  
  382.         clearPhoto();  // Be sure to release the previous bitmap
  383.                        // before creating another one
  384.         mPhotoUri = uri;
  385.  
  386.         // A new photo always starts out uncaptioned.
  387.         clearCaptions();
  388.  
  389.         // Load the selected photo into our ImageView.
  390.         mLolcatView.loadFromUri(mPhotoUri);
  391.     }
  392.  
  393.     private void showCaptionDialog() {
  394.         // If the dialog already exists, always reset focus to the top
  395.         // item each time it comes up.
  396.         if (mCaptionDialog != null) {
  397.             EditText topText = (EditText) mCaptionDialog.findViewById(R.id.top_edittext);
  398.             topText.requestFocus();
  399.         }
  400.  
  401.         showDialog(DIALOG_CAPTION);
  402.     }
  403.  
  404.     private void showSaveSuccessDialog() {
  405.         // If the dialog already exists, update the body text based on the
  406.         // current values of mSavedImageFilename and mSavedImageUri
  407.         // (otherwise the dialog will still have the body text from when
  408.         // it was first created!)
  409.  
  410.         if (mSaveSuccessDialog != null) {
  411.             updateSaveSuccessDialogBody();
  412.         }
  413.         showDialog(DIALOG_SAVE_SUCCESS);
  414.     }
  415.  
  416.     private void updateSaveSuccessDialogBody() {
  417.         if (mSaveSuccessDialog == null) {
  418.             throw new IllegalStateException(
  419.                     "updateSaveSuccessDialogBody: mSaveSuccessDialog hasn't been created yet");
  420.         }
  421.         String dialogBody = String.format(
  422.                 getResources().getString(R.string.lolcat_save_succeeded_dialog_body_format),
  423.                 mSavedImageFilename, mSavedImageUri);
  424.         mSaveSuccessDialog.setMessage(dialogBody);
  425.     }
  426.  
  427.     @Override
  428.     protected Dialog onCreateDialog(int id) {
  429.         Log.i(TAG, "onCreateDialog(id " + id + ")...");
  430.  
  431.         // This is only run once (per dialog), the very first time
  432.         // a given dialog needs to be shown.
  433.  
  434.         switch (id) {
  435.             case DIALOG_CAPTION:
  436.                 LayoutInflater factory = LayoutInflater.from(this);
  437.                 final View textEntryView = factory.inflate(R.layout.lolcat_caption_dialog, null);
  438.                 mCaptionDialog = new AlertDialog.Builder(this)
  439.                         .setTitle(R.string.lolcat_caption_dialog_title)
  440.                         .setIcon(0)
  441.                         .setView(textEntryView)
  442.                         .setPositiveButton(
  443.                                 R.string.lolcat_caption_dialog_ok,
  444.                                 new DialogInterface.OnClickListener() {
  445.                                     public void onClick(DialogInterface dialog, int whichButton) {
  446.                                         Log.i(TAG, "Caption dialog: OK...");
  447.                                         updateCaptionsFromDialog();
  448.                                     }
  449.                                 })
  450.                         .setNegativeButton(
  451.                                 R.string.lolcat_caption_dialog_cancel,
  452.                                 new DialogInterface.OnClickListener() {
  453.                                     public void onClick(DialogInterface dialog, int whichButton) {
  454.                                         Log.i(TAG, "Caption dialog: CANCEL...");
  455.                                         // Nothing to do here (for now at least)
  456.                                     }
  457.                                 })
  458.                         .create();
  459.                 return mCaptionDialog;
  460.  
  461.             case DIALOG_SAVE_PROGRESS:
  462.                 mSaveProgressDialog = new ProgressDialog(this);
  463.                 mSaveProgressDialog.setMessage(getResources().getString(R.string.lolcat_saving));
  464.                 mSaveProgressDialog.setIndeterminate(true);
  465.                 mSaveProgressDialog.setCancelable(false);
  466.                 return mSaveProgressDialog;
  467.  
  468.             case DIALOG_SAVE_SUCCESS:
  469.                 mSaveSuccessDialog = new AlertDialog.Builder(this)
  470.                         .setTitle(R.string.lolcat_save_succeeded_dialog_title)
  471.                         .setIcon(0)
  472.                         .setPositiveButton(
  473.                                 R.string.lolcat_save_succeeded_dialog_view,
  474.                                 new DialogInterface.OnClickListener() {
  475.                                     public void onClick(DialogInterface dialog, int whichButton) {
  476.                                         Log.i(TAG, "Save dialog: View...");
  477.                                         viewSavedImage();
  478.                                     }
  479.                                 })
  480.                         .setNeutralButton(
  481.                                 R.string.lolcat_save_succeeded_dialog_share,
  482.                                 new DialogInterface.OnClickListener() {
  483.                                     public void onClick(DialogInterface dialog, int whichButton) {
  484.                                         Log.i(TAG, "Save dialog: Share...");
  485.                                         shareSavedImage();
  486.                                     }
  487.                                 })
  488.                         .setNegativeButton(
  489.                                 R.string.lolcat_save_succeeded_dialog_cancel,
  490.                                 new DialogInterface.OnClickListener() {
  491.                                     public void onClick(DialogInterface dialog, int whichButton) {
  492.                                         Log.i(TAG, "Save dialog: CANCEL...");
  493.                                         // Nothing to do here...
  494.                                     }
  495.                                 })
  496.                         .create();
  497.                 updateSaveSuccessDialogBody();
  498.                 return mSaveSuccessDialog;
  499.  
  500.             default:
  501.                 Log.w(TAG, "Request for unexpected dialog id: " + id);
  502.                 break;
  503.         }
  504.         return null;
  505.     }
  506.  
  507.     private void updateCaptionsFromDialog() {
  508.         Log.i(TAG, "updateCaptionsFromDialog()...");
  509.  
  510.         if (mCaptionDialog == null) {
  511.             Log.w(TAG, "updateCaptionsFromDialog: null mCaptionDialog!");
  512.             return;
  513.         }
  514.  
  515.         // Get the two caption strings:
  516.  
  517.         EditText topText = (EditText) mCaptionDialog.findViewById(R.id.top_edittext);
  518.         Log.i(TAG, "- Top editText: " + topText);
  519.         String topString = topText.getText().toString();
  520.         Log.i(TAG, "  - String: '" + topString + "'");
  521.  
  522.         EditText bottomText = (EditText) mCaptionDialog.findViewById(R.id.bottom_edittext);
  523.         Log.i(TAG, "- Bottom editText: " + bottomText);
  524.         String bottomString = bottomText.getText().toString();
  525.         Log.i(TAG, "  - String: '" + bottomString + "'");
  526.  
  527.         mLolcatView.setCaptions(topString, bottomString);
  528.         updateButtons();
  529.     }
  530.  
  531.  
  532.     /**
  533.      * Kicks off the process of saving the LolcatView's working Bitmap to
  534.      * the SD card, in preparation for viewing it later and/or sharing it.
  535.      */
  536.     private void saveImage() {
  537.         Log.i(TAG, "saveImage()...");
  538.  
  539.         // First of all, bring up a progress dialog.
  540.         showDialog(DIALOG_SAVE_PROGRESS);
  541.  
  542.         // We now need to save the bitmap to the SD card, and then ask the
  543.         // MediaScanner to scan it.  Do the actual work of all this in a
  544.         // helper thread, since it's fairly slow (and will occasionally
  545.         // ANR if we do it here in the UI thread.)
  546.  
  547.         Thread t = new Thread() {
  548.                 public void run() {
  549.                     Log.i(TAG, "Running worker thread...");
  550.                     saveImageInternal();
  551.                 }
  552.             };
  553.         t.start();
  554.         // Next steps:
  555.         // - saveImageInternal()
  556.         // - onMediaScannerConnected()
  557.         // - onScanCompleted
  558.     }
  559.  
  560.     /**
  561.      * Saves the LolcatView's working Bitmap to the SD card, in
  562.      * preparation for viewing it later and/or sharing it.
  563.      *
  564.      * The bitmap will be saved as a new file in the directory
  565.      * LOLCAT_SAVE_DIRECTORY, with an automatically-generated filename
  566.      * based on the current time.  It also connects to the
  567.      * MediaScanner service, since we'll need to scan that new file (in
  568.      * order to get a Uri we can then VIEW or share.)
  569.      *
  570.      * This method is run in a worker thread; @see saveImage().
  571.      */
  572.     private void saveImageInternal() {
  573.         Log.i(TAG, "saveImageInternal()...");
  574.  
  575.         // TODO: Currently we save the bitmap to a file on the sdcard,
  576.         // then ask the MediaScanner to scan it (which gives us a Uri we
  577.         // can then do an ACTION_VIEW on.)  But rather than doing these
  578.         // separate steps, maybe there's some easy way (given an
  579.         // OutputStream) to directly talk to the MediaProvider
  580.         // (i.e. com.android.provider.MediaStore) and say "here's an
  581.         // image, please save it somwhere and return the URI to me"...
  582.  
  583.         // Save the bitmap to a file on the sdcard.
  584.         // (Based on similar code in MusicUtils.java.)
  585.         // TODO: Make this filename more human-readable?  Maybe "Lolcat-YYYY-MM-DD-HHMMSS.png"?
  586.         String filename = Environment.getExternalStorageDirectory()
  587.                 + "/" + LOLCAT_SAVE_DIRECTORY
  588.                 + String.valueOf(System.currentTimeMillis() + SAVED_IMAGE_EXTENSION);
  589.         Log.i(TAG, "- filename: '" + filename + "'");
  590.  
  591.         if (ensureFileExists(filename)) {
  592.             try {
  593.                 OutputStream outstream = new FileOutputStream(filename);
  594.                 Bitmap bitmap = mLolcatView.getWorkingBitmap();
  595.                 boolean success = bitmap.compress(SAVED_IMAGE_COMPRESS_FORMAT,
  596.                                                   100, outstream);
  597.                 Log.i(TAG, "- success code from Bitmap.compress: " + success);
  598.                 outstream.close();
  599.  
  600.                 if (success) {
  601.                     Log.i(TAG, "- Saved!  filename = " + filename);
  602.                     mSavedImageFilename = filename;
  603.  
  604.                     // Ok, now we need to get the MediaScanner to scan the
  605.                     // file we just wrote.  Step 1 is to get our
  606.                     // MediaScannerConnection object to connect to the
  607.                     // MediaScanner service.
  608.                     mMediaScannerConnection.connect();
  609.                     // See onMediaScannerConnected() for the next step
  610.                 } else {
  611.                     Log.w(TAG, "Bitmap.compress failed: bitmap " + bitmap
  612.                           + ", filename '" + filename + "'");
  613.                     onSaveFailed(R.string.lolcat_save_failed);
  614.                 }
  615.             } catch (FileNotFoundException e) {
  616.                 Log.w(TAG, "error creating file", e);
  617.                 onSaveFailed(R.string.lolcat_save_failed);
  618.             } catch (IOException e) {
  619.                 Log.w(TAG, "error creating file", e);
  620.                 onSaveFailed(R.string.lolcat_save_failed);
  621.             }
  622.         } else {
  623.             Log.w(TAG, "ensureFileExists failed for filename '" + filename + "'");
  624.             onSaveFailed(R.string.lolcat_save_failed);
  625.         }
  626.     }
  627.  
  628.  
  629.     //
  630.     // MediaScanner-related code
  631.     //
  632.  
  633.     /**
  634.      * android.media.MediaScannerConnection.MediaScannerConnectionClient implementation.
  635.      */
  636.     private MediaScannerConnection.MediaScannerConnectionClient mMediaScanConnClient =
  637.         new MediaScannerConnection.MediaScannerConnectionClient() {
  638.             /**
  639.              * Called when a connection to the MediaScanner service has been established.
  640.              */
  641.             public void onMediaScannerConnected() {
  642.                 Log.i(TAG, "MediaScannerConnectionClient.onMediaScannerConnected...");
  643.                 // The next step happens in the UI thread:
  644.                 mHandler.post(new Runnable() {
  645.                         public void run() {
  646.                             LolcatActivity.this.onMediaScannerConnected();
  647.                         }
  648.                     });
  649.             }
  650.  
  651.             /**
  652.              * Called when the media scanner has finished scanning a file.
  653.              * @param path the path to the file that has been scanned.
  654.              * @param uri the Uri for the file if the scanning operation succeeded
  655.              *        and the file was added to the media database, or null if scanning failed.
  656.              */
  657.             public void onScanCompleted(final String path, final Uri uri) {
  658.                 Log.i(TAG, "MediaScannerConnectionClient.onScanCompleted: path "
  659.                       + path + ", uri " + uri);
  660.                 // Just run the "real" onScanCompleted() method in the UI thread:
  661.                 mHandler.post(new Runnable() {
  662.                         public void run() {
  663.                             LolcatActivity.this.onScanCompleted(path, uri);
  664.                         }
  665.                     });
  666.             }
  667.         };
  668.  
  669.     /**
  670.      * This method is called when our MediaScannerConnection successfully
  671.      * connects to the MediaScanner service.  At that point we fire off a
  672.      * request to scan the lolcat image we just saved.
  673.      *
  674.      * This needs to run in the UI thread, so it's called from
  675.      * mMediaScanConnClient's onMediaScannerConnected() method via our Handler.
  676.      */
  677.     private void onMediaScannerConnected() {
  678.         Log.i(TAG, "onMediaScannerConnected()...");
  679.  
  680.         // Update the message in the progress dialog...
  681.         mSaveProgressDialog.setMessage(getResources().getString(R.string.lolcat_scanning));
  682.  
  683.         // Fire off a request to the MediaScanner service to scan this
  684.         // file; we'll get notified when the scan completes.
  685.         Log.i(TAG, "- Requesting scan for file: " + mSavedImageFilename);
  686.         mMediaScannerConnection.scanFile(mSavedImageFilename,
  687.                                          null /* mimeType */);
  688.  
  689.         // Next step: mMediaScanConnClient will get an onScanCompleted() callback,
  690.         // which calls our own onScanCompleted() method via our Handler.
  691.     }
  692.  
  693.     /**
  694.      * Updates the UI after the media scanner finishes the scanFile()
  695.      * request we issued from onMediaScannerConnected().
  696.      *
  697.      * This needs to run in the UI thread, so it's called from
  698.      * mMediaScanConnClient's onScanCompleted() method via our Handler.
  699.      */
  700.     private void onScanCompleted(String path, final Uri uri) {
  701.         Log.i(TAG, "onScanCompleted: path " + path + ", uri " + uri);
  702.         mMediaScannerConnection.disconnect();
  703.  
  704.         if (uri == null) {
  705.             Log.w(TAG, "onScanCompleted: scan failed.");
  706.             mSavedImageUri = null;
  707.             onSaveFailed(R.string.lolcat_scan_failed);
  708.             return;
  709.         }
  710.  
  711.         // Success!
  712.  
  713.         dismissDialog(DIALOG_SAVE_PROGRESS);
  714.  
  715.         // We can now access the saved lolcat image using the specified Uri.
  716.         mSavedImageUri = uri;
  717.  
  718.         // Bring up a success dialog, giving the user the option to go to
  719.         // the pictures app (so you can share the image).
  720.         showSaveSuccessDialog();
  721.     }
  722.  
  723.  
  724.     //
  725.     // Other misc utility methods
  726.     //
  727.  
  728.     /**
  729.      * Ensure that the specified file exists on the SD card, creating it
  730.      * if necessary.
  731.      *
  732.      * Copied from MediaProvider / MusicUtils.
  733.      *
  734.      * @return true if the file already exists, or we
  735.      *         successfully created it.
  736.      */
  737.     private static boolean ensureFileExists(String path) {
  738.         File file = new File(path);
  739.         if (file.exists()) {
  740.             return true;
  741.         } else {
  742.             // we will not attempt to create the first directory in the path
  743.             // (for example, do not create /sdcard if the SD card is not mounted)
  744.             int secondSlash = path.indexOf('/', 1);
  745.             if (secondSlash < 1) return false;
  746.             String directoryPath = path.substring(0, secondSlash);
  747.             File directory = new File(directoryPath);
  748.             if (!directory.exists())
  749.                 return false;
  750.             file.getParentFile().mkdirs();
  751.             try {
  752.                 return file.createNewFile();
  753.             } catch (IOException ioe) {
  754.                 Log.w(TAG, "File creation failed", ioe);
  755.             }
  756.             return false;
  757.         }
  758.     }
  759.  
  760.     /**
  761.      * Updates the UI after a failure anywhere in the bitmap saving / scanning
  762.      * sequence.
  763.      */
  764.     private void onSaveFailed(int errorMessageResId) {
  765.         Looper.prepare();
  766.         dismissDialog(DIALOG_SAVE_PROGRESS);
  767.         Toast.makeText(this, errorMessageResId, Toast.LENGTH_SHORT).show();
  768.     }
  769.  
  770.     /**
  771.      * Goes to the Pictures app for the specified URI.
  772.      */
  773.     private void viewSavedImage(Uri uri) {
  774.         Log.i(TAG, "viewSavedImage(" + uri + ")...");
  775.  
  776.         if (uri == null) {
  777.             Log.w(TAG, "viewSavedImage: null uri!");
  778.             return;
  779.         }
  780.  
  781.         Intent intent = new Intent(Intent.ACTION_VIEW, uri);
  782.         Log.i(TAG, "- running startActivity...  Intent = " + intent);
  783.         startActivity(intent);
  784.     }
  785.  
  786.     private void viewSavedImage() {
  787.         viewSavedImage(mSavedImageUri);
  788.     }
  789.  
  790.     /**
  791.      * Shares the image with the specified URI.
  792.      */
  793.     private void shareSavedImage(Uri uri) {
  794.         Log.i(TAG, "shareSavedImage(" + uri + ")...");
  795.  
  796.         if (uri == null) {
  797.             Log.w(TAG, "shareSavedImage: null uri!");
  798.             return;
  799.         }
  800.  
  801.         Intent intent = new Intent();
  802.         intent.setAction(Intent.ACTION_SEND);
  803.         intent.setType(SAVED_IMAGE_MIME_TYPE);
  804.         intent.putExtra(Intent.EXTRA_STREAM, uri);
  805.         try {
  806.             startActivity(
  807.                     Intent.createChooser(
  808.                             intent,
  809.                             getResources().getString(R.string.lolcat_sendImage_label)));
  810.         } catch (android.content.ActivityNotFoundException ex) {
  811.             Log.w(TAG, "shareSavedImage: startActivity failed", ex);
  812.             Toast.makeText(this, R.string.lolcat_share_failed, Toast.LENGTH_SHORT).show();
  813.         }
  814.     }
  815.  
  816.     private void shareSavedImage() {
  817.         shareSavedImage(mSavedImageUri);
  818.     }
  819.    
  820.         @Override
  821.         public boolean onCreateOptionsMenu(Menu menu) {
  822.                 MenuInflater inflater = getMenuInflater();
  823.             inflater.inflate(R.menu.mainmenu, menu);
  824.             return true;
  825.         }
  826.         @Override
  827.         public boolean onOptionsItemSelected(MenuItem item) {
  828.             // Handle item selection
  829.             switch (item.getItemId()) {
  830.                 case R.id.donate:
  831.                     Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.mithex.com/donate.html"));
  832.                     startActivity(browserIntent);
  833.                     return true;
  834.                 default:
  835.                     return super.onOptionsItemSelected(item);
  836.             }
  837.         }
  838. }
clone this paste RAW Paste Data