Skip to content

Commit f4a779d

Browse files
committed
add a preview image of the last photo taken
1 parent 2a2a5cc commit f4a779d

8 files changed

Lines changed: 147 additions & 25 deletions

File tree

app/src/main/java/com/simplemobiletools/camera/PhotoProcessor.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
import java.io.IOException;
1313
import java.lang.ref.WeakReference;
1414

15-
public class PhotoProcessor extends AsyncTask<byte[], Void, Void> {
15+
public class PhotoProcessor extends AsyncTask<byte[], Void, String> {
1616
private static final String TAG = PhotoProcessor.class.getSimpleName();
1717
private static WeakReference<MainActivity> mActivity;
1818
private static Uri mUri;
@@ -23,7 +23,7 @@ public PhotoProcessor(MainActivity activity, Uri uri) {
2323
}
2424

2525
@Override
26-
protected Void doInBackground(byte[]... params) {
26+
protected String doInBackground(byte[]... params) {
2727
FileOutputStream fos = null;
2828
String path;
2929
try {
@@ -34,15 +34,15 @@ protected Void doInBackground(byte[]... params) {
3434
}
3535

3636
if (path.isEmpty()) {
37-
return null;
37+
return "";
3838
}
3939

4040
final File photoFile = new File(path);
4141
final byte[] data = params[0];
4242
fos = new FileOutputStream(photoFile);
4343
fos.write(data);
4444
fos.close();
45-
Utils.scanFile(path, mActivity.get().getApplicationContext());
45+
return photoFile.getAbsolutePath();
4646
} catch (FileNotFoundException e) {
4747
Log.e(TAG, "PhotoProcessor file not found: " + e.getMessage());
4848
} catch (IOException e) {
@@ -57,19 +57,19 @@ protected Void doInBackground(byte[]... params) {
5757
}
5858
}
5959

60-
return null;
60+
return "";
6161
}
6262

6363
@Override
64-
protected void onPostExecute(Void aVoid) {
65-
super.onPostExecute(aVoid);
64+
protected void onPostExecute(String path) {
65+
super.onPostExecute(path);
6666
final MediaSavedListener listener = mActivity.get();
6767
if (listener != null) {
68-
listener.mediaSaved();
68+
listener.mediaSaved(path);
6969
}
7070
}
7171

7272
public interface MediaSavedListener {
73-
void mediaSaved();
73+
void mediaSaved(String path);
7474
}
7575
}

app/src/main/java/com/simplemobiletools/camera/Preview.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -621,7 +621,7 @@ private void stopRecording() {
621621
try {
622622
mRecorder.stop();
623623
final String[] paths = {mCurVideoPath};
624-
MediaScannerConnection.scanFile(getContext(), paths, null, mIsVideoCaptureIntent ? this : null);
624+
MediaScannerConnection.scanFile(getContext(), paths, null, this);
625625
} catch (RuntimeException e) {
626626
new File(mCurVideoPath).delete();
627627
Utils.showToast(getContext(), R.string.video_saving_error);

app/src/main/java/com/simplemobiletools/camera/Utils.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import android.content.res.Resources;
88
import android.graphics.Point;
99
import android.hardware.Camera;
10-
import android.media.MediaScannerConnection;
1110
import android.os.Environment;
1211
import android.support.v4.content.ContextCompat;
1312
import android.view.Display;
@@ -85,11 +84,6 @@ private static File getMainDirectory(boolean isPhoto) {
8584
return Environment.getExternalStoragePublicDirectory(type);
8685
}
8786

88-
public static void scanFile(String path, Context context) {
89-
final String[] paths = {path};
90-
MediaScannerConnection.scanFile(context, paths, null, null);
91-
}
92-
9387
public static String formatSeconds(int duration) {
9488
final StringBuilder sb = new StringBuilder(8);
9589
final int hours = duration / (60 * 60);

app/src/main/java/com/simplemobiletools/camera/activities/MainActivity.java

Lines changed: 124 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
package com.simplemobiletools.camera.activities;
22

33
import android.Manifest;
4+
import android.content.ActivityNotFoundException;
5+
import android.content.ContentResolver;
46
import android.content.Intent;
57
import android.content.pm.PackageManager;
68
import android.content.res.Resources;
9+
import android.database.Cursor;
10+
import android.graphics.Bitmap;
11+
import android.graphics.Matrix;
712
import android.hardware.Camera;
813
import android.hardware.Sensor;
914
import android.hardware.SensorEvent;
1015
import android.hardware.SensorEventListener;
1116
import android.hardware.SensorManager;
17+
import android.media.MediaScannerConnection;
1218
import android.net.Uri;
1319
import android.os.Bundle;
1420
import android.os.Handler;
@@ -41,21 +47,25 @@
4147
import butterknife.ButterKnife;
4248
import butterknife.OnClick;
4349

44-
public class MainActivity extends AppCompatActivity implements SensorEventListener, PreviewListener, PhotoProcessor.MediaSavedListener {
50+
public class MainActivity extends AppCompatActivity
51+
implements SensorEventListener, PreviewListener, PhotoProcessor.MediaSavedListener, MediaScannerConnection.OnScanCompletedListener {
4552
@BindView(R.id.viewHolder) RelativeLayout mViewHolder;
4653
@BindView(R.id.toggle_camera) ImageView mToggleCameraBtn;
4754
@BindView(R.id.toggle_flash) ImageView mToggleFlashBtn;
4855
@BindView(R.id.toggle_photo_video) ImageView mTogglePhotoVideoBtn;
4956
@BindView(R.id.shutter) ImageView mShutterBtn;
5057
@BindView(R.id.video_rec_curr_timer) TextView mRecCurrTimer;
5158
@BindView(R.id.about) View mAboutBtn;
59+
@BindView(R.id.last_photo_video_preview) ImageView mLastPhotoVideoPreview;
5260

61+
private static final String TAG = MainActivity.class.getSimpleName();
5362
private static final int CAMERA_STORAGE_PERMISSION = 1;
5463
private static final int AUDIO_PERMISSION = 2;
5564

5665
private static SensorManager mSensorManager;
5766
private static Preview mPreview;
5867
private static Handler mTimerHandler;
68+
private static Uri mPreviewUri;
5969

6070
private static boolean mIsFlashEnabled;
6171
private static boolean mIsInPhotoMode;
@@ -153,6 +163,7 @@ private void initializeCamera() {
153163
mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
154164
mIsInPhotoMode = true;
155165
mTimerHandler = new Handler();
166+
setupPreviewImage(true);
156167
}
157168

158169
private boolean hasCameraAndStoragePermission() {
@@ -209,6 +220,25 @@ public void toggleCamera() {
209220
}
210221
}
211222

223+
@OnClick(R.id.last_photo_video_preview)
224+
public void showLastMediaPreview() {
225+
if (mPreviewUri == null)
226+
return;
227+
228+
try {
229+
final String REVIEW_ACTION = "com.android.camera.action.REVIEW";
230+
Intent intent = new Intent(REVIEW_ACTION, mPreviewUri);
231+
this.startActivity(intent);
232+
} catch (ActivityNotFoundException e) {
233+
Intent intent = new Intent(Intent.ACTION_VIEW, mPreviewUri);
234+
if (intent.resolveActivity(getPackageManager()) != null) {
235+
startActivity(intent);
236+
} else {
237+
Utils.showToast(getApplicationContext(), R.string.no_gallery_app_available);
238+
}
239+
}
240+
}
241+
212242
@OnClick(R.id.toggle_flash)
213243
public void toggleFlash() {
214244
if (!checkCameraAvailable()) {
@@ -304,7 +334,7 @@ private void checkButtons() {
304334
if (mIsInPhotoMode) {
305335
initPhotoButtons();
306336
} else {
307-
initVideoButtons();
337+
tryInitVideoButtons();
308338
}
309339
}
310340

@@ -313,24 +343,101 @@ private void initPhotoButtons() {
313343
mTogglePhotoVideoBtn.setImageDrawable(res.getDrawable(R.mipmap.videocam));
314344
mShutterBtn.setImageDrawable(res.getDrawable(R.mipmap.camera));
315345
mPreview.initPhotoMode();
346+
setupPreviewImage(true);
316347
}
317348

318-
private void initVideoButtons() {
349+
private void tryInitVideoButtons() {
319350
if (mPreview.initRecorder()) {
320-
setupVideoIcons();
351+
initVideoButtons();
321352
} else {
322353
if (!mIsVideoCaptureIntent) {
323354
Utils.showToast(getApplicationContext(), R.string.video_mode_error);
324355
}
325356
}
326357
}
327358

328-
private void setupVideoIcons() {
359+
private void initVideoButtons() {
329360
final Resources res = getResources();
330361
mTogglePhotoVideoBtn.setImageDrawable(res.getDrawable(R.mipmap.photo));
331362
mToggleCameraBtn.setVisibility(View.VISIBLE);
332363
mShutterBtn.setImageDrawable(res.getDrawable(R.mipmap.video_rec));
333364
checkFlash();
365+
setupPreviewImage(false);
366+
}
367+
368+
private void setupPreviewImage(boolean isPhoto) {
369+
final Uri uri = (isPhoto) ? MediaStore.Images.Media.EXTERNAL_CONTENT_URI : MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
370+
final long lastMediaId = getLastMediaId(uri);
371+
if (lastMediaId == 0) {
372+
return;
373+
}
374+
final ContentResolver cr = getContentResolver();
375+
mPreviewUri = Uri.withAppendedPath(uri, String.valueOf(lastMediaId));
376+
Bitmap tmb;
377+
378+
if (isPhoto) {
379+
tmb = MediaStore.Images.Thumbnails.getThumbnail(cr, lastMediaId, MediaStore.Images.Thumbnails.MICRO_KIND, null);
380+
final int rotationDegrees = getImageRotation();
381+
tmb = rotateThumbnail(tmb, rotationDegrees);
382+
} else {
383+
tmb = MediaStore.Video.Thumbnails.getThumbnail(cr, lastMediaId, MediaStore.Video.Thumbnails.MICRO_KIND, null);
384+
}
385+
386+
setPreviewImage(tmb);
387+
}
388+
389+
private int getImageRotation() {
390+
final String[] projection = {MediaStore.Images.ImageColumns.ORIENTATION};
391+
Cursor cursor = null;
392+
try {
393+
cursor = getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, null, null, null);
394+
if (cursor != null && cursor.moveToFirst()) {
395+
final int orientationIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.ORIENTATION);
396+
return cursor.getInt(orientationIndex);
397+
}
398+
} finally {
399+
if (cursor != null) {
400+
cursor.close();
401+
}
402+
}
403+
return 0;
404+
}
405+
406+
private Bitmap rotateThumbnail(Bitmap tmb, int degrees) {
407+
if (degrees == 0)
408+
return tmb;
409+
410+
final Matrix matrix = new Matrix();
411+
matrix.setRotate(degrees, tmb.getWidth() / 2, tmb.getHeight() / 2);
412+
return Bitmap.createBitmap(tmb, 0, 0, tmb.getWidth(), tmb.getHeight(), matrix, true);
413+
}
414+
415+
private void setPreviewImage(final Bitmap bmp) {
416+
if (bmp != null) {
417+
mLastPhotoVideoPreview.post(new Runnable() {
418+
@Override
419+
public void run() {
420+
mLastPhotoVideoPreview.setImageBitmap(bmp);
421+
}
422+
});
423+
}
424+
}
425+
426+
private long getLastMediaId(Uri uri) {
427+
final String[] projection = {MediaStore.Images.ImageColumns._ID};
428+
Cursor cursor = null;
429+
try {
430+
cursor = getContentResolver().query(uri, projection, null, null, MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC");
431+
if (cursor != null && cursor.moveToFirst()) {
432+
final int idIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns._ID);
433+
return cursor.getLong(idIndex);
434+
}
435+
} finally {
436+
if (cursor != null) {
437+
cursor.close();
438+
}
439+
}
440+
return 0;
334441
}
335442

336443
private void hideNavigationBarIcons() {
@@ -364,6 +471,7 @@ protected void onResume() {
364471
super.onResume();
365472
if (hasCameraAndStoragePermission()) {
366473
resumeCameraItems();
474+
setupPreviewImage(mIsInPhotoMode);
367475

368476
if (mIsVideoCaptureIntent && mIsInPhotoMode) {
369477
togglePhotoVideo();
@@ -387,7 +495,7 @@ private void resumeCameraItems() {
387495
}
388496

389497
if (!mIsInPhotoMode) {
390-
setupVideoIcons();
498+
initVideoButtons();
391499
}
392500
} else {
393501
Utils.showToast(getApplicationContext(), R.string.camera_switch_error);
@@ -461,6 +569,7 @@ public int getCurrentOrientation() {
461569

462570
@Override
463571
public void videoSaved(Uri uri) {
572+
setupPreviewImage(mIsInPhotoMode);
464573
if (mIsVideoCaptureIntent) {
465574
final Intent intent = new Intent();
466575
intent.setData(uri);
@@ -471,7 +580,10 @@ public void videoSaved(Uri uri) {
471580
}
472581

473582
@Override
474-
public void mediaSaved() {
583+
public void mediaSaved(String path) {
584+
final String[] paths = {path};
585+
MediaScannerConnection.scanFile(getApplicationContext(), paths, null, this);
586+
475587
if (mIsImageCaptureIntent) {
476588
setResult(RESULT_OK);
477589
finish();
@@ -485,4 +597,9 @@ protected void onDestroy() {
485597
if (mPreview != null)
486598
mPreview.releaseCamera();
487599
}
600+
601+
@Override
602+
public void onScanCompleted(String path, Uri uri) {
603+
setupPreviewImage(mIsInPhotoMode);
604+
}
488605
}

app/src/main/res/layout/activity_main.xml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
android:layout_width="@dimen/icon_size"
1717
android:layout_height="@dimen/icon_size"
1818
android:layout_alignParentRight="true"
19-
android:layout_margin="@dimen/side_icon_padding"
19+
android:layout_marginRight="@dimen/side_icon_padding"
20+
android:layout_marginTop="@dimen/side_icon_padding"
2021
android:padding="@dimen/side_icon_padding"
2122
android:src="@mipmap/about"/>
2223

@@ -30,6 +31,15 @@
3031
android:padding="@dimen/side_icon_padding"
3132
android:src="@mipmap/videocam"/>
3233

34+
<ImageView
35+
android:id="@+id/last_photo_video_preview"
36+
android:layout_width="@dimen/icon_size"
37+
android:layout_height="@dimen/icon_size"
38+
android:layout_alignParentRight="true"
39+
android:layout_below="@+id/toggle_photo_video"
40+
android:layout_marginRight="@dimen/side_icon_padding"
41+
android:padding="@dimen/side_preview_padding"/>
42+
3343
<LinearLayout
3444
android:id="@+id/btn_holder"
3545
android:layout_width="match_parent"

app/src/main/res/values-sw600dp/dimens.xml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
<resources>
2-
<dimen name="side_icon_padding">12dp</dimen>
32
<dimen name="icon_size">64dp</dimen>
43
<dimen name="settings_padding">12dp</dimen>
54
<dimen name="social_padding">12dp</dimen>

app/src/main/res/values/dimens.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
<dimen name="activity_margin">16dp</dimen>
33
<dimen name="preview_btn_margin">32dp</dimen>
44
<dimen name="side_icon_padding">12dp</dimen>
5+
<dimen name="side_preview_padding">8dp</dimen>
56
<dimen name="icon_size">56dp</dimen>
67
<dimen name="settings_padding">8dp</dimen>
78
<dimen name="social_padding">8dp</dimen>

app/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
<string name="camera_switch_error">Switching camera failed</string>
1212
<string name="no_permissions">Not much to do without accessing your camera and storage</string>
1313
<string name="no_audio_permissions">We need the audio permission for recording videos</string>
14+
<string name="no_gallery_app_available">No gallery app available</string>
1415

1516
<!-- Settings -->
1617
<string name="settings">Settings</string>

0 commit comments

Comments
 (0)