Capturing an image from POIDetailActivity

We are now ready to take on the task of capturing a photo. This will involve the following tasks:

  • Adding new user interface widgets to initiate capturing a photo and display it
  • Building a photo intent to navigate to an external camera app to capture a photo
  • Processing the results of the photo intent and displaying a photo if one was successfully captured

The following sections describe the details of each step.

Adding UI elements

There are a few new UI elements we will need to add to support capturing an image; we need an ImageButton element to initiate the process of capturing an image, and we also need an ImageView element to display the image. We will add the new ImageButton element at the bottom of the View next to the location and map buttons. We will add the ImageView element just below the Latitude and Longitude fields and just above the buttons at the bottom. The following list shows the definition for the ImageView, which should be placed just below the TableLayout used for the Latitude and Longitude widgets:

    ...
    </TableRow>
    </TableLayout>
    <ImageView
      p1:src="@android:drawable/ic_menu_gallery"
      p1:layout_width="wrap_content"
      p1:layout_height="wrap_content"
      p1:padding="10dp"
      p1:id="@+id/poiImageView"
      p1:layout_gravity="center_horizontal"
      p1:scaleType="fitCenter" />
    <LinearLayout
    ...

Create a private reference object in POIDetailActivity and assign the reference in OnCreate():

ImageView _poiImageView;
...
_poiImageView = FindViewById<ImageView> (Resource.Id.poiImageView);

Now, we need a button. Start by copying the ic_new_picture.png icon from the assets folder to the project's drawable folder and adding it to the project in the same manner as we did in the previous chapters. Add the following button definition to the LinearLayout that contains the other buttons:

    <ImageButton
      p1:src="@drawable/ic_new_picture"
      p1:layout_width="wrap_content"
      p1:layout_height="wrap_content"
      p1:id="@+id/photoImageButton" />

Create a private reference object in POIDetailActivity and assign the reference in OnCreate() as follows:

ImageButton _photoImageButton;
...
_photoImageButton =FindViewById<ImageButton> (Resource.Id.photoImageButton);

Creating the intent

To start an external camera app to capture a photo, we rely on the Intent class again, this time combined with an action. The following listing depicts creating an Intent with the image capture action:

Intent cameraIntent = new Intent(MediaStore.ActionImageCapture);

The MediaStore.ActionImageCapture action tells the Android platform you want to capture a photo and are willing to use any existing app that provides those capabilities.

Checking for registered camera apps

In Chapter 7, Making POIApp Location Aware, we used PackageManager to check to see if a map app was present to handle our intent. We now need to perform the same check for an app that can handle our ActionImageCapture intent. The following listing shows the logic we need:

PackageManager packageManager = PackageManager;
IList<ResolveInfo> activities =packageManager.QueryIntentActivities(cameraIntent, 0);
if (activities.Count == 0) {
  //display alert indicating there are no camera apps
}
else {
  //launch the cameraIntent
}

Providing additional information with the intent

Prior to starting the intent, we need to provide some information to the camera app that processes our request; specifically, a filename and location, and the maximum size of the resulting photo. We do this by adding Extras to the intent. The MediaStore class defines a number of standard Extras that can be added to an intent to control how an external app fulfils the intent.

Providing a filename and location

The MediaStore.ExtraOutput extra can be added to control the filename and location the external app should use in order to capture an image. We previously enhanced the data service to provide this information. Unfortunately, we will need to convert the string path we get from the data service to an instance of Android.Net.Uri, which is the expected format for camera apps that consume MediaStore.ExtraOutput.

This is a two-step process. First, we create a Java.IO.File object using the string path from the data service and then create an Android.Net.Uri object. The following listing shows how to accomplish the construction of the URI and set up the MediaStore.ExtraOutput extra:

Java.IO.File imageFile = new Java.IO.File(POIData.Service.GetImageFilename(_poi.Id.Value));

Android.Net.Uri imageUri = Android.Net.Uri.FromFile (imageFile);

cameraIntent.PutExtra (MediaStore.ExtraOutput, imageUri);

Providing size limit

The MediaStore.ExtraSizeLimit extra limits the image size. It is much more straightforward to set up as follows:

cameraIntent.PutExtra (MediaStore.ExtraSizeLimit, 1.5 * 1024);

Starting the intent

We are now ready to start the intent. In other cases where we used the Intent class, we were not looking for any information to be provided as a result. In this case, we are expecting the photo app to provide either a photo or a notification that the user cancelled the photo. You accomplish this by using StartActivityForResult() by passing in the intent. The StartActivityForResults() method works in conjunction with a callback to OnActivityResult(), to communicate the results of the intent. The following listing depicts the calling of StartActivityForResult():

const int CAPTURE_PHOTO = 0;
. . .
StartActivityForResult(cameraIntent, CAPTURE_PHOTO);

Notice the second parameter to StartActivityForResult(). It is an int value named requestCode that will be returned as a parameter in the callback to OnActivityResult() and help identify the original reason for launching an intent. The best practice is to define a constant value to pass in for each requestCode that can potentially cause OnActivityResult() to be called.

Completing the NewPhotoClicked() method

We have covered a number of topics related to starting the camera app in a somewhat fragmented fashion. The following listing is the complete implementation for NewPhotoClicked():

public void NewPhotoClicked(object sender, EventArgs e)
{
if (!_poi.Id.HasValue) {
  AlertDialog.Builder alertConfirm=new AlertDialog.Builder(this);
  alertConfirm.SetCancelable(false);
  alertConfirm.SetPositiveButton("OK", delegate {});
  alertConfirm.SetMessage(
    "You must save the POI prior to attaching a photo");
  alertConfirm.Show ();
  }
  else {
    Intent cameraIntent = new Intent (MediaStore.ActionImageCapture);
    PackageManager packageManager = PackageManager;
    IList<ResolveInfo> activities = packageManager.QueryIntentActivities(cameraIntent, 0);
    if (activities.Count == 0) {
      AlertDialog.Builder alertConfirm = newAlertDialog.Builder(this);
      alertConfirm.SetCancelable(false);
      alertConfirm.SetPositiveButton("OK", delegate {});
      alertConfirm.SetMessage(
        "No camera app available to capture photos.");
      alertConfirm.Show ();
    }
    else {
      Java.IO.File imageFile = new Java.IO.File(POIData.Service.GetImageFilename(_poi.Id.Value));

      Android.Net.Uri imageUri = Android.Net.Uri.FromFile (imageFile);

      cameraIntent.PutExtra (MediaStore.ExtraOutput, imageUri);
      cameraIntent.PutExtra (MediaStore.ExtraSizeLimit,1.5 * 1024);

      StartActivityForResult (cameraIntent, CAPTURE_PHOTO);
    }
  }
}

Processing the results of the intent

The initiating activity is notified of the results of an intent via the OnActivityResult() callback method. The following listing shows the signature for the OnActivityResult() method:

OnActivityResult (int requestCode, Result resultCode, Intent data)

We discussed requestCode in the previous section. The resultCode parameter indicates the result of the intent that was launched and is of type Result, which can have the following values:

Value

Meaning

RESULT_OK

The activity completed the request successfully.

REQUEST_CANCELED

The activity was cancelled, generally by a user action.

REQUEST_FIRST_USER

The first value that can be used for a custom meaning.

The third parameter, data, is of type Intent and can be used to pass additional information back from the activity that was launched. In our case, we are only concerned with requestCode and resultCode. The following listing shows the implementation of OnActivityResult() in POIDetailActivity:

protected override void OnActivityResult (int requestCode,Result resultCode, Intent data)
{
  if (requestCode == CAPTURE_PHOTO) {
    if (resultCode == RESULT_OK) {
    // display saved image
    Bitmap poiImage = POIData.GetImageFile (_poi.Id.Value);
    _poiImageView.SetImageBitmap (poiImage);
    if (poiImage != null)
      poiImage.Dispose ();
    }
    else {
      // let the user know the photo was cancelled
      Toast toast = Toast.MakeText (this, "No picture captured.",ToastLength.Short);
      toast.Show();
    }
  }
  else
    base.OnActivityResult (requestCode, resultCode, data);
}

Notice that when resultCode is RESULT_OK, we load the photo that was captured into a Bitmap object and then set the image for _poiImageView. This causes the image to be displayed at the bottom of the POIDetail layout. If resultCode is not RESULT_OK, we display a toast message to the user indicating that the action was cancelled.

You will also notice the magic method GetImageFile() on POIData that just showed up from nowhere. It is actually not magic; we need to add it. The GetImageFile() method is a simple utility method that accepts a POI ID and loads Android.Graphics.Bitmap using the Android utility class BitmapFactory. The following listing shows the GetImageFile() method:

public static Bitmap GetImageFile(int poiId)
{
  string filename = Service.GetImageFilename (poiId);
  if (File.Exists (filename)) {
    Java.IO.File imageFile = new Java.IO.File (filename);
    return BitmapFactory.DecodeFile (imageFile.Path);
  }
  else
    return null;
}

We could have simply embedded this code in OnActivityResult(), but we will need the same functionality in a few more places. We could have also chosen to add the method to POIJsonService, but that would have required us to introduce specific Android types to the data service, which would have limited its reuse in other platforms.

We have added a lot of code. Run POIApp and test adding a photo.

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

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