# Camera and Photo Gallery APIs
# Objective
In this chapter, you'll learn how to capture an image from the camera, use it in an app, and save it to the gallery or filesystem. You'll also explore the closely related gallery APIs, which let your users grab images from their photo albums for use within your app.
# Contents
The APIs that you use to access the camera and photo gallery are very similar. Both use the same function signature, though the actual method names vary. We'll start with a look at the camera API then see how you apply the same techniques to the gallery.
# Permissions
Firstly, it is important to have the required permissions to take photos and capture video.
# Android permissions
On Android you must define the required camera permissions in your tiapp.xml:
Android Camera Permissions
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
Additionally, to capture audio; you must also define the required audio permissions in your tiapp.xml:
Android Audio Permission
<uses-permission android:name="android.permission.RECORD_AUDIO" />
# Requesting Permissions
For all platforms you must verify you have the correct permissions to capture media and request them from the user if they are not available. You can do this by using the provided Titanium.Media APIs:
Requesting Permissions
// check if we already have permissions to capture media
if (!Ti.Media.hasCameraPermissions()) {
// request permissions to capture media
Ti.Media.requestCameraPermissions(function (e) {
// success! we can capture media!
if (e.success) {
Ti.Media.showCamera({ ... });
// oops! could not obtain required permissions...
} else {
Ti.API.error('could not obtain camera permissions!');
}
});
} else {
// yay! we already have permissions!
Ti.Media.showCamera({ ... });
}
# Camera
You can access both the rear-facing and front-facing cameras in devices that have them. You can capture still images and video from the camera. Then you can use the resulting images in your app, save them to the file system, upload them, or save them to the gallery. You open the camera by calling the showCamera()
(opens new window) method. When doing so, you define three callback functions that are called for the success, cancel, and error events, as shown here:
var win = Ti.UI.createWindow({
layout: 'vertical',
backgroundColor: 'gray'
}),
photoBtn = Ti.UI.createButton({
title: 'TAKE PHOTO'
}),
videoBtn = Ti.UI.createButton({
title: 'RECORD VIDEO'
});
/**
* showCamera: handle required permissions and display camera for video capture
* and photo capture
*
* @param type: capture type, can be Ti.Media.MEDIA_TYPE_VIDEO or Ti.Media.MEDIA_TYPE_PHOTO
* @param callback: callback from camera
* @param error: defined when an error has occurred, otherwise null
* @param result: result from the camera containing captured media information
*/
function showCamera (type, callback) {
var camera = function () {
// call Titanium.Media.showCamera and respond callbacks
Ti.Media.showCamera({
success: function (e) {
callback(null, e);
},
cancel: function (e) {
callback(e, null);
},
error: function (e) {
callback(e, null);
},
saveToPhotoGallery: true, // save our media to the gallery
mediaTypes: [ type ]
});
};
// check if we already have permissions to capture media
if (!Ti.Media.hasCameraPermissions()) {
// request permissions to capture media
Ti.Media.requestCameraPermissions(function (e) {
// success! display the camera
if (e.success) {
camera();
// oops! could not obtain required permissions
} else {
callback(new Error('could not obtain camera permissions!'), null);
}
});
} else {
camera();
}
}
photoBtn.addEventListener('click', function () {
// attempt to take a photo with the camera
showCamera(Ti.Media.MEDIA_TYPE_PHOTO, function (error, result) {
if (error) {
alert('could not take photo');
return;
}
// validate we taken a photo
if (result.mediaType == Ti.Media.MEDIA_TYPE_PHOTO) {
// create an imageView to display our photo
var imageView = Ti.UI.createImageView({
image: result.media
});
// add the imageView to the window
win.add(imageView);
}
});
});
videoBtn.addEventListener('click', function () {
// attempt to capture video with the camera
showCamera(Ti.Media.MEDIA_TYPE_VIDEO, function (error, result) {
if (error) {
alert('could not capture video');
return;
}
// validate we taken a video
if (result.mediaType == Ti.Media.MEDIA_TYPE_VIDEO) {
// create a videoPlayer to display our video
var videoPlayer = Ti.Media.createVideoPlayer({
url: result.media.nativePath,
autoplay: true
});
// add the videoPlayer to the window
win.add(videoPlayer);
}
});
});
win.add([ photoBtn, videoBtn ]);
win.open();
The saveToPhotoGallery
property controls whether the photo or video is automatically saved to the device's photo gallery. Using Ti.Filesystem
you could save to the filesystem in addition to or in place of saving to the gallery. Because the user can delete items from the gallery separately from your app, you should save media to the filesystem if your app will depend on its availability later.
Some other useful properties include:
autohide
(boolean) if the camera should auto hide after the media capture is completed (defaults to true) (iOS only)animated
(boolean) if the dialog should be animated (defaults to true) upon showing and hiding (Android and iOS only)allowEditing
(boolean) if the media should be editable after capture in the UI interface (iOS only)mediaTypes
an array of media type constants supported by the capture device UI (iOS only)videoMaximumDuration
(float) duration on how long in milliseconds to allow capture before completing (iOS only)videoQuality
(Ti.Media constant) to indicate the video quality during capture (iOS only)showControls
(boolean) to indicate if the built-in UI controls should be displayed (iOS only)overlay
(Ti.UI.View) which is added as an overlay to the camera UI (on top)transform
a transformation matrix that applies to the camera UI (not to the media returned) (iOS only)
You can check for the availability of the camera with the Titanium.Media.isCameraSupported
property: it's false if the device has no camera. The simulator/emulator does not have camera support. Other devices without cameras include 3rd generation and older iPods, the Nook Color, and Kindle Fire.
# Front/rear camera support
You can access both the front and rear facing cameras, if a device has such cameras. The code snippet below shows one way you might do this. Key properties are the Ti.Media.CAMERA_REAR
and Ti.Media.CAMERA_FRONT
as well as the Ti.Media.switchCamera()
method.
var cameras = Ti.Media.availableCameras;
for (var i = 0; i < cameras.length; i++) {
if (cameras[i] == Ti.Media.CAMERA_REAR) {
cameraType.addEventListener('click', function () {
if (Ti.Media.camera == Ti.Media.CAMERA_FRONT) {
cameraType.title = 'front';
Ti.Media.switchCamera(Ti.Media.CAMERA_REAR);
} else {
cameraType.title = 'rear';
Ti.Media.switchCamera(Ti.Media.CAMERA_FRONT);
}
});
break;
}
}
# Alternative Android capture video example
On Android you can alternatively create an intent to capture video. This intent is used to start a new activity, which launches the camera to record video. After the user stops recording, the video is saved to the device's gallery. The callback retrieves the URI of the video media and stores the reference in a global variable. Using the global variable, you can launch the video in a media player or save the content to a different location.
var win = Ti.UI.createWindow({
layout: 'vertical',
backgroundColor: 'gray'
}),
recordButton = Ti.UI.createButton({
title: 'RECORD VIDEO'
}),
playButton = Ti.UI.createButton({
title: 'PLAY VIDEO',
visible: false
}),
videoIntent = Ti.Android.createIntent({
// http://developer.android.com/reference/android/provider/MediaStore.html#ACTION_VIDEO_CAPTURE
action: 'android.media.action.VIDEO_CAPTURE'
}),
videoURL = null;
recordButton.addEventListener('click', function () {
// start an intent to capture video
win.getActivity().startActivityForResult(videoIntent, function (e) {
// video capture activity returned successfully
if (e.resultCode === Ti.Android.RESULT_OK) {
// verify we have a video URL
if (e.intent.data != null) {
videoURL = e.intent.data;
playButton.visible = true;
} else {
Ti.API.error('could not retrieve media URL!');
}
// video capture was cancelled
} else if (e.resultCode == Ti.Android.RESULT_CANCELED) {
Ti.API.trace('user cancelled video capture session.');
// handle all other results as a failure
} else {
Ti.API.error('could not record video!');
}
});
});
playButton.addEventListener('click', function (e) {
var player = Ti.Media.createVideoPlayer({ url: videoURL, autoplay: true });
win.add(player);
});
win.add([ recordButton, playButton ]);
win.open();
⚠️ Warning
Some third party Android camera apps may choose to ignore video recording quality settings. If you wish to specifically set the video quality, don't assume EXTRA_VIDEO_QUALITY intent will be respected by the camera app and use Titanium's built-in camera window which can be used to assign the "overlay" property when calling the showCamera()
method.
# Gallery
You open the native gallery by calling the openPhotoGallery()
(opens new window) method. As with the camera, you define three callback functions that are called for the success, cancel, and error events:
Gallery
var win = Ti.UI.createWindow(),
btn = Ti.UI.createButton({
title: 'OPEN GALLERY',
});
btn.addEventListener('click', function(){
Ti.Media.openPhotoGallery({
mediaTypes: [ Titanium.Media.MEDIA_TYPE_PHOTO ],
success: function (e) {
alert('media.width: ' + e.media.width
+ '\nmedia.height: ' + e.media.height
+ '\nmedia.length: ' + e.media.length
+ '\nmedia.mimeType: ' + e.media.mimeType
+ '\nmedia.nativePath: ' + e.media.nativePath);
},
error: function (e) {
alert('error opening image: ' + e);
}
});
});
win.add(btn);
win.open();
Some other useful properties include:
animated
(boolean) if the dialog should be animated (defaults to true) upon showing and hidingallowEditing
(boolean) if the media should be editable after capture in the UI interfacesaveToPhotoGallery
(boolean) whether modified media should be saved back to the gallery, generally only useful withallowEditing=true
mediaTypes
an array of media type constants supported by the capture device UIshowControls
(boolean) to indicate if the built-in UI controls should be displayedoverlay
(Ti.UI.View) which is added as an overlay to the camera UI (on top)transform
a transformation matrix that applies to the camera UI (not to the media returned)(iPad only)
popoverView
(Ti.UI.View) to position the photo gallery popover a specific view(iPad only)
arrowDirection
control the type of arrow and position of the gallery
# Getting images into the simulator/emulator's gallery
The photo gallery on the simulator/emulator is of course empty to start. If you'll be testing photo gallery code, you'll need to put some images there. You can do so by following this procedure:
Open the gallery in the simulator/emulator, then close it. You have to do this at least once to initialize its data storage structures.
Find an image to save. For example, open the web browser, search for, and display an image.
Long-press on the image, when you release, you should get a menu of options.
Tap Save Image (or your device's equivalent button) to save the image to the gallery.
# Windows development considerations
❗️ Warning
Support for Windows 8.1 and Windows Phone SDKs has been deprecated as of SDK 6.3.0.GA and has be removed in SDK 7.0.0.GA.
In order to enable camera and audio recording for Windows Phone, you need to provide appropriate Capabilities in your tiapp.xml.
Windows Phone users are prompted to grant or deny permission when your application attempt to use it.
# Grant access to video stream and audio stream
<ti:app>
...
<windows>
...
<Capabilities>
<DeviceCapability Name="microphone" />
<DeviceCapability Name="webcam" />
</Capabilities>
...
</windows>
...
</ti:app>
# Grant access to pictures library
<ti:app>
...
<windows>
...
<Capabilities>
<Capability Name="picturesLibrary" />
</Capabilities>
...
</windows>
...
</ti:app>
For more information about audio configuration in tiapp.xml
, see Windows-specific section in tiapp.xml and timodule.xml Reference.
# Displaying builtin camera for Windows
# For Windows 10 Mobile
As of Titanium 5.4.0, you can launch "default camera UI" that is builtin to the platform when you use Ti.Media.showCamera(options)
without specifying overlay
property. This Windows builtin camera UI provides same look & feel that Microsoft Windows Camera (opens new window) app provides. Note that this builtin camera UI is supported as of Windows 10 apps including Mobile.
var imageView = Ti.UI.createImageView();
Ti.Media.showCamera({
mediaTypes: [Ti.Media.MEDIA_TYPE_PHOTO],
success: function (e) {
imageView.image = e.media;
},
error: function (e) {
Ti.API.error(JSON.stringify(e));
}
});
# Hands-on practice
# Goal
In this activity, you will write an app that will capture an image from the camera and display it in a view. If the device doesn't have a camera, your app should display the gallery picker instead.
# Steps
Create a new project named PhotoTest.
On tab1 of the default project, replace the label with an ImageView that is 300x300 points positioned 5 points from the top.
Add a button that is 100x50 positioned 5 points from the bottom.
When the button is clicked, show the camera if it's available, otherwise open the photo gallery. You'll need three callbacks--write your code to reuse the same three functions to minimize duplicated code. You'll need these callbacks:
Success: set the ImageView's image property to the media returned from the camera or gallery. Output the
mediaType
to the info console.Cancel: don't do anything in this function
Error: Display an alert dialog box showing the
error.code
describing why the operation failed.
Open the simulator/emulator and save at least one photo to it, following the steps above.
Build the app and run it on the simulator/emulator. The gallery should open.
(Optional) Install the app onto a device and run it from there. The camera should open.
# Summary
In this chapter, you learned how to capture an image from the camera, use it in an app, and save it to the gallery or filesystem. You also explored the closely related gallery APIs, which let your users grab images from their photo albums for use within your app.