Camera Picture Example

This example shows how to access the camera on a device and display its output in a view.

A screenshot of the application.

Note that, in order to access the camera, the application needs to be built with the android.hardware.camera feature and the android.permission.CAMERA and android.permission.WRITE_EXTERNAL_STORAGE permissions. The build script takes care of this.

We import the classes and modules needed by our application. The most relevant to this example are the Camera, SurfaceHolder and SurfaceView classes that we also used in the Camera example. In addition, we use the Environment and FileOutputStream

from java.io import File, FileOutputStream
from java.text import SimpleDateFormat
from java.util import Date
from android.app import Activity
from android.os import Bundle, Environment
from android.hardware import Camera
from android.view import SurfaceHolder, SurfaceView, View
from android.widget import Button, ImageView, LinearLayout

We define a class based on the standard Activity class. This represents the application, and will be used to present a graphical interface to the user.

class CameraPictureActivity(Activity):

    __interfaces__ = [SurfaceHolder.Callback, View.OnClickListener,
                      Camera.PictureCallback]

The class implements three interfaces that are declared in the list of interfaces defined by the __interfaces__ attribute. We implement these interfaces by defining methods later in the class.

We define a field that we can use as an instance attribute. This is needed because we cannot explicitly create an instance of the Camera class and the default value we want to assign to the attribute is the special None value, which corresponds to a null value in Java.

    __fields__ = {"camera": Camera}

The initialisation method calls the corresponding method in the base class. We also assign a value of None to the camera attribute, indicating that no camera is currently available.

    def __init__(self):
    
        Activity.__init__(self)
        self.camera = None

Similarly, the onCreate method calls the onCreate method of the base class to help set up the activity before creating a user interface.

    def onCreate(self, bundle):
    
        Activity.onCreate(self, bundle)

We create a button with a "Take picture" label and register the activity with it as a listener. Since the activity implements the View.OnClickListener interface, the activity's onClick method will be called when the user clicks the button.

        button = Button(self)
        button.setText("Take picture")
        button.setOnClickListener(self)

We use a SurfaceView to display a live preview from the camera. In order to receive updates from the camera, we register the activity to receive callbacks from the preview's SurfaceHolder object.

        preview = SurfaceView(self)
        self.holder = preview.getHolder()
        self.holder.addCallback(self)

We also create a layout to hold both the button and image preview. We make the layout the main view in the activity.

        layout = LinearLayout(self)
        layout.setOrientation(LinearLayout.VERTICAL)
        layout.addView(button)
        layout.addView(preview)
        
        self.setContentView(layout)

The onResume method is called when the activity starts or when the user navigates to it. After calling the corresponding method in the base class, we try to obtain a Camera object by calling the static open method. If a valid object is returned we obtain the first size supported by the device.

    def onResume(self):
    
        Activity.onResume(self)
        
        self.camera = Camera.open()
        parameters = self.camera.getParameters()
        self.size = parameters.getSupportedPreviewSizes().get(0)

The onPause method is called when the user navigates away from the activity. After calling the corresponding method in the base class, we release the Camera object obtained earlier, releasing it so that other activities can use it, and setting the camera attribute to None to indicate that the activity no longer has access to the camera.

    def onPause(self):
    
        Activity.onPause(self)
        
        if self.camera != None:
            self.camera.release()
            self.camera = None

The following three methods must be implemented because they are part of the SurfaceHolder.Callback interface whose methods are abstract.

The surfaceCreated method is used to inform us that a surface has been created to show images from the camera. If we have access to the camera, we tell it to use the SurfaceHolder associated with the SurfaceView object we created earlier.

    def surfaceCreated(self, holder):
    
        if self.camera != None:
            self.camera.setPreviewDisplay(self.holder)

The surfaceDestroyed method is used to inform the activity that a surface has been destroyed. If we have access to the camera, we stop the generation of preview images since the activity can no longer display them.

    def surfaceDestroyed(self, holder):
    
        if self.camera != None:
            self.camera.stopPreview()

The surfaceChanged method is used to inform the activity about changes to the format or size of the surface. We read the current parameters of the camera, update the preview size to match the size provided and start the camera preview.

    def surfaceChanged(self, holder, format, width, height):
    
        parameters = self.camera.getParameters()
        parameters.setPreviewSize(self.size.width, self.size.height)
        self.camera.setParameters(parameters)
        self.camera.startPreview()

The onClick method implements the View.OnClickListener interface and is called when the user clicks the button. If a camera is available then we call the Camera instance's takePicture method to request a picture from the device. This will cause the onPictureTaken method to be called later.

    def onClick(self, view):
    
        if self.camera == None:
            return
        
        self.camera.takePicture(None, None, self)

The onPictureTaken method implements the Camera.PictureCallback interface and is called when the camera delivers a picture to the activity. The data parameter contains the picture data and the camera parameter indicates which device the picture came from. We respond to the method call by saving the picture to a file.

    def onPictureTaken(self, data, camera):

If the Camera object passed to the method is not the one we are interested in then just return.

        if camera != self.camera:
            return

We call a custom method to create a file on the external storage device. If a file was created then we write the picture data to it.

        f = self.createFile()
        
        if f != None:
            stream = FileOutputStream(f)
            stream.write(data)
            
            # Closing the stream here causes an exception to be raised.

We start the preview again so that the camera is ready to take another picture. Not doing this will cause the activity to hang the next time the button is pressed.

        self.camera.startPreview()

The createFile method is a custom method that we use to create a file on the external storage device, returning a File object if successful or None if not.

    @args(File, [])
    def createFile(self):
    
        if Environment.getExternalStorageState() != Environment.MEDIA_MOUNTED:
            return None

If no external storage is mounted then return None immediately.

We obtain the directory on the external storage device that is used to store pictures.

        storageDir = Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_PICTURES)

If possible, we create a subdirectory for this example, returning None to indicate failure.

        subdir = File(storageDir, "CameraPicture")
        if not subdir.exists():
            if not subdir.mkdirs():
                return None

Finally, we use the SimpleDateFormat class to create a file name based on the current date, then use this to create a File object which we return.

        dateString = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
        return File(subdir, dateString + ".jpg")

Files