Skip to content

Commit e0cc55a

Browse files
author
Eino-Ville Talvala
committed
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
1 parent 843e04d commit e0cc55a

File tree

5 files changed

+209
-3
lines changed

5 files changed

+209
-3
lines changed

core/java/android/hardware/Camera.java

Lines changed: 206 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,12 @@
2222
import android.graphics.Point;
2323
import android.graphics.Rect;
2424
import android.graphics.SurfaceTexture;
25+
import android.media.AudioManager;
26+
import android.media.MediaPlayer;
2527
import android.os.Handler;
2628
import android.os.Looper;
2729
import android.os.Message;
30+
import android.os.SystemProperties;
2831
import android.util.Log;
2932
import android.view.Surface;
3033
import 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
}

data/sounds/AudioPackage5.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ PRODUCT_COPY_FILES += \
2020
$(LOCAL_PATH)/effects/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
2121
$(LOCAL_PATH)/effects/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
2222
$(LOCAL_PATH)/effects/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
23+
$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
2324
$(LOCAL_PATH)/effects/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
2425
$(LOCAL_PATH)/effects/Dock.ogg:system/media/audio/ui/Dock.ogg \
2526
$(LOCAL_PATH)/effects/Undock.ogg:system/media/audio/ui/Undock.ogg \

data/sounds/AudioPackage6.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ PRODUCT_COPY_FILES += \
1919
$(LOCAL_PATH)/effects/ogg/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
2020
$(LOCAL_PATH)/effects/ogg/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
2121
$(LOCAL_PATH)/effects/ogg/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
22+
$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
2223
$(LOCAL_PATH)/effects/ogg/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
2324
$(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
2425
$(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg \

data/sounds/AudioPackage7.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ PRODUCT_COPY_FILES += \
2121
$(LOCAL_PATH)/effects/ogg/KeypressReturn_120.ogg:system/media/audio/ui/KeypressReturn.ogg \
2222
$(LOCAL_PATH)/effects/ogg/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
2323
$(LOCAL_PATH)/effects/ogg/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
24+
$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
2425
$(LOCAL_PATH)/effects/ogg/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
2526
$(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
2627
$(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg \
9.16 KB
Binary file not shown.

0 commit comments

Comments
 (0)