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.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 WeakReference<MainActivity> mActivity;
private static Uri mUri;
@ -23,7 +23,7 @@ public class PhotoProcessor extends AsyncTask<byte[], Void, Void> {
}
@Override
protected Void doInBackground(byte[]... params) {
protected String doInBackground(byte[]... params) {
FileOutputStream fos = null;
String path;
try {
@ -34,7 +34,7 @@ public class PhotoProcessor extends AsyncTask<byte[], Void, Void> {
}
if (path.isEmpty()) {
return null;
return "";
}
final File photoFile = new File(path);
@ -42,7 +42,7 @@ public class PhotoProcessor extends AsyncTask<byte[], Void, Void> {
fos = new FileOutputStream(photoFile);
fos.write(data);
fos.close();
Utils.scanFile(path, mActivity.get().getApplicationContext());
return photoFile.getAbsolutePath();
} catch (FileNotFoundException e) {
Log.e(TAG, "PhotoProcessor file not found: " + e.getMessage());
} catch (IOException e) {
@ -57,19 +57,19 @@ public class PhotoProcessor extends AsyncTask<byte[], Void, Void> {
}
}
return null;
return "";
}
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
protected void onPostExecute(String path) {
super.onPostExecute(path);
final MediaSavedListener listener = mActivity.get();
if (listener != null) {
listener.mediaSaved();
listener.mediaSaved(path);
}
}
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 {
mRecorder.stop();
final String[] paths = {mCurVideoPath};
MediaScannerConnection.scanFile(getContext(), paths, null, mIsVideoCaptureIntent ? this : null);
MediaScannerConnection.scanFile(getContext(), paths, null, this);
} catch (RuntimeException e) {
new File(mCurVideoPath).delete();
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.graphics.Point;
import android.hardware.Camera;
import android.media.MediaScannerConnection;
import android.os.Environment;
import android.support.v4.content.ContextCompat;
import android.view.Display;
@ -85,11 +84,6 @@ public class Utils {
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) {
final StringBuilder sb = new StringBuilder(8);
final int hours = duration / (60 * 60);

View File

@ -1,14 +1,20 @@
package com.simplemobiletools.camera.activities;
import android.Manifest;
import android.content.ActivityNotFoundException;
import android.content.ContentResolver;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.hardware.Camera;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@ -41,7 +47,8 @@ import butterknife.BindView;
import butterknife.ButterKnife;
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.toggle_camera) ImageView mToggleCameraBtn;
@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.video_rec_curr_timer) TextView mRecCurrTimer;
@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 AUDIO_PERMISSION = 2;
private static SensorManager mSensorManager;
private static Preview mPreview;
private static Handler mTimerHandler;
private static Uri mPreviewUri;
private static boolean mIsFlashEnabled;
private static boolean mIsInPhotoMode;
@ -153,6 +163,7 @@ public class MainActivity extends AppCompatActivity implements SensorEventListen
mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
mIsInPhotoMode = true;
mTimerHandler = new Handler();
setupPreviewImage(true);
}
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)
public void toggleFlash() {
if (!checkCameraAvailable()) {
@ -304,7 +334,7 @@ public class MainActivity extends AppCompatActivity implements SensorEventListen
if (mIsInPhotoMode) {
initPhotoButtons();
} else {
initVideoButtons();
tryInitVideoButtons();
}
}
@ -313,11 +343,12 @@ public class MainActivity extends AppCompatActivity implements SensorEventListen
mTogglePhotoVideoBtn.setImageDrawable(res.getDrawable(R.mipmap.videocam));
mShutterBtn.setImageDrawable(res.getDrawable(R.mipmap.camera));
mPreview.initPhotoMode();
setupPreviewImage(true);
}
private void initVideoButtons() {
private void tryInitVideoButtons() {
if (mPreview.initRecorder()) {
setupVideoIcons();
initVideoButtons();
} else {
if (!mIsVideoCaptureIntent) {
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();
mTogglePhotoVideoBtn.setImageDrawable(res.getDrawable(R.mipmap.photo));
mToggleCameraBtn.setVisibility(View.VISIBLE);
mShutterBtn.setImageDrawable(res.getDrawable(R.mipmap.video_rec));
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() {
@ -364,6 +471,7 @@ public class MainActivity extends AppCompatActivity implements SensorEventListen
super.onResume();
if (hasCameraAndStoragePermission()) {
resumeCameraItems();
setupPreviewImage(mIsInPhotoMode);
if (mIsVideoCaptureIntent && mIsInPhotoMode) {
togglePhotoVideo();
@ -387,7 +495,7 @@ public class MainActivity extends AppCompatActivity implements SensorEventListen
}
if (!mIsInPhotoMode) {
setupVideoIcons();
initVideoButtons();
}
} else {
Utils.showToast(getApplicationContext(), R.string.camera_switch_error);
@ -461,6 +569,7 @@ public class MainActivity extends AppCompatActivity implements SensorEventListen
@Override
public void videoSaved(Uri uri) {
setupPreviewImage(mIsInPhotoMode);
if (mIsVideoCaptureIntent) {
final Intent intent = new Intent();
intent.setData(uri);
@ -471,7 +580,10 @@ public class MainActivity extends AppCompatActivity implements SensorEventListen
}
@Override
public void mediaSaved() {
public void mediaSaved(String path) {
final String[] paths = {path};
MediaScannerConnection.scanFile(getApplicationContext(), paths, null, this);
if (mIsImageCaptureIntent) {
setResult(RESULT_OK);
finish();
@ -485,4 +597,9 @@ public class MainActivity extends AppCompatActivity implements SensorEventListen
if (mPreview != null)
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_height="@dimen/icon_size"
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:src="@mipmap/about"/>
@ -30,6 +31,15 @@
android:padding="@dimen/side_icon_padding"
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
android:id="@+id/btn_holder"
android:layout_width="match_parent"

View File

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

View File

@ -2,6 +2,7 @@
<dimen name="activity_margin">16dp</dimen>
<dimen name="preview_btn_margin">32dp</dimen>
<dimen name="side_icon_padding">12dp</dimen>
<dimen name="side_preview_padding">8dp</dimen>
<dimen name="icon_size">56dp</dimen>
<dimen name="settings_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="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_gallery_app_available">No gallery app available</string>
<!-- Settings -->
<string name="settings">Settings</string>