add a preview image of the last photo taken

This commit is contained in:
tibbi 2016-07-10 16:23:30 +02:00
parent 2a2a5cc9c6
commit f4a779db66
8 changed files with 147 additions and 25 deletions

View File

@ -12,7 +12,7 @@ import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
public class PhotoProcessor extends AsyncTask<byte[], Void, Void> { public class PhotoProcessor extends AsyncTask<byte[], Void, String> {
private static final String TAG = PhotoProcessor.class.getSimpleName(); private static final String TAG = PhotoProcessor.class.getSimpleName();
private static WeakReference<MainActivity> mActivity; private static WeakReference<MainActivity> mActivity;
private static Uri mUri; private static Uri mUri;
@ -23,7 +23,7 @@ public class PhotoProcessor extends AsyncTask<byte[], Void, Void> {
} }
@Override @Override
protected Void doInBackground(byte[]... params) { protected String doInBackground(byte[]... params) {
FileOutputStream fos = null; FileOutputStream fos = null;
String path; String path;
try { try {
@ -34,7 +34,7 @@ public class PhotoProcessor extends AsyncTask<byte[], Void, Void> {
} }
if (path.isEmpty()) { if (path.isEmpty()) {
return null; return "";
} }
final File photoFile = new File(path); final File photoFile = new File(path);
@ -42,7 +42,7 @@ public class PhotoProcessor extends AsyncTask<byte[], Void, Void> {
fos = new FileOutputStream(photoFile); fos = new FileOutputStream(photoFile);
fos.write(data); fos.write(data);
fos.close(); fos.close();
Utils.scanFile(path, mActivity.get().getApplicationContext()); return photoFile.getAbsolutePath();
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
Log.e(TAG, "PhotoProcessor file not found: " + e.getMessage()); Log.e(TAG, "PhotoProcessor file not found: " + e.getMessage());
} catch (IOException e) { } catch (IOException e) {
@ -57,19 +57,19 @@ public class PhotoProcessor extends AsyncTask<byte[], Void, Void> {
} }
} }
return null; return "";
} }
@Override @Override
protected void onPostExecute(Void aVoid) { protected void onPostExecute(String path) {
super.onPostExecute(aVoid); super.onPostExecute(path);
final MediaSavedListener listener = mActivity.get(); final MediaSavedListener listener = mActivity.get();
if (listener != null) { if (listener != null) {
listener.mediaSaved(); listener.mediaSaved(path);
} }
} }
public interface MediaSavedListener { public interface MediaSavedListener {
void mediaSaved(); void mediaSaved(String path);
} }
} }

View File

@ -621,7 +621,7 @@ public class Preview extends ViewGroup implements SurfaceHolder.Callback, View.O
try { try {
mRecorder.stop(); mRecorder.stop();
final String[] paths = {mCurVideoPath}; final String[] paths = {mCurVideoPath};
MediaScannerConnection.scanFile(getContext(), paths, null, mIsVideoCaptureIntent ? this : null); MediaScannerConnection.scanFile(getContext(), paths, null, this);
} catch (RuntimeException e) { } catch (RuntimeException e) {
new File(mCurVideoPath).delete(); new File(mCurVideoPath).delete();
Utils.showToast(getContext(), R.string.video_saving_error); Utils.showToast(getContext(), R.string.video_saving_error);

View File

@ -7,7 +7,6 @@ import android.content.pm.PackageManager;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Point; import android.graphics.Point;
import android.hardware.Camera; import android.hardware.Camera;
import android.media.MediaScannerConnection;
import android.os.Environment; import android.os.Environment;
import android.support.v4.content.ContextCompat; import android.support.v4.content.ContextCompat;
import android.view.Display; import android.view.Display;
@ -85,11 +84,6 @@ public class Utils {
return Environment.getExternalStoragePublicDirectory(type); return Environment.getExternalStoragePublicDirectory(type);
} }
public static void scanFile(String path, Context context) {
final String[] paths = {path};
MediaScannerConnection.scanFile(context, paths, null, null);
}
public static String formatSeconds(int duration) { public static String formatSeconds(int duration) {
final StringBuilder sb = new StringBuilder(8); final StringBuilder sb = new StringBuilder(8);
final int hours = duration / (60 * 60); final int hours = duration / (60 * 60);

View File

@ -1,14 +1,20 @@
package com.simplemobiletools.camera.activities; package com.simplemobiletools.camera.activities;
import android.Manifest; import android.Manifest;
import android.content.ActivityNotFoundException;
import android.content.ContentResolver;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.res.Resources; import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.hardware.Camera; import android.hardware.Camera;
import android.hardware.Sensor; import android.hardware.Sensor;
import android.hardware.SensorEvent; import android.hardware.SensorEvent;
import android.hardware.SensorEventListener; import android.hardware.SensorEventListener;
import android.hardware.SensorManager; import android.hardware.SensorManager;
import android.media.MediaScannerConnection;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
@ -41,7 +47,8 @@ import butterknife.BindView;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import butterknife.OnClick; import butterknife.OnClick;
public class MainActivity extends AppCompatActivity implements SensorEventListener, PreviewListener, PhotoProcessor.MediaSavedListener { public class MainActivity extends AppCompatActivity
implements SensorEventListener, PreviewListener, PhotoProcessor.MediaSavedListener, MediaScannerConnection.OnScanCompletedListener {
@BindView(R.id.viewHolder) RelativeLayout mViewHolder; @BindView(R.id.viewHolder) RelativeLayout mViewHolder;
@BindView(R.id.toggle_camera) ImageView mToggleCameraBtn; @BindView(R.id.toggle_camera) ImageView mToggleCameraBtn;
@BindView(R.id.toggle_flash) ImageView mToggleFlashBtn; @BindView(R.id.toggle_flash) ImageView mToggleFlashBtn;
@ -49,13 +56,16 @@ public class MainActivity extends AppCompatActivity implements SensorEventListen
@BindView(R.id.shutter) ImageView mShutterBtn; @BindView(R.id.shutter) ImageView mShutterBtn;
@BindView(R.id.video_rec_curr_timer) TextView mRecCurrTimer; @BindView(R.id.video_rec_curr_timer) TextView mRecCurrTimer;
@BindView(R.id.about) View mAboutBtn; @BindView(R.id.about) View mAboutBtn;
@BindView(R.id.last_photo_video_preview) ImageView mLastPhotoVideoPreview;
private static final String TAG = MainActivity.class.getSimpleName();
private static final int CAMERA_STORAGE_PERMISSION = 1; private static final int CAMERA_STORAGE_PERMISSION = 1;
private static final int AUDIO_PERMISSION = 2; private static final int AUDIO_PERMISSION = 2;
private static SensorManager mSensorManager; private static SensorManager mSensorManager;
private static Preview mPreview; private static Preview mPreview;
private static Handler mTimerHandler; private static Handler mTimerHandler;
private static Uri mPreviewUri;
private static boolean mIsFlashEnabled; private static boolean mIsFlashEnabled;
private static boolean mIsInPhotoMode; private static boolean mIsInPhotoMode;
@ -153,6 +163,7 @@ public class MainActivity extends AppCompatActivity implements SensorEventListen
mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE); mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
mIsInPhotoMode = true; mIsInPhotoMode = true;
mTimerHandler = new Handler(); mTimerHandler = new Handler();
setupPreviewImage(true);
} }
private boolean hasCameraAndStoragePermission() { private boolean hasCameraAndStoragePermission() {
@ -209,6 +220,25 @@ public class MainActivity extends AppCompatActivity implements SensorEventListen
} }
} }
@OnClick(R.id.last_photo_video_preview)
public void showLastMediaPreview() {
if (mPreviewUri == null)
return;
try {
final String REVIEW_ACTION = "com.android.camera.action.REVIEW";
Intent intent = new Intent(REVIEW_ACTION, mPreviewUri);
this.startActivity(intent);
} catch (ActivityNotFoundException e) {
Intent intent = new Intent(Intent.ACTION_VIEW, mPreviewUri);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
} else {
Utils.showToast(getApplicationContext(), R.string.no_gallery_app_available);
}
}
}
@OnClick(R.id.toggle_flash) @OnClick(R.id.toggle_flash)
public void toggleFlash() { public void toggleFlash() {
if (!checkCameraAvailable()) { if (!checkCameraAvailable()) {
@ -304,7 +334,7 @@ public class MainActivity extends AppCompatActivity implements SensorEventListen
if (mIsInPhotoMode) { if (mIsInPhotoMode) {
initPhotoButtons(); initPhotoButtons();
} else { } else {
initVideoButtons(); tryInitVideoButtons();
} }
} }
@ -313,11 +343,12 @@ public class MainActivity extends AppCompatActivity implements SensorEventListen
mTogglePhotoVideoBtn.setImageDrawable(res.getDrawable(R.mipmap.videocam)); mTogglePhotoVideoBtn.setImageDrawable(res.getDrawable(R.mipmap.videocam));
mShutterBtn.setImageDrawable(res.getDrawable(R.mipmap.camera)); mShutterBtn.setImageDrawable(res.getDrawable(R.mipmap.camera));
mPreview.initPhotoMode(); mPreview.initPhotoMode();
setupPreviewImage(true);
} }
private void initVideoButtons() { private void tryInitVideoButtons() {
if (mPreview.initRecorder()) { if (mPreview.initRecorder()) {
setupVideoIcons(); initVideoButtons();
} else { } else {
if (!mIsVideoCaptureIntent) { if (!mIsVideoCaptureIntent) {
Utils.showToast(getApplicationContext(), R.string.video_mode_error); Utils.showToast(getApplicationContext(), R.string.video_mode_error);
@ -325,12 +356,88 @@ public class MainActivity extends AppCompatActivity implements SensorEventListen
} }
} }
private void setupVideoIcons() { private void initVideoButtons() {
final Resources res = getResources(); final Resources res = getResources();
mTogglePhotoVideoBtn.setImageDrawable(res.getDrawable(R.mipmap.photo)); mTogglePhotoVideoBtn.setImageDrawable(res.getDrawable(R.mipmap.photo));
mToggleCameraBtn.setVisibility(View.VISIBLE); mToggleCameraBtn.setVisibility(View.VISIBLE);
mShutterBtn.setImageDrawable(res.getDrawable(R.mipmap.video_rec)); mShutterBtn.setImageDrawable(res.getDrawable(R.mipmap.video_rec));
checkFlash(); checkFlash();
setupPreviewImage(false);
}
private void setupPreviewImage(boolean isPhoto) {
final Uri uri = (isPhoto) ? MediaStore.Images.Media.EXTERNAL_CONTENT_URI : MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
final long lastMediaId = getLastMediaId(uri);
if (lastMediaId == 0) {
return;
}
final ContentResolver cr = getContentResolver();
mPreviewUri = Uri.withAppendedPath(uri, String.valueOf(lastMediaId));
Bitmap tmb;
if (isPhoto) {
tmb = MediaStore.Images.Thumbnails.getThumbnail(cr, lastMediaId, MediaStore.Images.Thumbnails.MICRO_KIND, null);
final int rotationDegrees = getImageRotation();
tmb = rotateThumbnail(tmb, rotationDegrees);
} else {
tmb = MediaStore.Video.Thumbnails.getThumbnail(cr, lastMediaId, MediaStore.Video.Thumbnails.MICRO_KIND, null);
}
setPreviewImage(tmb);
}
private int getImageRotation() {
final String[] projection = {MediaStore.Images.ImageColumns.ORIENTATION};
Cursor cursor = null;
try {
cursor = getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
final int orientationIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.ORIENTATION);
return cursor.getInt(orientationIndex);
}
} finally {
if (cursor != null) {
cursor.close();
}
}
return 0;
}
private Bitmap rotateThumbnail(Bitmap tmb, int degrees) {
if (degrees == 0)
return tmb;
final Matrix matrix = new Matrix();
matrix.setRotate(degrees, tmb.getWidth() / 2, tmb.getHeight() / 2);
return Bitmap.createBitmap(tmb, 0, 0, tmb.getWidth(), tmb.getHeight(), matrix, true);
}
private void setPreviewImage(final Bitmap bmp) {
if (bmp != null) {
mLastPhotoVideoPreview.post(new Runnable() {
@Override
public void run() {
mLastPhotoVideoPreview.setImageBitmap(bmp);
}
});
}
}
private long getLastMediaId(Uri uri) {
final String[] projection = {MediaStore.Images.ImageColumns._ID};
Cursor cursor = null;
try {
cursor = getContentResolver().query(uri, projection, null, null, MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC");
if (cursor != null && cursor.moveToFirst()) {
final int idIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns._ID);
return cursor.getLong(idIndex);
}
} finally {
if (cursor != null) {
cursor.close();
}
}
return 0;
} }
private void hideNavigationBarIcons() { private void hideNavigationBarIcons() {
@ -364,6 +471,7 @@ public class MainActivity extends AppCompatActivity implements SensorEventListen
super.onResume(); super.onResume();
if (hasCameraAndStoragePermission()) { if (hasCameraAndStoragePermission()) {
resumeCameraItems(); resumeCameraItems();
setupPreviewImage(mIsInPhotoMode);
if (mIsVideoCaptureIntent && mIsInPhotoMode) { if (mIsVideoCaptureIntent && mIsInPhotoMode) {
togglePhotoVideo(); togglePhotoVideo();
@ -387,7 +495,7 @@ public class MainActivity extends AppCompatActivity implements SensorEventListen
} }
if (!mIsInPhotoMode) { if (!mIsInPhotoMode) {
setupVideoIcons(); initVideoButtons();
} }
} else { } else {
Utils.showToast(getApplicationContext(), R.string.camera_switch_error); Utils.showToast(getApplicationContext(), R.string.camera_switch_error);
@ -461,6 +569,7 @@ public class MainActivity extends AppCompatActivity implements SensorEventListen
@Override @Override
public void videoSaved(Uri uri) { public void videoSaved(Uri uri) {
setupPreviewImage(mIsInPhotoMode);
if (mIsVideoCaptureIntent) { if (mIsVideoCaptureIntent) {
final Intent intent = new Intent(); final Intent intent = new Intent();
intent.setData(uri); intent.setData(uri);
@ -471,7 +580,10 @@ public class MainActivity extends AppCompatActivity implements SensorEventListen
} }
@Override @Override
public void mediaSaved() { public void mediaSaved(String path) {
final String[] paths = {path};
MediaScannerConnection.scanFile(getApplicationContext(), paths, null, this);
if (mIsImageCaptureIntent) { if (mIsImageCaptureIntent) {
setResult(RESULT_OK); setResult(RESULT_OK);
finish(); finish();
@ -485,4 +597,9 @@ public class MainActivity extends AppCompatActivity implements SensorEventListen
if (mPreview != null) if (mPreview != null)
mPreview.releaseCamera(); mPreview.releaseCamera();
} }
@Override
public void onScanCompleted(String path, Uri uri) {
setupPreviewImage(mIsInPhotoMode);
}
} }

View File

@ -16,7 +16,8 @@
android:layout_width="@dimen/icon_size" android:layout_width="@dimen/icon_size"
android:layout_height="@dimen/icon_size" android:layout_height="@dimen/icon_size"
android:layout_alignParentRight="true" android:layout_alignParentRight="true"
android:layout_margin="@dimen/side_icon_padding" android:layout_marginRight="@dimen/side_icon_padding"
android:layout_marginTop="@dimen/side_icon_padding"
android:padding="@dimen/side_icon_padding" android:padding="@dimen/side_icon_padding"
android:src="@mipmap/about"/> android:src="@mipmap/about"/>
@ -30,6 +31,15 @@
android:padding="@dimen/side_icon_padding" android:padding="@dimen/side_icon_padding"
android:src="@mipmap/videocam"/> android:src="@mipmap/videocam"/>
<ImageView
android:id="@+id/last_photo_video_preview"
android:layout_width="@dimen/icon_size"
android:layout_height="@dimen/icon_size"
android:layout_alignParentRight="true"
android:layout_below="@+id/toggle_photo_video"
android:layout_marginRight="@dimen/side_icon_padding"
android:padding="@dimen/side_preview_padding"/>
<LinearLayout <LinearLayout
android:id="@+id/btn_holder" android:id="@+id/btn_holder"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -1,5 +1,4 @@
<resources> <resources>
<dimen name="side_icon_padding">12dp</dimen>
<dimen name="icon_size">64dp</dimen> <dimen name="icon_size">64dp</dimen>
<dimen name="settings_padding">12dp</dimen> <dimen name="settings_padding">12dp</dimen>
<dimen name="social_padding">12dp</dimen> <dimen name="social_padding">12dp</dimen>

View File

@ -2,6 +2,7 @@
<dimen name="activity_margin">16dp</dimen> <dimen name="activity_margin">16dp</dimen>
<dimen name="preview_btn_margin">32dp</dimen> <dimen name="preview_btn_margin">32dp</dimen>
<dimen name="side_icon_padding">12dp</dimen> <dimen name="side_icon_padding">12dp</dimen>
<dimen name="side_preview_padding">8dp</dimen>
<dimen name="icon_size">56dp</dimen> <dimen name="icon_size">56dp</dimen>
<dimen name="settings_padding">8dp</dimen> <dimen name="settings_padding">8dp</dimen>
<dimen name="social_padding">8dp</dimen> <dimen name="social_padding">8dp</dimen>

View File

@ -11,6 +11,7 @@
<string name="camera_switch_error">Switching camera failed</string> <string name="camera_switch_error">Switching camera failed</string>
<string name="no_permissions">Not much to do without accessing your camera and storage</string> <string name="no_permissions">Not much to do without accessing your camera and storage</string>
<string name="no_audio_permissions">We need the audio permission for recording videos</string> <string name="no_audio_permissions">We need the audio permission for recording videos</string>
<string name="no_gallery_app_available">No gallery app available</string>
<!-- Settings --> <!-- Settings -->
<string name="settings">Settings</string> <string name="settings">Settings</string>