Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Timob 13001 3 1 x: Android: Camera overlay does not support portrait mode and default view is in Landscape only. #4200

Merged
merged 3 commits into from
Apr 23, 2013
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Color;
import android.hardware.Camera;
import android.hardware.Camera.AutoFocusCallback;
Expand All @@ -35,20 +35,25 @@
import android.net.Uri;
import android.os.Bundle;
import android.view.Gravity;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.ViewGroup.LayoutParams;
import android.widget.FrameLayout;

public class TiCameraActivity extends TiBaseActivity implements SurfaceHolder.Callback {
public class TiCameraActivity extends TiBaseActivity implements SurfaceHolder.Callback
{
private static final String TAG = "TiCameraActivity";
private static Camera camera;
private static Size optimalPreviewSize;
private static List<Size> supportedPreviewSizes;

private TiViewProxy localOverlayProxy = null;
private SurfaceView preview;
private PreviewLayout previewLayout;
private FrameLayout cameraLayout;
private boolean previewRunning = false;
private int currentRotation;

public static TiViewProxy overlayProxy = null;
public static TiCameraActivity cameraActivity = null;
Expand All @@ -57,23 +62,31 @@ public class TiCameraActivity extends TiBaseActivity implements SurfaceHolder.Ca
public static KrollFunction successCallback, errorCallback, cancelCallback;
public static boolean saveToPhotoGallery = false;

private static class PreviewLayout extends FrameLayout {
private double aspectRatio;
private static class PreviewLayout extends FrameLayout
{
private double aspectRatio = 1;

public PreviewLayout(Context context) {
public PreviewLayout(Context context)
{
super(context);
setAspectRatio(4.0/3.0);
}

public void setAspectRatio(double aspectRatio) {
this.aspectRatio = aspectRatio;
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
int previewWidth = MeasureSpec.getSize(widthMeasureSpec);
int previewHeight = MeasureSpec.getSize(heightMeasureSpec);

// Set the preview size to the most optimal given the target size
optimalPreviewSize = getOptimalPreviewSize(supportedPreviewSizes, previewWidth, previewHeight);
if (optimalPreviewSize != null) {
if (previewWidth > previewHeight) {
aspectRatio = (double) optimalPreviewSize.width / optimalPreviewSize.height;
} else {
aspectRatio = (double) optimalPreviewSize.height / optimalPreviewSize.width;
}
}

// Resize the preview frame with correct aspect ratio.
if (previewWidth > previewHeight * aspectRatio) {
previewWidth = (int) (previewHeight * aspectRatio + .5);
Expand All @@ -83,12 +96,13 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
}

super.onMeasure(MeasureSpec.makeMeasureSpec(previewWidth, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(previewHeight, MeasureSpec.EXACTLY));
MeasureSpec.makeMeasureSpec(previewHeight, MeasureSpec.EXACTLY));
}
}

@Override
public void onCreate(Bundle savedInstanceState) {
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);

// create camera preview
Expand All @@ -105,75 +119,142 @@ public void onCreate(Bundle savedInstanceState) {
previewLayout = new PreviewLayout(this);
cameraLayout = new FrameLayout(this);
cameraLayout.setBackgroundColor(Color.BLACK);
cameraLayout.addView(previewLayout, new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, Gravity.CENTER));
cameraLayout.addView(previewLayout, new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT, Gravity.CENTER));

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
setContentView(cameraLayout);

camera = Camera.open();
if (camera != null) {
supportedPreviewSizes = camera.getParameters().getSupportedPreviewSizes();
} else {
onError(MediaModule.UNKNOWN_ERROR, "Unable to access the first back-facing camera.");
finish();
}
}

public void surfaceChanged(SurfaceHolder previewHolder, int format, int width, int height) {
if (!previewRunning) {
// Set the preview size to the most optimal given the target size and aspect ratio.
Parameters param = camera.getParameters();
Size pictureSize = param.getPictureSize();
double aspectRatio = (double) pictureSize.width / pictureSize.height;
previewLayout.setAspectRatio(aspectRatio);
List<Size> supportedPreviewSizes = param.getSupportedPreviewSizes();
Size previewSize = getOptimalPreviewSize(supportedPreviewSizes, width, height, aspectRatio);

// Set appropriate focus mode if supported.
List<String> supportedFocusModes = param.getSupportedFocusModes();
if (supportedFocusModes.contains(MediaModule.FOCUS_MODE_CONTINUOUS_PICTURE)) {
param.setFocusMode(MediaModule.FOCUS_MODE_CONTINUOUS_PICTURE);
} else if (supportedFocusModes.contains(Parameters.FOCUS_MODE_AUTO)) {
param.setFocusMode(Parameters.FOCUS_MODE_AUTO);
} else if (supportedFocusModes.contains(Parameters.FOCUS_MODE_MACRO)) {
param.setFocusMode(Parameters.FOCUS_MODE_MACRO);
}
public void surfaceChanged(SurfaceHolder previewHolder, int format, int width, int height)
{
if (camera == null) {
return;
}

if (previewSize != null) {
param.setPreviewSize(previewSize.width, previewSize.height);
camera.setParameters(param);
int rotation = getWindowManager().getDefaultDisplay().getRotation();
if (currentRotation == rotation && previewRunning) {
return;
}
if (previewRunning) {
try {
camera.stopPreview();
} catch (Exception e) {
// ignore: tried to stop a non=existent preview
}
}

currentRotation = rotation;
Parameters param = camera.getParameters();
int orientation = TiApplication.getInstance().getResources().getConfiguration().orientation;
// The camera preview is always displayed in landscape mode. Need to rotate the preview according to
// the current orientation of the device.
switch (rotation) {
case Surface.ROTATION_0:
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
// The "natural" orientation of the device is a portrait orientation, eg. phones.
// Need to rotate 90 degrees.
camera.setDisplayOrientation(90);
} else {
// The "natural" orientation of the device is a landscape orientation, eg. tablets.
// Set the camera to the starting position (0 degree).
camera.setDisplayOrientation(0);
}
break;
case Surface.ROTATION_90:
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
camera.setDisplayOrientation(0);
} else {
camera.setDisplayOrientation(270);
}
break;
case Surface.ROTATION_180:
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
camera.setDisplayOrientation(270);
} else {
camera.setDisplayOrientation(180);
}
break;
case Surface.ROTATION_270:
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
camera.setDisplayOrientation(180);
} else {
camera.setDisplayOrientation(90);
}
break;
}

// Set appropriate focus mode if supported.
List<String> supportedFocusModes = param.getSupportedFocusModes();
if (supportedFocusModes.contains(MediaModule.FOCUS_MODE_CONTINUOUS_PICTURE)) {
param.setFocusMode(MediaModule.FOCUS_MODE_CONTINUOUS_PICTURE);
} else if (supportedFocusModes.contains(Parameters.FOCUS_MODE_AUTO)) {
param.setFocusMode(Parameters.FOCUS_MODE_AUTO);
} else if (supportedFocusModes.contains(Parameters.FOCUS_MODE_MACRO)) {
param.setFocusMode(Parameters.FOCUS_MODE_MACRO);
}

if (optimalPreviewSize != null) {
param.setPreviewSize(optimalPreviewSize.width, optimalPreviewSize.height);
camera.setParameters(param);
}

try {
camera.setPreviewDisplay(previewHolder);
previewRunning = true;
camera.startPreview();
} catch (Exception e) {
onError(MediaModule.UNKNOWN_ERROR, "Unable to setup preview surface: " + e.getMessage());
finish();
return;
}
}

public void surfaceCreated(SurfaceHolder previewHolder) {
camera = Camera.open();

public void surfaceCreated(SurfaceHolder previewHolder)
{
try {
camera.setPreviewDisplay(previewHolder);

} catch(IOException e) {
} catch (Exception e) {
onError(MediaModule.UNKNOWN_ERROR, "Unable to setup preview surface: " + e.getMessage());
cancelCallback = null;
finish();
return;
}
currentRotation = getWindowManager().getDefaultDisplay().getRotation();
}

// make sure to call release() otherwise you will have to force kill the app before
// make sure to call release() otherwise you will have to force kill the app before
// the built in camera will open
public void surfaceDestroyed(SurfaceHolder previewHolder) {
public void surfaceDestroyed(SurfaceHolder previewHolder)
{
stopPreview();
camera.release();
camera = null;
if (camera != null) {
camera.release();
camera = null;
}
}

@Override
protected void onResume() {
protected void onResume()
{
super.onResume();

cameraActivity = this;
previewLayout.addView(preview, new FrameLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
cameraLayout.addView(localOverlayProxy.getOrCreateView().getNativeView(), new FrameLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
cameraLayout.addView(localOverlayProxy.getOrCreateView().getNativeView(), new FrameLayout.LayoutParams(
LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
}

@Override
protected void onPause() {
protected void onPause()
{
super.onPause();

stopPreview();
Expand All @@ -190,7 +271,8 @@ protected void onPause() {
cameraActivity = null;
}

private void stopPreview() {
private void stopPreview()
{
if (camera == null || !previewRunning) {
return;
}
Expand All @@ -201,51 +283,53 @@ private void stopPreview() {

/**
* Computes the optimal preview size given the target display size and aspect ratio.
*
* @param supportPreviewSizes a list of preview sizes the camera supports
* @param targetSize the target display size that will render the preview
* @param aspectRatio the aspect ratio to use for previewing the image
*
* @param supportPreviewSizes
* a list of preview sizes the camera supports
* @param targetSize
* the target display size that will render the preview
* @return the optimal size of the preview
*/
private static Size getOptimalPreviewSize(List<Size> supportedPreviewSizes, int width, int height, double aspectRatio) {
final double ASPECT_TOLERANCE = 0.001;
private static Size getOptimalPreviewSize(List<Size> sizes, int w, int h)
{
final double ASPECT_TOLERANCE = 0.01;
double targetRatio = (double) w / h;
if (sizes == null) {
return null;
}
Size optimalSize = null;
double minDiff = Double.MAX_VALUE;

int targetHeight = Math.min(height, width);
if (targetHeight <= 0) {
// We don't know the size of SurfaceView, use screen height
targetHeight = height;
}
int targetHeight = h;

for (Size size : supportedPreviewSizes) {
// Try to find an size match aspect ratio and size
for (Size size : sizes) {
double ratio = (double) size.width / size.height;
if (Math.abs(ratio - aspectRatio) > ASPECT_TOLERANCE) continue;

if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) {
continue;
}
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}

// If a size cannot be found that matches the aspect ratio, try
// again and just ignore the aspect ratio. We will just try to find
// the best size that fits best.
// Cannot find the one match the aspect ratio, ignore the requirement
if (optimalSize == null) {
Log.w(TAG, "No preview size found that matches the aspect ratio.", Log.DEBUG_MODE);
minDiff = Double.MAX_VALUE;
for (Size size : supportedPreviewSizes) {
for (Size size : sizes) {
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}

return optimalSize;
}

private static void onError(int code, String message) {
private static void onError(int code, String message)
{
if (errorCallback == null) {
Log.e(TAG, message);
return;
Expand All @@ -258,7 +342,8 @@ private static void onError(int code, String message) {
errorCallback.callAsync(callbackContext, dict);
}

private static void saveToPhotoGallery(byte[] data) {
private static void saveToPhotoGallery(byte[] data)
{
File imageFile = MediaModule.createGalleryImageFile();
try {
FileOutputStream imageOut = new FileOutputStream(imageFile);
Expand All @@ -283,8 +368,8 @@ private static void saveToPhotoGallery(byte[] data) {
static public void takePicture()
{
String focusMode = camera.getParameters().getFocusMode();
if (!(focusMode.equals(Parameters.FOCUS_MODE_EDOF) || focusMode.equals(Parameters.FOCUS_MODE_FIXED)
|| focusMode.equals(Parameters.FOCUS_MODE_INFINITY))) {
if (!(focusMode.equals(Parameters.FOCUS_MODE_EDOF) || focusMode.equals(Parameters.FOCUS_MODE_FIXED) || focusMode
.equals(Parameters.FOCUS_MODE_INFINITY))) {
AutoFocusCallback focusCallback = new AutoFocusCallback()
{
public void onAutoFocus(boolean success, Camera camera)
Expand All @@ -299,8 +384,10 @@ public void onAutoFocus(boolean success, Camera camera)
}
}

static PictureCallback jpegCallback = new PictureCallback() {
public void onPictureTaken(byte[] data, Camera camera) {
static PictureCallback jpegCallback = new PictureCallback()
{
public void onPictureTaken(byte[] data, Camera camera)
{
if (saveToPhotoGallery) {
saveToPhotoGallery(data);
}
Expand Down