diff options
Diffstat (limited to 'java/com/android/dialer/callcomposer/camera')
-rw-r--r-- | java/com/android/dialer/callcomposer/camera/CameraManager.java | 146 | ||||
-rw-r--r-- | java/com/android/dialer/callcomposer/camera/ImagePersistWorker.java (renamed from java/com/android/dialer/callcomposer/camera/ImagePersistTask.java) | 98 | ||||
-rw-r--r-- | java/com/android/dialer/callcomposer/camera/camerafocus/FocusOverlayManager.java | 2 | ||||
-rw-r--r-- | java/com/android/dialer/callcomposer/camera/exif/ExifInterface.java | 2 | ||||
-rw-r--r-- | java/com/android/dialer/callcomposer/camera/exif/ExifParser.java | 2 | ||||
-rw-r--r-- | java/com/android/dialer/callcomposer/camera/exif/ExifTag.java | 2 |
6 files changed, 146 insertions, 106 deletions
diff --git a/java/com/android/dialer/callcomposer/camera/CameraManager.java b/java/com/android/dialer/callcomposer/camera/CameraManager.java index 4cc08ba32..f79f6548c 100644 --- a/java/com/android/dialer/callcomposer/camera/CameraManager.java +++ b/java/com/android/dialer/callcomposer/camera/CameraManager.java @@ -35,6 +35,7 @@ import com.android.dialer.callcomposer.camera.camerafocus.FocusOverlayManager; import com.android.dialer.callcomposer.camera.camerafocus.RenderOverlay; import com.android.dialer.common.Assert; import com.android.dialer.common.LogUtil; +import com.android.dialer.common.concurrent.DialerExecutors; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; @@ -118,7 +119,7 @@ public class CameraManager implements FocusOverlayManager.Listener { /** * The task for opening the camera, so it doesn't block the UI thread Using AsyncTask rather than * SafeAsyncTask because the tasks need to be serialized, but don't need to be on the UI thread - * TODO: If we have other AyncTasks (not SafeAsyncTasks) this may contend and we may need + * TODO(blemmon): If we have other AyncTasks (not SafeAsyncTasks) this may contend and we may need * to create a dedicated thread, or synchronize the threads in the thread pool */ private AsyncTask<Integer, Void, Camera> mOpenCameraTask; @@ -457,9 +458,9 @@ public class CameraManager implements FocusOverlayManager.Listener { int height; if (mRotation == 90 || mRotation == 270) { // Is rotated, so swapping dimensions is desired - //noinspection SuspiciousNameCombination + // noinspection SuspiciousNameCombination width = size.height; - //noinspection SuspiciousNameCombination + // noinspection SuspiciousNameCombination height = size.width; } else { width = size.width; @@ -467,9 +468,20 @@ public class CameraManager implements FocusOverlayManager.Listener { } LogUtil.i( "CameraManager.onPictureTaken", "taken picture size: " + bytes.length + " bytes"); - new ImagePersistTask( - width, height, heightPercent, bytes, mCameraPreview.getContext(), callback) - .execute(); + DialerExecutors.createNonUiTaskBuilder( + new ImagePersistWorker( + width, height, heightPercent, bytes, mCameraPreview.getContext())) + .onSuccess( + (result) -> { + callback.onMediaReady( + result.getUri(), "image/jpeg", result.getWidth(), result.getHeight()); + }) + .onFailure( + (throwable) -> { + callback.onMediaFailed(new Exception("Persisting image failed", throwable)); + }) + .build() + .executeSerial(null); } }; @@ -509,62 +521,61 @@ public class CameraManager implements FocusOverlayManager.Listener { }.execute(); } - /** Updates the orientation of the camera to match the orientation of the device */ - private void updateCameraOrientation() { - if (mCamera == null || mCameraPreview == null || mTakingPicture) { - return; + /** + * Updates the orientation of the {@link Camera} w.r.t. the orientation of the device and the + * orientation that the physical camera is mounted on the device. + * + * @param camera that needs to be reorientated + * @param screenRotation rotation of the physical device + * @param cameraOrientation {@link CameraInfo#orientation} + * @param cameraIsFrontFacing {@link CameraInfo#CAMERA_FACING_FRONT} + * @return rotation that images returned from {@link + * android.hardware.Camera.PictureCallback#onPictureTaken(byte[], Camera)} will be rotated. + */ + @VisibleForTesting + static int updateCameraRotation( + @NonNull Camera camera, + int screenRotation, + int cameraOrientation, + boolean cameraIsFrontFacing) { + Assert.isNotNull(camera); + Assert.checkArgument(cameraOrientation % 90 == 0); + + int rotation = screenRotationToDegress(screenRotation); + boolean portrait = rotation == 0 || rotation == 180; + + if (!portrait && !cameraIsFrontFacing) { + rotation += 180; } + rotation += cameraOrientation; + rotation %= 360; - final WindowManager windowManager = - (WindowManager) mCameraPreview.getContext().getSystemService(Context.WINDOW_SERVICE); + // Rotate the camera + if (portrait && cameraIsFrontFacing) { + camera.setDisplayOrientation((rotation + 180) % 360); + } else { + camera.setDisplayOrientation(rotation); + } + + // Rotate the images returned when a picture is taken + Camera.Parameters params = camera.getParameters(); + params.setRotation(rotation); + camera.setParameters(params); + return rotation; + } - int degrees; - switch (windowManager.getDefaultDisplay().getRotation()) { + private static int screenRotationToDegress(int screenRotation) { + switch (screenRotation) { case Surface.ROTATION_0: - degrees = 0; - break; + return 0; case Surface.ROTATION_90: - degrees = 90; - break; + return 90; case Surface.ROTATION_180: - degrees = 180; - break; + return 180; case Surface.ROTATION_270: - degrees = 270; - break; + return 270; default: - throw Assert.createAssertionFailException(""); - } - - // The display orientation of the camera (this controls the preview image). - int orientation; - - // The clockwise rotation angle relative to the orientation of the camera. This affects - // pictures returned by the camera in Camera.PictureCallback. - int rotation; - if (mCameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { - orientation = (mCameraInfo.orientation + degrees) % 360; - rotation = orientation; - // compensate the mirror but only for orientation - orientation = (360 - orientation) % 360; - } else { // back-facing - orientation = (mCameraInfo.orientation - degrees + 360) % 360; - rotation = orientation; - } - mRotation = rotation; - try { - mCamera.setDisplayOrientation(orientation); - final Camera.Parameters params = mCamera.getParameters(); - params.setRotation(rotation); - mCamera.setParameters(params); - } catch (final RuntimeException e) { - LogUtil.e( - "CameraManager.updateCameraOrientation", - "RuntimeException in CameraManager.updateCameraOrientation", - e); - if (mListener != null) { - mListener.onCameraError(ERROR_OPENING_CAMERA, e); - } + throw Assert.createIllegalStateFailException("Invalid surface rotation."); } } @@ -589,13 +600,19 @@ public class CameraManager implements FocusOverlayManager.Listener { mOrientationHandler.disable(); mOrientationHandler = null; } - // releaseMediaRecorder(true /* cleanupFile */); mFocusOverlayManager.onPreviewStopped(); return; } try { mCamera.stopPreview(); - updateCameraOrientation(); + if (!mTakingPicture) { + mRotation = + updateCameraRotation( + mCamera, + getScreenRotation(), + mCameraInfo.orientation, + mCameraInfo.facing == CameraInfo.CAMERA_FACING_FRONT); + } final Camera.Parameters params = mCamera.getParameters(); final Camera.Size pictureSize = chooseBestPictureSize(); @@ -644,6 +661,14 @@ public class CameraManager implements FocusOverlayManager.Listener { } } + private int getScreenRotation() { + return mCameraPreview + .getContext() + .getSystemService(WindowManager.class) + .getDefaultDisplay() + .getRotation(); + } + public boolean isCameraAvailable() { return mCamera != null && !mTakingPicture && mIsHardwareAccelerationSupported; } @@ -681,7 +706,14 @@ public class CameraManager implements FocusOverlayManager.Listener { @Override public void onOrientationChanged(final int orientation) { - updateCameraOrientation(); + if (!mTakingPicture) { + mRotation = + updateCameraRotation( + mCamera, + getScreenRotation(), + mCameraInfo.orientation, + mCameraInfo.facing == CameraInfo.CAMERA_FACING_FRONT); + } } } diff --git a/java/com/android/dialer/callcomposer/camera/ImagePersistTask.java b/java/com/android/dialer/callcomposer/camera/ImagePersistWorker.java index 31751e536..26b0bde00 100644 --- a/java/com/android/dialer/callcomposer/camera/ImagePersistTask.java +++ b/java/com/android/dialer/callcomposer/camera/ImagePersistWorker.java @@ -20,18 +20,18 @@ import android.annotation.TargetApi; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; -import android.graphics.Canvas; -import android.graphics.Matrix; import android.net.Uri; import android.os.Build.VERSION_CODES; +import android.support.annotation.NonNull; import android.support.v4.content.FileProvider; +import com.android.dialer.callcomposer.camera.ImagePersistWorker.Result; import com.android.dialer.callcomposer.camera.exif.ExifInterface; -import com.android.dialer.callcomposer.camera.exif.ExifTag; import com.android.dialer.callcomposer.util.BitmapResizer; import com.android.dialer.common.Assert; -import com.android.dialer.common.concurrent.FallibleAsyncTask; +import com.android.dialer.common.concurrent.DialerExecutor.Worker; import com.android.dialer.constants.Constants; import com.android.dialer.util.DialerUtils; +import com.google.auto.value.AutoValue; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -39,58 +39,70 @@ import java.io.OutputStream; /** Persisting image routine. */ @TargetApi(VERSION_CODES.M) -public class ImagePersistTask extends FallibleAsyncTask<Void, Void, Uri> { +public class ImagePersistWorker implements Worker<Void, Result> { private int mWidth; private int mHeight; private final float mHeightPercent; private final byte[] mBytes; private final Context mContext; - private final CameraManager.MediaCallback mCallback; - ImagePersistTask( + @AutoValue + abstract static class Result { + + public static Builder builder() { + return new AutoValue_ImagePersistWorker_Result.Builder(); + } + + @NonNull + abstract Uri getUri(); + + abstract int getWidth(); + + abstract int getHeight(); + + @AutoValue.Builder + abstract static class Builder { + abstract Builder setUri(@NonNull Uri uri); + + abstract Builder setWidth(int width); + + abstract Builder setHeight(int height); + + abstract Result build(); + } + } + + ImagePersistWorker( final int width, final int height, final float heightPercent, final byte[] bytes, - final Context context, - final CameraManager.MediaCallback callback) { + final Context context) { Assert.checkArgument(heightPercent >= 0 && heightPercent <= 1); Assert.isNotNull(bytes); Assert.isNotNull(context); - Assert.isNotNull(callback); mWidth = width; mHeight = height; mHeightPercent = heightPercent; mBytes = bytes; mContext = context; - mCallback = callback; } @Override - protected Uri doInBackgroundFallible(final Void... params) throws Exception { + public Result doInBackground(Void unused) throws Exception { File outputFile = DialerUtils.createShareableFile(mContext); try (OutputStream outputStream = new FileOutputStream(outputFile)) { - if (mHeightPercent != 1.0f) { - writeClippedBitmap(outputStream); - } else { - Bitmap bitmap = BitmapFactory.decodeByteArray(mBytes, 0, mBytes.length); - bitmap = BitmapResizer.resizeForEnrichedCalling(bitmap); - bitmap.compress(Bitmap.CompressFormat.JPEG, 90, outputStream); - } + writeClippedBitmap(outputStream); } - return FileProvider.getUriForFile( - mContext, Constants.get().getFileProviderAuthority(), outputFile); - } - - @Override - protected void onPostExecute(FallibleTaskResult<Uri> result) { - if (result.isFailure()) { - mCallback.onMediaFailed(new Exception("Persisting image failed", result.getThrowable())); - } else { - mCallback.onMediaReady(result.getResult(), "image/jpeg", mWidth, mHeight); - } + return Result.builder() + .setUri( + FileProvider.getUriForFile( + mContext, Constants.get().getFileProviderAuthority(), outputFile)) + .setWidth(mWidth) + .setHeight(mHeight) + .build(); } private void writeClippedBitmap(OutputStream outputStream) throws IOException { @@ -105,10 +117,12 @@ public class ImagePersistTask extends FallibleAsyncTask<Void, Void, Uri> { } catch (final IOException e) { // Couldn't get exif tags, not the end of the world } + + ExifInterface.OrientationParams params = ExifInterface.getOrientationParams(orientation); Bitmap bitmap = BitmapFactory.decodeByteArray(mBytes, 0, mBytes.length); final int clippedWidth; final int clippedHeight; - if (ExifInterface.getOrientationParams(orientation).invertDimensions) { + if (params.invertDimensions) { Assert.checkState(mWidth == bitmap.getHeight()); Assert.checkState(mHeight == bitmap.getWidth()); clippedWidth = (int) (mHeight * mHeightPercent); @@ -119,24 +133,18 @@ public class ImagePersistTask extends FallibleAsyncTask<Void, Void, Uri> { clippedWidth = mWidth; clippedHeight = (int) (mHeight * mHeightPercent); } - final int offsetTop = (bitmap.getHeight() - clippedHeight) / 2; - final int offsetLeft = (bitmap.getWidth() - clippedWidth) / 2; + + int offsetTop = (bitmap.getHeight() - clippedHeight) / 2; + int offsetLeft = (bitmap.getWidth() - clippedWidth) / 2; mWidth = clippedWidth; mHeight = clippedHeight; + Bitmap clippedBitmap = - Bitmap.createBitmap(clippedWidth, clippedHeight, Bitmap.Config.ARGB_8888); - clippedBitmap.setDensity(bitmap.getDensity()); - final Canvas clippedBitmapCanvas = new Canvas(clippedBitmap); - final Matrix matrix = new Matrix(); - matrix.postTranslate(-offsetLeft, -offsetTop); - clippedBitmapCanvas.drawBitmap(bitmap, matrix, null /* paint */); - clippedBitmapCanvas.save(); - clippedBitmap = BitmapResizer.resizeForEnrichedCalling(clippedBitmap); - // EXIF data can take a big chunk of the file size and is often cleared by the - // carrier, only store orientation since that's critical - final ExifTag orientationTag = exifInterface.getTag(ExifInterface.TAG_ORIENTATION); + Bitmap.createBitmap(bitmap, offsetLeft, offsetTop, clippedWidth, clippedHeight); + clippedBitmap = BitmapResizer.resizeForEnrichedCalling(clippedBitmap, params.rotation); + // EXIF data can take a big chunk of the file size and we've already manually rotated our image, + // so remove all of the exif data. exifInterface.clearExif(); - exifInterface.setTag(orientationTag); exifInterface.writeExif(clippedBitmap, outputStream); clippedBitmap.recycle(); diff --git a/java/com/android/dialer/callcomposer/camera/camerafocus/FocusOverlayManager.java b/java/com/android/dialer/callcomposer/camera/camerafocus/FocusOverlayManager.java index 1c5ac380c..a5edf3309 100644 --- a/java/com/android/dialer/callcomposer/camera/camerafocus/FocusOverlayManager.java +++ b/java/com/android/dialer/callcomposer/camera/camerafocus/FocusOverlayManager.java @@ -389,7 +389,7 @@ public class FocusOverlayManager { focusIndicator.showStart(); } else { if (Parameters.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusMode)) { - // TODO: check HAL behavior and decide if this can be removed. + // TODO(blemmon): check HAL behavior and decide if this can be removed. focusIndicator.showSuccess(false); } else if (mState == STATE_SUCCESS) { focusIndicator.showSuccess(false); diff --git a/java/com/android/dialer/callcomposer/camera/exif/ExifInterface.java b/java/com/android/dialer/callcomposer/camera/exif/ExifInterface.java index 92dee1c94..1bf9519ad 100644 --- a/java/com/android/dialer/callcomposer/camera/exif/ExifInterface.java +++ b/java/com/android/dialer/callcomposer/camera/exif/ExifInterface.java @@ -286,7 +286,7 @@ public class ExifInterface { /** Wrapper class to define some orientation parameters. */ public static class OrientationParams { - int rotation = 0; + public int rotation = 0; int scaleX = 1; int scaleY = 1; public boolean invertDimensions = false; diff --git a/java/com/android/dialer/callcomposer/camera/exif/ExifParser.java b/java/com/android/dialer/callcomposer/camera/exif/ExifParser.java index 23d748c17..c728845a1 100644 --- a/java/com/android/dialer/callcomposer/camera/exif/ExifParser.java +++ b/java/com/android/dialer/callcomposer/camera/exif/ExifParser.java @@ -499,7 +499,7 @@ public class ExifParser { mTiffStream.skip(4); return null; } - // TODO: handle numOfComp overflow + // TODO(blemmon): handle numOfComp overflow ExifTag tag = new ExifTag( tagId, diff --git a/java/com/android/dialer/callcomposer/camera/exif/ExifTag.java b/java/com/android/dialer/callcomposer/camera/exif/ExifTag.java index a254ae93b..9a03c103c 100644 --- a/java/com/android/dialer/callcomposer/camera/exif/ExifTag.java +++ b/java/com/android/dialer/callcomposer/camera/exif/ExifTag.java @@ -187,7 +187,7 @@ public class ExifTag { /** Gets the component count of this tag. */ - // TODO: fix integer overflows with this + // TODO(blemmon): fix integer overflows with this int getComponentCount() { return mComponentCountActual; } |