Using the camera

Making use of the camera allows the user to interact with the real world through the device. This can range from taking a photo and adding a message and effects all the way to providing an augmented reality experience.

How to do it...

If our app requires a photo, we can make use of the camera to record an image. However, we need to first ensure that there is a camera available on the device for us to use:

  1. If our app requires the camera and cannot function without it, we can enforce this by specifying that there must be a camera on the device in the features:
    [assembly: UsesFeature(PackageManager.FeatureCamera)]
    [assembly: UsesFeature(PackageManager.FeatureCameraFront)]
  2. If the feature is not required, we can indicate that it is optional by adding the Required property to the [UsesFeature] attribute:
    [assembly: UsesFeature(..., Required = false)]
  3. Then, we can check when the app starts if the feature is available and adjust functionality accordingly:
    if (!PackageManager.HasSystemFeature(
    Android.Content.PM.PackageManager.FeatureCamera) {
      // we don't have a rear camera
    }
  4. Finally, as we are going to rely on the existence of a camera app, we need to check whether it exists:
    var activities = PackageManager.QueryIntentActivities(
      new Intent(MediaStore.ActionImageCapture),
      PackageInfoFlags.MatchDefaultOnly);
    if (activities.Count == 0) {
      // handle no camera apps
    }

Once we know there is a camera app on the device, we need a place for the photos to be stored by the camera:

  1. As we can't ask another app to store photos into our app sandbox, we have to use a shared location, such as the external storage photos directory:
    var root = Environment.GetExternalStoragePublicDirectory(
      Environment.DirectoryPictures);
  2. We can then get the location of the file that we will use to store the photo:
    root = new Java.IO.File(root, "XamarinCookbook");
    if (!root.Exists()) {
      root.Mkdirs();
    }
    var imageFile = new Java.IO.File(root, "image.jpg");
  3. As we need to use a Uri instance to specify the location, we convert the File into a Uri instance using the FromFile() method:
    var imageUri = Android.Net.Uri.FromFile(imageFile);

Now that we know we have a camera app and a place to store the photo, we can launch the camera:

  1. First, we will start the camera using the MediaStore.ActionImageCapture intent action:
    const int TakePhotoCode = 1;
    var intent = new Intent(MediaStore.ActionImageCapture);
    intent.PutExtra(MediaStore.ExtraOutput, imageUri);
    StartActivityForResult(intent, TakePhotoCode);
  2. We will then override the OnActivityResult() method of the activity to handle the response:
    if (requestCode == TakePhotoCode) {
      if (resultCode == Result.Ok) {
        // display the picture found at imageUri
      }
      else {
        // photo intent was canceled
      }
    }

Recording videos using an intent is just as easy; all we need is a different intent action:

  1. To start the camera app to record video, we use the MediaStore.ActionVideoCapture intent action:
    var intent = new Intent (MediaStore.ActionVideoCapture);
    intent.PutExtra(MediaStore.ExtraOutput, videoUri);
    StartActivityForResult (intent, TakeVideoCode); 
  2. Then, in OnActivityResult(), we can read the video file as we would for an image:
    if (requestCode == TakeVideoCode) {
      if (resultCode == Result.Ok) {
        // display the video found at videoUri
      }
      else {
        // video capture intent was canceled
      }
    }

How it works...

The camera is a very powerful piece of hardware on a user's device. It can capture a moment that was powerful enough to compel that person to take a photo in the first place. Adding the ability to capture, share, store, or react to a moment of the user's life can make our app even more personal.

We don't have to go on to create a brand new camera app; instead, we can just use the existing camera app and ask it to take a photo for us. To do this, we use the normal way of communicating between activities using an Intent instance with the StartActivityForResult() and OnActivityResult() methods.

Tip

Not everything has to be recreated, making use of other apps installed on the device allows the user to install preferred applications to do a specific task.

Before trying to access the camera app, we have to ensure that there is a camera on the device and that there is a camera app installed. Usually, if there is a camera, the device manufacturer will have made sure there is a camera app installed. But, the user might have uninstalled it for some reason.

The first check is to ensure that there is an actual camera on the device. To prevent our app from appearing in Google Play if the user is using a device without a camera, we can make use of the feature filtering support of Google Play. We use the [UsesFeature] attribute to indicate what feature the app requires, and in the case of a camera, we can specify FeatureCamera and/or FeatureCameraFront to indicate that we require a camera.

Tip

Before using some hardware, it should be verified that the device includes it.

If the app does not require a camera, but rather it is optional, we can specify this using the Required property on the [UsesFeature] attribute. If we set this to false, we will have to check at runtime for a camera. We can check for features at runtime using the HasSystemFeature() method of the PackageManager class. We obtain the current package manager from the PackageManager property of an Activity or Service. Similar to the attribute, we use FeatureCamera and/or FeatureCameraFront as a parameter when checking for the existence of a camera.

Tip

If the camera is only accessed using an Intent instance, then the app does not require the camera permissions.

Once we know there is a camera on the device, we can check whether there are any camera apps on the device. To do this, we query the PackageManager class for a list activities that support the camera intent. To launch a camera app, we create an Intent instance with the ActionImageCapture action. We then pass this intent to the QueryIntentActivities() method of the package manager, which will return a list of all the activities that can take a photo. If there are any items in the list, we know that there is at least one camera app.

Tip

Before starting an activity with an intent, it should be verified that there is an app on the device that can handle it.

Now that we know the device has a camera and at least one camera app, we can ask one of these activities to take a photo. This requires setting up an intent that will start an activity. The only thing we need is to specify where the camera app should place the file for us to read. As the camera app cannot access our app's private content and we cannot access the camera's private content, we have to specify a shared location.

Usually, we can use the result of GetExternalStoragePublicDirectory, with the DirectoryPictures parameter. This will provide us with a location to the public location of all photos. We then pass a Uri instance of that location as an extra on the intent, with a key of ExtraOutput and the value being the Uri.

Note

To start the camera app to take a photo, the ActionImageCapture intent action is used; for videos, the ActionVideoCapture intent action is used.

To start the camera app, we pass the Intent instance, along with a code to identify the action, to the StartActivityForResult() method. The camera app will launch and allow the user to take a photo. Once the user takes a photo, the camera will close and take us back to our app. We are informed about this through the OnActivityResult() method, which we override.

In the OnActivityResult() method, we can use the request code to identify the camera action, which will be the value passed to the StartActivityForResult() method. Also, to determine whether the user took a photo or canceled the camera, we use the result code. If the user took a photo, the result code will be Ok and we can then handle reading the image. The image will be located at the same location that we specified when creating the Intent instance.

Recording a video is exactly the same, but instead we specify a different intent action. In the case of a video, we use the ActionVideoCapture intent action. We then set the location extra and pass the intent to the StartActivtyForResult() method. In the OnActivityResult() method, we can read the video file if the result is Ok.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset