0

I wondered if anyone has tried using the standard Android Camera API (http://developer.android.com/guide/topics/media/camera.html) to control the camera in an app.

I was able to get my app to work using the startActivityForResult() method, but was a bit disappointed in how long it took to get an image back from the file system, so I wanted to try the other route, using the CameraPreview.

I set up some code based on the Android Camera API example, and it builds and runs fine, but after surfaceChanged executes I get a message in the screen "The Camera has stopped."

Later Edit - I moved the camera activity into its own project and now it works but it gets the preview scrambled. But after the pic is taken it shows fine. Here is the layout code:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
  <FrameLayout
    android:id="@+id/camera_preview"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_weight="1"
    />
</LinearLayout>

And here is the Activity source:

package com.example.cameratest;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

import com.google.android.glass.touchpad.Gesture;
import com.google.android.glass.touchpad.GestureDetector;

import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore.Files.FileColumns;
import android.app.Activity;
import android.content.Context;
import android.hardware.Camera;
import android.hardware.Camera.PictureCallback;
import android.util.Log;
import android.view.Menu;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.WindowManager;
import android.widget.FrameLayout;

public class MainActivity extends Activity {

private static final String TAG = MainActivity.class.getSimpleName();

private GestureDetector mGestureDetector;

private Camera mCamera;
private CameraPreview mPreview;

private static boolean gotCamera = false;

private PictureCallback mPicture = new PictureCallback() {
    @Override
    public void onPictureTaken(byte[] data, Camera camera) {
        File pictureFile = getOutputMediaFile(FileColumns.MEDIA_TYPE_IMAGE);
        if (pictureFile == null){
            Log.d(TAG, "Error creating media file, check storage permissions");
            return;
        }
        try {
            FileOutputStream fos = new FileOutputStream(pictureFile);
            fos.write(data);
            fos.close();
        } catch (FileNotFoundException e) {
            Log.d(TAG, "File not found: " + e.getMessage());
        } catch (IOException e) {
            Log.d(TAG, "Error accessing file: " + e.getMessage());
        }
    }
};

/** Create a file Uri for saving an image or video */
private static Uri getOutputMediaFileUri(int type){
    return Uri.fromFile(getOutputMediaFile(type));
}

/** Create a File for saving an image or video */
private static File getOutputMediaFile(int type){
    // To be safe, you should check that the SDCard is mounted
    // using Environment.getExternalStorageState() before doing this.

    File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
              Environment.DIRECTORY_PICTURES), "MyCameraApp");
    // This location works best if you want the created images to be shared
    // between applications and persist after your app has been uninstalled.

    // Create the storage directory if it does not exist
    if (! mediaStorageDir.exists()){
        if (! mediaStorageDir.mkdirs()){
            Log.d("MyCameraApp", "failed to create directory");
            return null;
        }
    }

    // Create a media file name
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
    File mediaFile;
    if (type == FileColumns.MEDIA_TYPE_IMAGE){
        mediaFile = new File(mediaStorageDir.getPath() + File.separator +
                "IMG_"+ timeStamp + ".jpg");
    } else if(type == FileColumns.MEDIA_TYPE_VIDEO) {
        mediaFile = new File(mediaStorageDir.getPath() + File.separator +
                "VID_"+ timeStamp + ".mp4");
    } else {
        return null;
    }

    return mediaFile;
}

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

    getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

    mGestureDetector = createGestureDetector(this);

    setContentView(R.layout.activity_main);

    // Create an instance of Camera
    mCamera = getCameraInstance();

    if (gotCamera) {
        // Create our Preview view and set it as the content of our activity.
        Log.v(TAG,"We got the camera");
        mPreview = new CameraPreview(this, mCamera);
        FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
        preview.addView(mPreview);
    }
    else {
        finish();
    }


}

private static Camera getCameraInstance(){
    gotCamera = false;
    Camera c = null;
    try {
        c = Camera.open(); // attempt to get a Camera instance
        gotCamera = true;
    }
    catch (Exception e){
        // Camera is not available (in use or does not exist)
        Log.v(TAG,"Camera is not available!");
    }
    return c; // returns null if camera is unavailable
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
}

/*
 * Send generic motion events to the gesture detector
 */
@Override
public boolean onGenericMotionEvent(MotionEvent event) {
    if (mGestureDetector != null) {
        return mGestureDetector.onMotionEvent(event);
    }
    return false;
}

private GestureDetector createGestureDetector(Context context) {
    GestureDetector gestureDetector = new GestureDetector(context);
        //Create a base listener for generic gestures
        gestureDetector.setBaseListener( new GestureDetector.BaseListener() {
            @Override
            public boolean onGesture(Gesture gesture) {
                if (gesture == Gesture.TAP) {
                    // do something on tap
                    Log.v(TAG,"tap");

                    // take a picture
                    takePic();

                    return true;
                } else if (gesture == Gesture.TWO_TAP) {
                    // do something on two finger tap
                    return true;
                } else if (gesture == Gesture.SWIPE_RIGHT) {
                    // do something on right (forward) swipe
                    return true;
                } else if (gesture == Gesture.SWIPE_LEFT) {
                    // do something on left (backwards) swipe
                    return true;
                }
                return false;
            }
        });
        gestureDetector.setFingerListener(new GestureDetector.FingerListener() {
            @Override
            public void onFingerCountChanged(int previousCount, int currentCount) {
              // do something on finger count changes
            }
        });
        gestureDetector.setScrollListener(new GestureDetector.ScrollListener() {
            @Override
            public boolean onScroll(float displacement, float delta, float velocity) {
                // do something on scrolling
                return false;
            }
        });
        return gestureDetector;
    }

private class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
    private SurfaceHolder mHolder;
    private Camera mCamera;

    public CameraPreview(Context context, Camera camera) {
        super(context);
        mCamera = camera;

        Log.v(TAG,"In CameraPreview");

        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
        mHolder = getHolder();

        Log.v(TAG,"Got holder");

        mHolder.addCallback(this);

        Log.v(TAG,"Added callback");

        // deprecated setting, but required on Android versions prior to 3.0
        //mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    public void surfaceCreated(SurfaceHolder holder) {
        // The Surface has been created, now tell the camera where to draw the preview.
        try {
            Log.v(TAG,"in surface created");
            mCamera.setPreviewDisplay(holder);
            Log.v(TAG,"set preview display");
            mCamera.startPreview();
            Log.v(TAG,"preview started");
        } catch (IOException e) {
            Log.d(TAG, "Error setting camera preview: " + e.getMessage());
        }
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        // empty. Take care of releasing the Camera preview in your activity.
        mCamera.stopPreview();
        mCamera.release();
        mCamera = null;
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        // If your preview can change or rotate, take care of those events here.
        // Make sure to stop the preview before resizing or reformatting it.

        Log.v(TAG,"in surface changted");

        if (mHolder.getSurface() == null){
          // preview surface does not exist
            Log.v(TAG,"surface don't exist");
          return;
        }

        // stop preview before making changes
        try {
            mCamera.stopPreview();
            Log.v(TAG,"stopped preview");
        } catch (Exception e){
          // ignore: tried to stop a non-existent preview
            Log.v(TAG,"preview e");
        }

        // start preview with new settings
        try {
            Log.v(TAG,"startpreview");
            mCamera.setPreviewDisplay(mHolder);
            mCamera.startPreview();

        } catch (Exception e){
            Log.d(TAG, "Error starting camera preview: " + e.getMessage());
        }
    }
}

public void takePic() {
    // get an image from the camera
    mCamera.takePicture(null, null, mPicture);
}

}
Darren
  • 1,417
  • 13
  • 23

1 Answers1

3

The code you have for surfaceChanged looks fine, similar to what I have in my working Glass app. Have you checked the camera parameters passed to Camera.setParameters, such as preview size and focus mode? If you can post the full activity source in the question that would help finding the issue.

Satish
  • 121
  • 3
  • Thanks. No I haven't set the parameters. That is probably what it is. Actually I just tried pulling the camera portion alone into its own project and now it works but the image in the preview screen is scrambled, like it can't get the correct frame rate or something. I'm editing the post with the complete source for the Activity and layout. – Darren Nov 27 '13 at 13:19
  • For the preview appearing scrambled, its a bug since XE10. See http://stackoverflow.com/questions/19235477/google-glass-preview-image-scrambled-with-new-xe10-release/19257078#19257078 for a workaround. – Satish Nov 27 '13 at 15:12