From e0cc55ac725feec88c77b482d1990221c9a80f74 Mon Sep 17 00:00:00 2001 From: Eino-Ville Talvala Date: Tue, 8 Nov 2011 10:12:09 -0800 Subject: [PATCH] Add a sound-playing method to Camera API. To allow applications with non-standard Camera use cases to use the platform sound files and routing, add a method to play any of the standard Camera sounds (shutter, autofocus, record start/stop) using a background thread. Bug: 5447107 Change-Id: I2524853a626e3ce334a7aad2f7de061d5c04abd0 --- core/java/android/hardware/Camera.java | 209 ++++++++++++++++++++++- data/sounds/AudioPackage5.mk | 1 + data/sounds/AudioPackage6.mk | 1 + data/sounds/AudioPackage7.mk | 1 + data/sounds/effects/ogg/camera_focus.ogg | Bin 0 -> 9376 bytes 5 files changed, 209 insertions(+), 3 deletions(-) create mode 100644 data/sounds/effects/ogg/camera_focus.ogg diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 48adfad675586..3becec0189f96 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -22,9 +22,12 @@ import android.graphics.ImageFormat; import android.graphics.Point; import android.graphics.Rect; import android.graphics.SurfaceTexture; +import android.media.AudioManager; +import android.media.MediaPlayer; import android.os.Handler; import android.os.Looper; import android.os.Message; +import android.os.SystemProperties; import android.util.Log; import android.view.Surface; import android.view.SurfaceHolder; @@ -154,6 +157,7 @@ public class Camera { private boolean mOneShot; private boolean mWithBuffer; private boolean mFaceDetectionRunning = false; + private boolean mReleased = false; /** * Broadcast Action: A new picture is taken by the camera, and the entry of @@ -303,7 +307,7 @@ public class Camera { } protected void finalize() { - native_release(); + release(); } private native final void native_setup(Object camera_this, int cameraId); @@ -318,6 +322,15 @@ public class Camera { public final void release() { native_release(); mFaceDetectionRunning = false; + if (mCameraSoundPlayers != null) { + for (CameraSoundPlayer csp: mCameraSoundPlayers) { + if (csp != null) { + csp.release(); + } + } + mCameraSoundPlayers = null; + } + mReleased = true; } /** @@ -2354,7 +2367,7 @@ public class Camera { * *

The reference code is as follows. * - *

+         * 
          * public void onOrientationChanged(int orientation) {
          *     if (orientation == ORIENTATION_UNKNOWN) return;
          *     android.hardware.Camera.CameraInfo info =
@@ -2369,7 +2382,7 @@ public class Camera {
          *     }
          *     mParameters.setRotation(rotation);
          * }
-	 * 
+ *
* * @param rotation The rotation angle in degrees relative to the * orientation of the camera. Rotation can only be 0, @@ -3452,4 +3465,194 @@ public class Camera { return result; } }; + + /** + *

The set of default system sounds for camera actions. Use this with + * {@link #playSound} to play an appropriate sound when implementing a + * custom still or video recording mechanism through the preview + * callbacks.

+ * + *

There is no need to play sounds when using {@link #takePicture} or + * {@link android.media.MediaRecorder} for still images or video, + * respectively, as these play their own sounds when needed.

+ * + * @see #playSound + * @hide + */ + public static class Sound { + /** + * The sound used by {@link android.hardware.Camera#takePicture} to + * indicate still image capture. + */ + public static final int SHUTTER_CLICK = 0; + + /** + * A sound to indicate that focusing has completed. Because deciding + * when this occurs is application-dependent, this sound is not used by + * any methods in the Camera class. + */ + public static final int FOCUS_COMPLETE = 1; + + /** + * The sound used by {@link android.media.MediaRecorder#start} to + * indicate the start of video recording. + */ + public static final int START_VIDEO_RECORDING = 2; + + /** + * The sound used by {@link android.media.MediaRecorder#stop} to + * indicate the end of video recording. + */ + public static final int STOP_VIDEO_RECORDING = 3; + + private static final int NUM_SOUNDS = 4; + }; + + /** + *

Play one of the predefined platform sounds for camera actions.

+ * + *

Use this method to play a platform-specific sound for various camera + * actions. The sound playing is done asynchronously, with the same behavior + * and content as the sounds played by {@link #takePicture takePicture}, + * {@link android.media.MediaRecorder#start MediaRecorder.start}, and + * {@link android.media.MediaRecorder#stop MediaRecorder.stop}.

+ * + *

Using this method makes it easy to match the default device sounds + * when recording or capturing data through the preview callbacks + * ({@link #setPreviewCallback setPreviewCallback}, + * {@link #setPreviewTexture setPreviewTexture}).

+ * + * @param soundId The type of sound to play, selected from the options in + * {@link android.hardware.Camera.Sound} + * @see android.hardware.Camera.Sound + * @see #takePicture + * @see android.media.MediaRecorder + * @hide + */ + public void playSound(int soundId) { + if (mReleased) return; + if (mCameraSoundPlayers == null) { + mCameraSoundPlayers = new CameraSoundPlayer[Sound.NUM_SOUNDS]; + } + if (mCameraSoundPlayers[soundId] == null) { + mCameraSoundPlayers[soundId] = new CameraSoundPlayer(soundId); + } + mCameraSoundPlayers[soundId].play(); + } + + private CameraSoundPlayer[] mCameraSoundPlayers; + + private static class CameraSoundPlayer implements Runnable { + private int mSoundId; + private int mAudioStreamType; + private MediaPlayer mPlayer; + private Thread mThread; + private boolean mExit; + private int mPlayCount; + + private static final String mShutterSound = + "/system/media/audio/ui/camera_click.ogg"; + private static final String mFocusSound = + "/system/media/audio/ui/camera_focus.ogg"; + private static final String mVideoStartSound = + "/system/media/audio/ui/VideoRecord.ogg"; + private static final String mVideoStopSound = + "/system/media/audio/ui/VideoRecord.ogg"; + + @Override + public void run() { + String soundFilePath; + switch (mSoundId) { + case Sound.SHUTTER_CLICK: + soundFilePath = mShutterSound; + break; + case Sound.FOCUS_COMPLETE: + soundFilePath = mFocusSound; + break; + case Sound.START_VIDEO_RECORDING: + soundFilePath = mVideoStartSound; + break; + case Sound.STOP_VIDEO_RECORDING: + soundFilePath = mVideoStopSound; + break; + default: + Log.e(TAG, "Unknown sound " + mSoundId + " requested."); + return; + } + mPlayer = new MediaPlayer(); + try { + mPlayer.setAudioStreamType(mAudioStreamType); + mPlayer.setDataSource(soundFilePath); + mPlayer.setLooping(false); + mPlayer.prepare(); + } catch(IOException e) { + Log.e(TAG, "Error setting up sound " + mSoundId, e); + return; + } + + while(true) { + try { + synchronized (this) { + while(true) { + if (mExit) { + return; + } else if (mPlayCount <= 0) { + wait(); + } else { + mPlayCount--; + break; + } + } + } + mPlayer.start(); + } catch (Exception e) { + Log.e(TAG, "Error playing sound " + mSoundId, e); + } + } + } + + public CameraSoundPlayer(int soundId) { + mSoundId = soundId; + if (SystemProperties.get("ro.camera.sound.forced", "0").equals("0")) { + mAudioStreamType = AudioManager.STREAM_MUSIC; + } else { + mAudioStreamType = AudioManager.STREAM_SYSTEM_ENFORCED; + } + } + + public void play() { + if (mThread == null) { + mThread = new Thread(this); + mThread.start(); + } + synchronized (this) { + mPlayCount++; + notifyAll(); + } + } + + public void release() { + if (mThread != null) { + synchronized (this) { + mExit = true; + notifyAll(); + } + try { + mThread.join(); + } catch (InterruptedException e) { + } + mThread = null; + } + if (mPlayer != null) { + mPlayer.release(); + mPlayer = null; + } + } + + @Override + protected void finalize() { + release(); + } + } + } diff --git a/data/sounds/AudioPackage5.mk b/data/sounds/AudioPackage5.mk index 550f990a8f669..5961f06a57c00 100755 --- a/data/sounds/AudioPackage5.mk +++ b/data/sounds/AudioPackage5.mk @@ -20,6 +20,7 @@ PRODUCT_COPY_FILES += \ $(LOCAL_PATH)/effects/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \ $(LOCAL_PATH)/effects/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \ $(LOCAL_PATH)/effects/camera_click.ogg:system/media/audio/ui/camera_click.ogg \ + $(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \ $(LOCAL_PATH)/effects/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \ $(LOCAL_PATH)/effects/Dock.ogg:system/media/audio/ui/Dock.ogg \ $(LOCAL_PATH)/effects/Undock.ogg:system/media/audio/ui/Undock.ogg \ diff --git a/data/sounds/AudioPackage6.mk b/data/sounds/AudioPackage6.mk index 610e82136d6cd..d113a29f59bb7 100755 --- a/data/sounds/AudioPackage6.mk +++ b/data/sounds/AudioPackage6.mk @@ -19,6 +19,7 @@ PRODUCT_COPY_FILES += \ $(LOCAL_PATH)/effects/ogg/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \ $(LOCAL_PATH)/effects/ogg/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \ $(LOCAL_PATH)/effects/ogg/camera_click.ogg:system/media/audio/ui/camera_click.ogg \ + $(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \ $(LOCAL_PATH)/effects/ogg/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \ $(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \ $(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg \ diff --git a/data/sounds/AudioPackage7.mk b/data/sounds/AudioPackage7.mk index 93e0fa021d75d..6ae624e916d08 100755 --- a/data/sounds/AudioPackage7.mk +++ b/data/sounds/AudioPackage7.mk @@ -21,6 +21,7 @@ PRODUCT_COPY_FILES += \ $(LOCAL_PATH)/effects/ogg/KeypressReturn_120.ogg:system/media/audio/ui/KeypressReturn.ogg \ $(LOCAL_PATH)/effects/ogg/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \ $(LOCAL_PATH)/effects/ogg/camera_click.ogg:system/media/audio/ui/camera_click.ogg \ + $(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \ $(LOCAL_PATH)/effects/ogg/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \ $(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \ $(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg \ diff --git a/data/sounds/effects/ogg/camera_focus.ogg b/data/sounds/effects/ogg/camera_focus.ogg new file mode 100644 index 0000000000000000000000000000000000000000..0db2683ad5f46b2bf1d53b6bab5bc1c8dd95092c GIT binary patch literal 9376 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 literal 0 HcmV?d00001