is still useful without a camera: <uses-feature android:name="android.hardware.camera" android: required="false" /> • Other camera feature descriptors: android.hardware.camera.any android.hardware.camera.front android.hardware.camera.autofocus android.hardware.camera.flash
application that is installed on the device • Intents are guaranteed to be honoured on any device with a rear facing camera that is certified by CTS and has the Google Play Store
a picture and return control to the calling application Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); // create a file to save the image fileUri = getOutputMediaFileUri(MEDIA_TYPE_IMAGE); // set the image file name intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri); // start the image capture Intent startActivityForResult(intent, CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE); • MediaStore.EXTRA_OUTPUT ◦ Uri object specifying target path and filename
int resultCode, Intent data) { if (requestCode == CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE) { if (resultCode == RESULT_OK) { // Image captured and saved to fileUri specified in the Intent Toast.makeText(this, "Image saved to: " + data.getData(), Toast.LENGTH_LONG).show(); } else if (resultCode == RESULT_CANCELED) { // User cancelled the image capture } else { // Image capture failed, advise user } } }
= new Intent(MediaStore.ACTION_VIDEO_CAPTURE); // create a file to save the video fileUri = getOutputMediaFileUri(MEDIA_TYPE_VIDEO); // set the video file name intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri); // set the video image quality to high intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1); // start the Video Capture Intent startActivityForResult(intent, CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE);
specifying target path and filename • MediaStore.EXTRA_VIDEO_QUALITY ◦ 0 = low, 1 = high • MediaStore.EXTRA_DURATION_LIMIT ◦ Limit the video length, in seconds • MediaStore.EXTRA_SIZE_LIMIT ◦ Limit the video file size, in bytes
int resultCode, Intent data) { if (requestCode == CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE) { if (resultCode == RESULT_OK) { // Video captured and saved to fileUri specified in the Intent Toast.makeText(this, "Video saved to:\n" + data.getData(), Toast.LENGTH_LONG).show(); } else if (resultCode == RESULT_CANCELED) { // User cancelled the video capture } else { // Video capture failed, advise user } } }
Only a few lines of code ◦ Takes advantage of features present in the device Camera app ◦ Perfect if the camera is not core to your application and you just need to capture a quick picture or video clip
interface or user experience ◦ Interface and experience will differ slightly across different devices ◦ No fine grained control of recording settings (e.g. MediaStore.EXTRA_VIDEO_QUALITY)
safely open the camera ◦ Code to test and enable required camera features ◦ A custom camera preview view extending SurfaceView that implements the SurfaceHolder. Callbacks interface ◦ An Activity or Fragment layout comprising the preview view and your custom controls (shutter button etc) ◦ Code to capture user interface events ◦ Code to capture and save media files ◦ Code to safely release the camera
as optional in manifest: /** Check if this device has a camera */ private boolean checkCameraHardware(Context context) { if (context.getPackageManager() .hasSystemFeature(PackageManager.FEATURE_CAMERA)){ // this device has a camera return true; } else { // no camera on this device return false; } }
camera: /** A safe way to get an instance of the Camera object. */ public static Camera getCameraInstance(){ Camera c = null; try { c = Camera.open(); // attempt to get a Camera instance } catch (Exception e){ // Camera is not available (in use or does not exist) } return c; // returns null if camera is unavailable }
access a specific hardware camera (API 9+) // Find the total number of cameras available int numberOfCameras = Camera.getNumberOfCameras(); // Find the ID of the default camera CameraInfo cameraInfo = new CameraInfo(); for (int i = 0; i < numberOfCameras; i++) { Camera.getCameraInfo(i, cameraInfo); if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) { defaultCameraId = i; } }
TextureView (API 14+) to render the camera preview public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback { private Camera mCamera; public CameraPreview(Context context, Camera camera) { super(context); mCamera = camera; // register a SurfaceHolder.Callback so we get notified when the // underlying surface is created and destroyed. getHolder().addCallback(this); } //... }
implements SurfaceHolder.Callback { // ... public void surfaceCreated(SurfaceHolder holder) { // The Surface has been created, now tell // the camera to start the preview. } public void surfaceDestroyed(SurfaceHolder holder) { // Take care of releasing the Camera preview. } public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { // If your preview can change or rotate, take // care of those events here. Make sure to stop // the preview before resizing or reformatting it. } // ... }
new CameraPreview passing it the camera instance, then add it to the content view • onResume: ◦ Open the camera instance • onPause: ◦ Release the camera instance
◦ Stop, reset and release the MediaRecorder: ▪ stop() ▪ reset() ▪ release() ◦ Lock the camera so future MediaRecorder sessions can use it: ▪ Camera.lock()
Detection ◦ Metering Areas ◦ Focus Areas ◦ White Balance Lock ◦ Exposure Lock ◦ Video Snapshot • API 11+ ◦ Time Lapse Video • API 9+ ◦ Multiple Cameras ◦ Focus Distance • API 8+ ◦ Zoom ◦ Exposure Compensation • API 5+ ◦ GPS Data ◦ White Balance ◦ Focus Mode ◦ Scene Mode ◦ JPEG Quality ◦ Flash Mode ◦ Color Effects ◦ Anti-Banding
params = mCamera.getParameters(); // set the focus mode params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO); // set Camera parameters mCamera.setParameters(params); • Some features cannot be changed while a camera preview has started • Some features require additional code to implement (Face detection etc)
query and use predefined settings supported by the device CamcorderProfile cp = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH); mMediaRecorder.setProfile(cp); • API 11+: ◦ QUALITY_1080P ◦ QUALITY_720P ◦ QUALITY_480P • API 8+ ◦ QUALITY_HIGH ◦ QUALITY_LOW
started: ◦ The camera API guide and training is overly simplistic and lacks important detail ◦ No fully fledged SDK sample application ◦ AOSP Camera app is quite complex, difficult to pull features from and is not necessarily broadly compatible
Real fragmentation, probably caused by underlying camera hardware differences across manufacturers and device types ◦ Camera.Parameters and CamcorderProfile do not always tell the truth
TODO hack for GS2 if (Build.MODEL.equals("GT-I9100")) { // green mess in video file without this params.set( "cam_mode", 1 ); } // TODO hack for 720p on GS4 if (Build.MODEL.equals("GT-I9500") || Build.MODEL.equals("GT-I9505")) { candidate = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH); candidate.videoFrameWidth = 1280; candidate.videoFrameHeight = 720; }
• The simplicity of the Intent approach combined with the flexibility and control provided by the Camera API • Encapsulates the details behind a scalable API • Easy to do simple things • Complex things not impossible • Open source (Apache 2.0)
The Busy Coder’s Guide to Android Development ◦ http://commonsware.com/books • CWAC = CommonsWare Android Components ◦ https://github.com/commonsguy • Droidcon London - "Android Security: Defending Your Users" ◦ http://uk.droidcon.com/2013/buyticket/ • One day workshop, Oct 22 - “Pushing the UI Envelope” ◦ http://skillsmatter.com/event/os-mobile-server/mark- murphys-pushing-the-ui-envelope
the appropriate surface format, optimally sized for the device ◦ Deals with configuration changes and rotation to support both portrait and landscape shooting ◦ Provides a simple API for taking pictures and recording video ◦ Opens and releases the camera automatically and safely • Native (API 11+) and support Fragment implementations • Underlying CameraView also available
your project • Include the ActionBarSherlock library project if supporting < API 11 • Add the appropriate camera manifest permissions • Create an XML layout for your camera UI with a container (e.g. FrameLayout) in which to attach CameraFragment • Create and attach an instance of CameraFragment in your Activity: public class MainActivity extends Activity { CameraFragment mCameraFragment; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mCameraFragment = new CameraFragment(); getFragmentManager().beginTransaction() .replace(R.id.preview_container, mCameraFragment).commit(); } }
◦ Output file names and locations ◦ Camera selection ◦ Exception handling ◦ Auto focus callbacks ◦ Shutter callbacks ◦ Preview sizes ◦ Picture and video recording parameters ◦ EXIF rotation behaviour ◦ DeviceProfile - a class that wraps any device specific hacks that might be needed