2222import android .graphics .Point ;
2323import android .graphics .Rect ;
2424import android .graphics .SurfaceTexture ;
25+ import android .media .AudioManager ;
26+ import android .media .MediaPlayer ;
2527import android .os .Handler ;
2628import android .os .Looper ;
2729import android .os .Message ;
30+ import android .os .SystemProperties ;
2831import android .util .Log ;
2932import android .view .Surface ;
3033import android .view .SurfaceHolder ;
@@ -154,6 +157,7 @@ public class Camera {
154157 private boolean mOneShot ;
155158 private boolean mWithBuffer ;
156159 private boolean mFaceDetectionRunning = false ;
160+ private boolean mReleased = false ;
157161
158162 /**
159163 * Broadcast Action: A new picture is taken by the camera, and the entry of
@@ -303,7 +307,7 @@ public static Camera open() {
303307 }
304308
305309 protected void finalize () {
306- native_release ();
310+ release ();
307311 }
308312
309313 private native final void native_setup (Object camera_this , int cameraId );
@@ -318,6 +322,15 @@ protected void finalize() {
318322 public final void release () {
319323 native_release ();
320324 mFaceDetectionRunning = false ;
325+ if (mCameraSoundPlayers != null ) {
326+ for (CameraSoundPlayer csp : mCameraSoundPlayers ) {
327+ if (csp != null ) {
328+ csp .release ();
329+ }
330+ }
331+ mCameraSoundPlayers = null ;
332+ }
333+ mReleased = true ;
321334 }
322335
323336 /**
@@ -2354,7 +2367,7 @@ private int pixelFormatForCameraFormat(String format) {
23542367 *
23552368 * <p>The reference code is as follows.
23562369 *
2357- * <pre>
2370+ * <pre>
23582371 * public void onOrientationChanged(int orientation) {
23592372 * if (orientation == ORIENTATION_UNKNOWN) return;
23602373 * android.hardware.Camera.CameraInfo info =
@@ -2369,7 +2382,7 @@ private int pixelFormatForCameraFormat(String format) {
23692382 * }
23702383 * mParameters.setRotation(rotation);
23712384 * }
2372- * </pre>
2385+ * </pre>
23732386 *
23742387 * @param rotation The rotation angle in degrees relative to the
23752388 * orientation of the camera. Rotation can only be 0,
@@ -3452,4 +3465,194 @@ private ArrayList<Area> splitArea(String str) {
34523465 return result ;
34533466 }
34543467 };
3468+
3469+ /**
3470+ * <p>The set of default system sounds for camera actions. Use this with
3471+ * {@link #playSound} to play an appropriate sound when implementing a
3472+ * custom still or video recording mechanism through the preview
3473+ * callbacks.</p>
3474+ *
3475+ * <p>There is no need to play sounds when using {@link #takePicture} or
3476+ * {@link android.media.MediaRecorder} for still images or video,
3477+ * respectively, as these play their own sounds when needed.</p>
3478+ *
3479+ * @see #playSound
3480+ * @hide
3481+ */
3482+ public static class Sound {
3483+ /**
3484+ * The sound used by {@link android.hardware.Camera#takePicture} to
3485+ * indicate still image capture.
3486+ */
3487+ public static final int SHUTTER_CLICK = 0 ;
3488+
3489+ /**
3490+ * A sound to indicate that focusing has completed. Because deciding
3491+ * when this occurs is application-dependent, this sound is not used by
3492+ * any methods in the Camera class.
3493+ */
3494+ public static final int FOCUS_COMPLETE = 1 ;
3495+
3496+ /**
3497+ * The sound used by {@link android.media.MediaRecorder#start} to
3498+ * indicate the start of video recording.
3499+ */
3500+ public static final int START_VIDEO_RECORDING = 2 ;
3501+
3502+ /**
3503+ * The sound used by {@link android.media.MediaRecorder#stop} to
3504+ * indicate the end of video recording.
3505+ */
3506+ public static final int STOP_VIDEO_RECORDING = 3 ;
3507+
3508+ private static final int NUM_SOUNDS = 4 ;
3509+ };
3510+
3511+ /**
3512+ * <p>Play one of the predefined platform sounds for camera actions.</p>
3513+ *
3514+ * <p>Use this method to play a platform-specific sound for various camera
3515+ * actions. The sound playing is done asynchronously, with the same behavior
3516+ * and content as the sounds played by {@link #takePicture takePicture},
3517+ * {@link android.media.MediaRecorder#start MediaRecorder.start}, and
3518+ * {@link android.media.MediaRecorder#stop MediaRecorder.stop}.</p>
3519+ *
3520+ * <p>Using this method makes it easy to match the default device sounds
3521+ * when recording or capturing data through the preview callbacks
3522+ * ({@link #setPreviewCallback setPreviewCallback},
3523+ * {@link #setPreviewTexture setPreviewTexture}).</p>
3524+ *
3525+ * @param soundId The type of sound to play, selected from the options in
3526+ * {@link android.hardware.Camera.Sound}
3527+ * @see android.hardware.Camera.Sound
3528+ * @see #takePicture
3529+ * @see android.media.MediaRecorder
3530+ * @hide
3531+ */
3532+ public void playSound (int soundId ) {
3533+ if (mReleased ) return ;
3534+ if (mCameraSoundPlayers == null ) {
3535+ mCameraSoundPlayers = new CameraSoundPlayer [Sound .NUM_SOUNDS ];
3536+ }
3537+ if (mCameraSoundPlayers [soundId ] == null ) {
3538+ mCameraSoundPlayers [soundId ] = new CameraSoundPlayer (soundId );
3539+ }
3540+ mCameraSoundPlayers [soundId ].play ();
3541+ }
3542+
3543+ private CameraSoundPlayer [] mCameraSoundPlayers ;
3544+
3545+ private static class CameraSoundPlayer implements Runnable {
3546+ private int mSoundId ;
3547+ private int mAudioStreamType ;
3548+ private MediaPlayer mPlayer ;
3549+ private Thread mThread ;
3550+ private boolean mExit ;
3551+ private int mPlayCount ;
3552+
3553+ private static final String mShutterSound =
3554+ "/system/media/audio/ui/camera_click.ogg" ;
3555+ private static final String mFocusSound =
3556+ "/system/media/audio/ui/camera_focus.ogg" ;
3557+ private static final String mVideoStartSound =
3558+ "/system/media/audio/ui/VideoRecord.ogg" ;
3559+ private static final String mVideoStopSound =
3560+ "/system/media/audio/ui/VideoRecord.ogg" ;
3561+
3562+ @ Override
3563+ public void run () {
3564+ String soundFilePath ;
3565+ switch (mSoundId ) {
3566+ case Sound .SHUTTER_CLICK :
3567+ soundFilePath = mShutterSound ;
3568+ break ;
3569+ case Sound .FOCUS_COMPLETE :
3570+ soundFilePath = mFocusSound ;
3571+ break ;
3572+ case Sound .START_VIDEO_RECORDING :
3573+ soundFilePath = mVideoStartSound ;
3574+ break ;
3575+ case Sound .STOP_VIDEO_RECORDING :
3576+ soundFilePath = mVideoStopSound ;
3577+ break ;
3578+ default :
3579+ Log .e (TAG , "Unknown sound " + mSoundId + " requested." );
3580+ return ;
3581+ }
3582+ mPlayer = new MediaPlayer ();
3583+ try {
3584+ mPlayer .setAudioStreamType (mAudioStreamType );
3585+ mPlayer .setDataSource (soundFilePath );
3586+ mPlayer .setLooping (false );
3587+ mPlayer .prepare ();
3588+ } catch (IOException e ) {
3589+ Log .e (TAG , "Error setting up sound " + mSoundId , e );
3590+ return ;
3591+ }
3592+
3593+ while (true ) {
3594+ try {
3595+ synchronized (this ) {
3596+ while (true ) {
3597+ if (mExit ) {
3598+ return ;
3599+ } else if (mPlayCount <= 0 ) {
3600+ wait ();
3601+ } else {
3602+ mPlayCount --;
3603+ break ;
3604+ }
3605+ }
3606+ }
3607+ mPlayer .start ();
3608+ } catch (Exception e ) {
3609+ Log .e (TAG , "Error playing sound " + mSoundId , e );
3610+ }
3611+ }
3612+ }
3613+
3614+ public CameraSoundPlayer (int soundId ) {
3615+ mSoundId = soundId ;
3616+ if (SystemProperties .get ("ro.camera.sound.forced" , "0" ).equals ("0" )) {
3617+ mAudioStreamType = AudioManager .STREAM_MUSIC ;
3618+ } else {
3619+ mAudioStreamType = AudioManager .STREAM_SYSTEM_ENFORCED ;
3620+ }
3621+ }
3622+
3623+ public void play () {
3624+ if (mThread == null ) {
3625+ mThread = new Thread (this );
3626+ mThread .start ();
3627+ }
3628+ synchronized (this ) {
3629+ mPlayCount ++;
3630+ notifyAll ();
3631+ }
3632+ }
3633+
3634+ public void release () {
3635+ if (mThread != null ) {
3636+ synchronized (this ) {
3637+ mExit = true ;
3638+ notifyAll ();
3639+ }
3640+ try {
3641+ mThread .join ();
3642+ } catch (InterruptedException e ) {
3643+ }
3644+ mThread = null ;
3645+ }
3646+ if (mPlayer != null ) {
3647+ mPlayer .release ();
3648+ mPlayer = null ;
3649+ }
3650+ }
3651+
3652+ @ Override
3653+ protected void finalize () {
3654+ release ();
3655+ }
3656+ }
3657+
34553658}
0 commit comments