Advertisement
Android SDK

Android Essentials: Create a Mirror

by

Learn how to make a simple "mirror" app in this quick tutorial!

One of the unexpected uses of cell phones, with their shiny screens, is that people began to use them as mirrors—to check hair, lipstick, or just to ensure no broccoli has become stuck between the teeth. When camera phones arrived, people immediately turned the lens on themselves in self portraits, even if it meant contorting in odd ways to get the shot. Hardware manufacturers capitalized on this by creating a new generation of smartphones with two cameras: one facing out, and one facing in—the front-facing camera. This hardware lends itself to all kinds of practical applications.

Since API Level 9, the Android SDK has provided a method for access multiple cameras on devices. And since then, most manufacturers have included a front facing camera. This camera is usually used for taking self portraits or for video chatting purposes.


Step 0: Getting Started

Open source code for this project can be downloaded and used locally or browsed online. Alternately, you can create your own project. This is a single activity project, so you could add the activity to your own project, too. We’ll assume you have a project ready to work with.


Step 1: Camera Layout

A typical way of presenting the camera is by creating a layout – any you wish – that has a FrameLayout control to hold the camera preview. There's usually some sort of button to take a picture, too. With that in mind, here's the layout we've used for portrait mode:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    <FrameLayout
        android:id="@+id/camPreview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true" >
    </FrameLayout>
    <Button
        android:id="@+id/capture"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:text="@string/capture" />
</RelativeLayout>

The landscape layout is similar, but the button is located along the side.


Step 2: Starting the Camera

Device cameras are accessed using the Camera class (android.hardware.Camera) of the Android SDK. A reasonable place to configure and start the camera is in the onCreate() method of your Activity class, like so:

private Camera mCam;
private MirrorView mCamPreview;
private int mCameraId = 0;
private FrameLayout mPreviewLayout;

@Override
public void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.main);

mCameraId = findFirstFrontFacingCamera();

	mPreviewLayout = (FrameLayout) findViewById(R.id.camPreview);
	mPreviewLayout.removeAllViews();

	startCameraInLayout(mPreviewLayout, mCameraId);

}

First, we determine which camera is the front camera. Since the API provides for the possibility of more than one front camera, we use the first one. Next, the FrameLayout is loaded and cleared. We only want the camera preview in it. Then, finally, we start the camera in this layout.

Now let's look at each of these helper methods.


Step 3: Finding a Front Camera

Use the getNumberOfCameras() method of the Camera object to iterate over each camera instance and retrieve a CameraInfo object. Use the facing field to determine if the camera is a CAMERA_FACING_FRONT. If so, return this camera id –we have found a front facing camera to use.

private int findFirstFrontFacingCamera() {
    int foundId = -1;
    int numCams = Camera.getNumberOfCameras();
    for (int camId = 0; camId < numCams; camId++) {
        CameraInfo info = new CameraInfo();
        Camera.getCameraInfo(camId, info);
        if (info.facing == CameraInfo.CAMERA_FACING_FRONT) {
            foundId = camId;
            break;
        }
    }
    return foundId;
}

Step 4: Starting the Front Camera

Next, retrieve an instance of the Camera object with a call to the Camera.open() method using the camera identifier for the front-facing camera. Instantiate a MirrorView object using the open camera and add the MirrorView object to the layout.

private void startCameraInLayout(FrameLayout layout, int cameraId) {
    mCam = Camera.open(cameraId);
    if (mCam != null) {
        mCamPreview = new MirrorView(this, mCam);
        layout.addView(mCamPreview);
    }
}

Now let's take a closer look at the MirrorView class.


Step 5: Creating the MirrorView

In order to place the preview on the screen to display what the camera is capturing, we must make a new SurfaceView such that we can assign its holder (SurfaceHolder) to the camera hardware. This allows the camera hardware to write to the surface directly.

Here's the implementation of our MirrorView:

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

    public MirrorView(Context context, Camera camera) {
        super(context);
        mCamera = camera;
        mHolder = getHolder();
        mHolder.addCallback(this);
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    public void surfaceCreated(SurfaceHolder holder) {
        try {
            mCamera.setPreviewDisplay(holder);
            mCamera.startPreview();
        } catch (Exception error) {
            Log.d(DEBUG_TAG,
                    "Error starting mPreviewLayout: " + error.getMessage());
        }
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w,
            int h) {
        if (mHolder.getSurface() == null) {
            return;
        }

        // can't make changes while mPreviewLayout is active
        try {
            mCamera.stopPreview();
        } catch (Exception e) {

        }

        try {

            // start up the mPreviewLayout
            mCamera.setPreviewDisplay(mHolder);
            mCamera.startPreview();

        } catch (Exception error) {
            Log.d(DEBUG_TAG,
                    "Error starting mPreviewLayout: " + error.getMessage());
        }
    }
}

When the surface is created, in the call onSurfaceCreated(), the camera SurfaceHolder is set and the preview is started with a call to the startPreview() method. When the surface is changed, in the call onSurfaceChanged(), the camera is stopped and restarted with the updated holder. For the complete implementation of this solution, with declarations for DEBUG_TAG and such, see the full source code.

If this is all you do, the application will work. However, the results won't be ideal. Likely, the preview will appear sideways and the aspect ratio won't be correct, making your face look stretched or squashed. Not good!


Step 6: Adjusting for Camera Orientation and Aspect Ratio

The orientation of the camera preview must match the orientation of the device screen for everything to appear correctly. Additionally, the aspect ratio of the camera preview should match the preview size, otherwise the image will appear warped. All of this data is readily available from the Display, CameraInfo, and Camera.Parameters classes. Here we've written a simple method that adjusts the display characteristics of the SurfaceHolder to fix these issues:

public void setCameraDisplayOrientationAndSize() {
    CameraInfo info = new CameraInfo();
    Camera.getCameraInfo(mCameraId, info);
    int rotation = getWindowManager().getDefaultDisplay().getRotation();
    int degrees = rotation * 90;

    int result;
    if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
        result = (info.orientation + degrees) % 360;
        result = (360 - result) % 360;
    } else {
        result = (info.orientation - degrees + 360) % 360;
    }
    mCamera.setDisplayOrientation(result);

    Camera.Size previewSize = mCam.getParameters().getPreviewSize();
    if (result == 90 || result == 270) {
        mHolder.setFixedSize(previewSize.height, previewSize.width);
    } else {
        mHolder.setFixedSize(previewSize.width, previewSize.height);

    }
}

At this point, the mirror should be fully functional.

Mirror in action

Step 7: Cleaning Up

The Camera object must be released as soon as your app is done with it. In the case of the sample Activity, we can release the camera in the onPause() method and start it in onResume(), like this:

@Override
protected void onResume() {
    super.onResume();
    if (mCam == null && mPreviewLayout != null) {
        mPreviewLayout.removeAllViews();
        startCameraInLayout(mPreviewLayout, mCameraId);
    }
}

@Override
protected void onPause() {
    if (mCam != null) {
        mCam.release();
        mCam = null;
    }
    super.onPause();
}

Conclusion

Never underestimate how much users love their front-facing cameras. Many of the latest generation smartphones have this feature, but few applications take advantage of it—yours should! You've learned how to identify and use front cameras in this tutorial. Additionally, you've learned how to adjust for the screen orientation and aspect ratio of the preview section.

Can you save the picture now? (Hint: You did download the open source code, right?)


About the Authors

Mobile developers Lauren Darcey and Shane Conder have coauthored several books on Android development: an in-depth programming book entitled Android Wireless Application Development and Sams Teach Yourself Android Application Development in 24 Hours. When not writing, they spend their time developing mobile software at their company and providing consulting services. They can be reached at via email to androidwirelessdev+mt@gmail.com, via their blog at androidbook.blogspot.com, and on Twitter @androidwireless.

Need More Help Writing Android Apps? Check out our Latest Books and Resources!

Buy Android Wireless Application Development, 2nd Edition  Buy Sam's Teach Yourself Android Application Development in 24 Hours  Mamlambo code at Code Canyon

Related Posts