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.
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:
[assembly: UsesFeature(PackageManager.FeatureCamera)] [assembly: UsesFeature(PackageManager.FeatureCameraFront)]
Required
property to the [UsesFeature]
attribute:[assembly: UsesFeature(..., Required = false)]
if (!PackageManager.HasSystemFeature( Android.Content.PM.PackageManager.FeatureCamera) { // we don't have a rear camera }
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:
var root = Environment.GetExternalStoragePublicDirectory( Environment.DirectoryPictures);
root = new Java.IO.File(root, "XamarinCookbook"); if (!root.Exists()) { root.Mkdirs(); } var imageFile = new Java.IO.File(root, "image.jpg");
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:
MediaStore.ActionImageCapture
intent action:const int TakePhotoCode = 1; var intent = new Intent(MediaStore.ActionImageCapture); intent.PutExtra(MediaStore.ExtraOutput, imageUri); StartActivityForResult(intent, TakePhotoCode);
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:
MediaStore.ActionVideoCapture
intent action:var intent = new Intent (MediaStore.ActionVideoCapture); intent.PutExtra(MediaStore.ExtraOutput, videoUri); StartActivityForResult (intent, TakeVideoCode);
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 } }
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.
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.
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.
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.
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
.
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
.