Media
Written for Titanium SDK 13.4.0 and later
Everything on this page needs permission before it works, and asking is your job. A camera call with no permission does not throw: it fails quietly, or the system dialog appears once and never again after the user says no.
Ask first
Each capability has a matching pair of methods.
| Capability | Check | Ask |
|---|---|---|
| Camera | Ti.Media.hasCameraPermissions() |
requestCameraPermissions() |
| Photo library | hasPhotoGalleryPermissions() |
requestPhotoGalleryPermissions() |
| Microphone | hasAudioRecorderPermissions() |
requestAudioRecorderPermissions() |
| Music library | hasMusicLibraryPermissions() |
requestMusicLibraryPermissions() |
function withCamera(done) {
if (Ti.Media.hasCameraPermissions()) {
done();
return;
}
Ti.Media.requestCameraPermissions((e) => {
if (e.success) {
done();
} else {
Ti.API.warn('camera denied');
}
});
}
iOS also needs a usage string in tiapp.xml, or the app is rejected at review
and the dialog never appears:
<ios>
<plist>
<dict>
<key>NSCameraUsageDescription</key>
<string>Attach a photo to a note.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Choose a photo to attach to a note.</string>
<key>NSMicrophoneUsageDescription</key>
<string>Record a voice note.</string>
</dict>
</plist>
</ios>
Write the real reason. "Required by the app" is the string reviewers reject.
The camera
Ti.Media.showCamera({
mediaTypes: [Ti.Media.MEDIA_TYPE_PHOTO],
saveToPhotoGallery: false,
success(e) {
const image = Ti.UI.createImageView({ image: e.media, width: 200, height: 200 });
win.add(image);
},
cancel() {},
error(e) {
Ti.API.error(e.error);
}
});
e.media is a Blob. Assign it straight to an ImageView, or write it to a
file with Ti.Filesystem.
saveToPhotoGallery defaults to false, so a photo your app took exists only in
that blob until you save it somewhere. Write it to
applicationDataDirectory if it needs to outlive the screen.
Close the camera with Ti.Media.hideCamera() when you dismiss it yourself.
The photo library
Ti.Media.openPhotoGallery({
mediaTypes: [Ti.Media.MEDIA_TYPE_PHOTO],
allowMultiple: false,
success(e) {
Ti.API.info(`picked ${e.media.length || 1} item`);
},
cancel() {}
});
With allowMultiple: true the success event carries an images array instead
of a single media, which is the shape change to handle rather than assume.
Audio
Three objects, for three jobs.
| Object | For |
|---|---|
Ti.Media.Sound |
Short effects, held in memory |
Ti.Media.AudioPlayer |
Long files and streams |
Ti.Media.AudioRecorder |
Recording |
const sound = Ti.Media.createSound({ url: '/audio/tap.wav' });
sound.play();
const player = Ti.Media.createAudioPlayer({ url: 'https://example.com/talk.mp3' });
player.addEventListener('change', (e) => Ti.API.info(e.description));
player.start();
Sound loads the whole file, so it is for something short. AudioPlayer
buffers, which is what you want for anything longer than a notification chime.
For audio that continues when the app is backgrounded, set
Ti.Media.audioSessionCategory and declare background audio in tiapp.xml.
Both are required; either alone stops the audio at the home gesture.
Video
const video = Ti.Media.createVideoPlayer({
url: 'https://example.com/clip.mp4',
width: Ti.UI.FILL,
height: 240,
autoplay: true
});
win.add(video);
A VideoPlayer is a view, so it lays out like any other and defaults to
filling its parent. Release it when the window closes: it holds a decoder, and
leaving it alive costs memory that the platform will eventually take back by
killing the app.
Next
Location and maps covers the other capability that needs asking first.