diff --git a/app/build.gradle b/app/build.gradle index eca8da289..679fb06b5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -190,6 +190,7 @@ dependencies { implementation 'androidx.camera:camera-camera2:1.0.1' implementation 'androidx.camera:camera-lifecycle:1.0.1' implementation 'androidx.camera:camera-view:1.0.0-alpha20' + implementation "androidx.exifinterface:exifinterface:1.3.3" implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' diff --git a/app/src/main/java/com/nextcloud/talk/activities/TakePhotoActivity.java b/app/src/main/java/com/nextcloud/talk/activities/TakePhotoActivity.java index 506f91db5..00b007d8a 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/TakePhotoActivity.java +++ b/app/src/main/java/com/nextcloud/talk/activities/TakePhotoActivity.java @@ -24,12 +24,15 @@ package com.nextcloud.talk.activities; import android.content.Context; import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Bundle; import android.util.Log; import android.util.Size; import android.view.OrientationEventListener; import android.view.Surface; +import android.view.View; import android.widget.Toast; import com.google.common.util.concurrent.ListenableFuture; @@ -39,6 +42,8 @@ import com.nextcloud.talk.models.TakePictureViewModel; import com.nextcloud.talk.utils.FileUtils; import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; @@ -53,6 +58,7 @@ import androidx.camera.core.ImageCaptureException; import androidx.camera.core.Preview; import androidx.camera.lifecycle.ProcessCameraProvider; import androidx.core.content.ContextCompat; +import androidx.exifinterface.media.ExifInterface; import androidx.lifecycle.ViewModelProvider; public class TakePhotoActivity extends AppCompatActivity { @@ -99,7 +105,7 @@ public class TakePhotoActivity extends AppCompatActivity { viewModel.isTorchEnabled() .observe( this, - enabled -> camera.getCameraControl().enableTorch(enabled)); + enabled -> camera.getCameraControl().enableTorch(enabled)); binding.toggleTorch.setOnClickListener((v) -> viewModel.toggleTorchEnabled()); binding.switchCamera.setOnClickListener((v) -> { @@ -111,6 +117,20 @@ public class TakePhotoActivity extends AppCompatActivity { imageCapture, preview); }); + binding.retake.setOnClickListener((v) -> { + Uri uri = (Uri) binding.photoPreview.getTag(); + File photoFile = new File(uri.getPath()); + if (!photoFile.delete()) { + Log.w(TAG, "Error deleting temp camera image"); + } + binding.takePhoto.setEnabled(true); + showCameraElements(); + }); + binding.send.setOnClickListener((v) -> { + Uri uri = (Uri) binding.photoPreview.getTag(); + setResult(RESULT_OK, new Intent().setDataAndType(uri, "image/jpeg")); + finish(); + }); } catch (IllegalArgumentException | ExecutionException | InterruptedException e) { Log.e(TAG, "Error taking picture", e); Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show(); @@ -119,6 +139,28 @@ public class TakePhotoActivity extends AppCompatActivity { }, ContextCompat.getMainExecutor(this)); } + private void showCameraElements() { + binding.send.setVisibility(View.GONE); + binding.retake.setVisibility(View.GONE); + binding.photoPreview.setVisibility(View.GONE); + + binding.preview.setVisibility(View.VISIBLE); + binding.takePhoto.setVisibility(View.VISIBLE); + binding.switchCamera.setVisibility(View.VISIBLE); + binding.toggleTorch.setVisibility(View.VISIBLE); + } + + private void showPictureProcessingElements() { + binding.preview.setVisibility(View.GONE); + binding.takePhoto.setVisibility(View.GONE); + binding.switchCamera.setVisibility(View.GONE); + binding.toggleTorch.setVisibility(View.GONE); + + binding.send.setVisibility(View.VISIBLE); + binding.retake.setVisibility(View.VISIBLE); + binding.photoPreview.setVisibility(View.VISIBLE); + } + private ImageCapture getImageCapture() { final ImageCapture imageCapture = new ImageCapture.Builder().setTargetResolution(new Size(720, 1280)).build(); @@ -145,7 +187,7 @@ public class TakePhotoActivity extends AppCompatActivity { binding.takePhoto.setOnClickListener((v) -> { binding.takePhoto.setEnabled(false); - final String photoFileName = dateFormat.format(new Date())+ ".jpg"; + final String photoFileName = dateFormat.format(new Date()) + ".jpg"; try { final File photoFile = FileUtils.getTempCacheFile(this, "photos/" + photoFileName); final ImageCapture.OutputFileOptions options = @@ -155,24 +197,33 @@ public class TakePhotoActivity extends AppCompatActivity { ContextCompat.getMainExecutor(this), new ImageCapture.OnImageSavedCallback() { - @Override - public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) { - final Uri savedUri = Uri.fromFile(photoFile); - Log.i(TAG, "onImageSaved - savedUri:" + savedUri); - setResult(RESULT_OK, new Intent().setDataAndType(savedUri, "image/jpeg")); - finish(); - } - - @Override - public void onError(@NonNull ImageCaptureException e) { - Log.e(TAG, "Error", e); - - if(!photoFile.delete()) { - Log.w(TAG, "Deleting picture failed"); + @Override + public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) { + final Uri savedUri = Uri.fromFile(photoFile); + Log.i(TAG, "onImageSaved - savedUri:" + savedUri); + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inPreferredConfig = Bitmap.Config.ARGB_8888; + try { + binding.photoPreview.setImageBitmap( + BitmapFactory.decodeStream(new FileInputStream(photoFile), null, options)); + binding.photoPreview.setRotation(getImageOrientation(photoFile)); + binding.photoPreview.setTag(savedUri); + showPictureProcessingElements(); + } catch (FileNotFoundException e) { + Log.w(TAG, "Error reading image", e); + } } - binding.takePhoto.setEnabled(true); - } - }); + + @Override + public void onError(@NonNull ImageCaptureException e) { + Log.e(TAG, "Error", e); + + if (!photoFile.delete()) { + Log.w(TAG, "Deleting picture failed"); + } + binding.takePhoto.setEnabled(true); + } + }); } catch (Exception e) { Toast.makeText(this, R.string.take_photo_error_deleting_picture, Toast.LENGTH_SHORT).show(); } @@ -181,6 +232,36 @@ public class TakePhotoActivity extends AppCompatActivity { return imageCapture; } + public int getImageOrientation(File imageFile) { + int rotate = 0; + try { + ExifInterface exif = new ExifInterface(imageFile.getAbsolutePath()); + int orientation = exif.getAttributeInt( + ExifInterface.TAG_ORIENTATION, + ExifInterface.ORIENTATION_NORMAL); + + switch (orientation) { + case ExifInterface.ORIENTATION_ROTATE_270: + rotate = 270; + break; + case ExifInterface.ORIENTATION_ROTATE_180: + rotate = 180; + break; + case ExifInterface.ORIENTATION_ROTATE_90: + rotate = 90; + break; + default: + rotate = 0; + break; + } + + Log.i(TAG, "ImageOrientation - Exif orientation: " + orientation + " - " + "Rotate value: " + rotate); + } catch (Exception e) { + Log.w(TAG, "Error calculation rotation value"); + } + return rotate; + } + private Preview getPreview() { Preview preview = new Preview.Builder().build(); preview.setSurfaceProvider(binding.preview.getSurfaceProvider()); diff --git a/app/src/main/res/drawable/ic_autorenew.xml b/app/src/main/res/drawable/ic_autorenew.xml new file mode 100644 index 000000000..870362054 --- /dev/null +++ b/app/src/main/res/drawable/ic_autorenew.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_take_picture.xml b/app/src/main/res/layout/activity_take_picture.xml index 160a584fc..b66347a18 100644 --- a/app/src/main/res/layout/activity_take_picture.xml +++ b/app/src/main/res/layout/activity_take_picture.xml @@ -34,6 +34,13 @@ android:layout_width="match_parent" android:layout_height="match_parent" /> + + + + + +