Advertisement
Guest User

Untitled

a guest
Nov 26th, 2012
832
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 31.48 KB | None | 0 0
  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. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement