Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d5f07990 authored by Eino-Ville Talvala's avatar Eino-Ville Talvala Committed by Android (Google) Code Review
Browse files

Merge "NEW_API: Rework CameraSound into MediaActionSound, and unhide it."

parents 7072dce0 e6909586
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -10780,6 +10780,17 @@ package android.media {
    method public abstract void onJetUserIdUpdate(android.media.JetPlayer, int, int);
  }
  public class MediaActionSound {
    ctor public MediaActionSound();
    method public void load(int);
    method public void play(int);
    method public void release();
    field public static final int FOCUS_COMPLETE = 1; // 0x1
    field public static final int SHUTTER_CLICK = 0; // 0x0
    field public static final int START_VIDEO_RECORDING = 2; // 0x2
    field public static final int STOP_VIDEO_RECORDING = 3; // 0x3
  }
  public class MediaMetadataRetriever {
    ctor public MediaMetadataRetriever();
    method public java.lang.String extractMetadata(int);
+0 −1
Original line number Diff line number Diff line
@@ -36,7 +36,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.StringTokenizer;


/**
 * The Camera class is used to set image capture settings, start/stop preview,
 * snap pictures, and retrieve frames for encoding for video.  This class is a
+0 −217
Original line number Diff line number Diff line
/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.hardware;

import android.media.AudioManager;
import android.media.MediaPlayer;
import android.os.SystemProperties;
import android.util.Log;

import java.io.IOException;

/**
 * <p>Use this class to play an appropriate sound when implementing a custom
 * still or video recording mechanism through the preview callbacks.</p>
 *
 * <p>There is no need to play sounds when using {@link #android.hardware.Camera#takePicture}
 * or {@link android.media.MediaRecorder} for still images or video,
 * respectively, as these play their own sounds when needed.</p>
 *
 * @hide
 */
public class CameraSound {
    private static final String TAG = "CameraSound";
    /**
     * 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;
    private CameraSoundPlayer[] mCameraSoundPlayers;

    public CameraSound() {
    }

    /**
     * <p>Play one of the predefined platform sounds for camera actions.</p>
     *
     * <p>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}.</p>
     *
     * <p>Using this method makes it easy to match the default device sounds
     * when recording or capturing data through the preview callbacks.</p>
     *
     * @param soundId The type of sound to play, selected from SHUTTER_CLICK,
     *         FOCUS_COMPLETE, START_VIDEO_RECORDING, or STOP_VIDEO_RECORDING.
     * @see android.hardware#takePicture
     * @see android.media.MediaRecorder
     * @see #SHUTTER_CLICK
     * @see #FOCUS_COMPLETE
     * @see #START_VIDEO_RECORDING
     * @see #STOP_VIDEO_RECORDING
     */
    public void playSound(int soundId) {
        if (mCameraSoundPlayers == null) {
            mCameraSoundPlayers = new CameraSoundPlayer[NUM_SOUNDS];
        }
        if (mCameraSoundPlayers[soundId] == null) {
            mCameraSoundPlayers[soundId] = new CameraSoundPlayer(soundId);
        }
        mCameraSoundPlayers[soundId].play();
    }

    public void release() {
        if (mCameraSoundPlayers != null) {
            for (CameraSoundPlayer csp: mCameraSoundPlayers) {
                if (csp != null) {
                    csp.release();
                }
            }
            mCameraSoundPlayers = null;
        }
    }

    private static class CameraSoundPlayer implements Runnable {
        private int mSoundId;
        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 SHUTTER_CLICK:
                    soundFilePath = mShutterSound;
                    break;
                case FOCUS_COMPLETE:
                    soundFilePath = mFocusSound;
                    break;
                case START_VIDEO_RECORDING:
                    soundFilePath = mVideoStartSound;
                    break;
                case STOP_VIDEO_RECORDING:
                    soundFilePath = mVideoStopSound;
                    break;
                default:
                    Log.e(TAG, "Unknown sound " + mSoundId + " requested.");
                    return;
            }
            mPlayer = new MediaPlayer();
            try {
                mPlayer.setAudioStreamType(AudioManager.STREAM_SYSTEM_ENFORCED);
                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;
        }

        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();
        }
    }
}
 No newline at end of file
+194 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.media;

import android.media.AudioManager;
import android.media.SoundPool;
import android.util.Log;

/**
 * <p>A class for producing sounds that match those produced by various actions
 * taken by the media and camera APIs.  </p>
 *
 * <p>Use this class to play an appropriate camera operation sound when
 * implementing a custom still or video recording mechanism (through the Camera
 * preview callbacks with {@link android.hardware.Camera#setPreviewCallback
 * Camera.setPreviewCallback}, or through GPU processing with {@link
 * android.hardware.Camera#setPreviewTexture Camera.setPreviewTexture}, for
 * example), or when implementing some other camera-like function in your
 * application.</p>
 *
 * <p>There is no need to play sounds when using
 * {@link android.hardware.Camera#takePicture Camera.takePicture} or
 * {@link android.media.MediaRecorder} for still images or video, respectively,
 * as the Android framework will play the appropriate sounds when needed for
 * these calls.</p>
 *
 */
public class MediaActionSound {
    private static final int NUM_MEDIA_SOUND_STREAMS = 1;

    private SoundPool mSoundPool;
    private int[]     mSoundIds;
    private int       mSoundIdToPlay;

    private static final String[] SOUND_FILES = {
        "/system/media/audio/ui/camera_click.ogg",
        "/system/media/audio/ui/camera_focus.ogg",
        "/system/media/audio/ui/VideoRecord.ogg",
        "/system/media/audio/ui/VideoRecord.ogg"
    };

    private static final String TAG = "MediaActionSound";
    /**
     * The sound used by
     * {@link android.hardware.Camera#takePicture Camera.takePicture} to
     * indicate still image capture.
     * @see #play
     */
    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 media or camera APIs.
     * @see #play
     */
    public static final int FOCUS_COMPLETE        = 1;

    /**
     * The sound used by
     * {@link android.media.MediaRecorder#start MediaRecorder.start()} to
     * indicate the start of video recording.
     * @see #play
     */
    public static final int START_VIDEO_RECORDING = 2;

    /**
     * The sound used by
     * {@link android.media.MediaRecorder#stop MediaRecorder.stop()} to
     * indicate the end of video recording.
     * @see #play
     */
    public static final int STOP_VIDEO_RECORDING  = 3;

    private static final int SOUND_NOT_LOADED = -1;

    /**
     * Construct a new MediaActionSound instance. Only a single instance is
     * needed for playing any platform media action sound; you do not need a
     * separate instance for each sound type.
     */
    public MediaActionSound() {
        mSoundPool = new SoundPool(NUM_MEDIA_SOUND_STREAMS,
                AudioManager.STREAM_SYSTEM_ENFORCED, 0);
        mSoundPool.setOnLoadCompleteListener(mLoadCompleteListener);
        mSoundIds = new int[SOUND_FILES.length];
        for (int i = 0; i < mSoundIds.length; i++) {
            mSoundIds[i] = SOUND_NOT_LOADED;
        }
        mSoundIdToPlay = SOUND_NOT_LOADED;
    }

    /**
     * Preload a predefined platform sound to minimize latency when the sound is
     * played later by {@link #play}.
     * @param soundName The type of sound to preload, selected from
     *         SHUTTER_CLICK, FOCUS_COMPLETE, START_VIDEO_RECORDING, or
     *         STOP_VIDEO_RECORDING.
     * @see #play
     * @see #SHUTTER_CLICK
     * @see #FOCUS_COMPLETE
     * @see #START_VIDEO_RECORDING
     * @see #STOP_VIDEO_RECORDING
     */
    public synchronized void load(int soundName) {
        if (soundName < 0 || soundName >= SOUND_FILES.length) {
            throw new RuntimeException("Unknown sound requested: " + soundName);
        }
        if (mSoundIds[soundName] == SOUND_NOT_LOADED) {
            mSoundIds[soundName] =
                    mSoundPool.load(SOUND_FILES[soundName], 1);
        }
    }

    /**
     * <p>Play one of the predefined platform sounds for media actions.</p>
     *
     * <p>Use this method to play a platform-specific sound for various media
     * actions. The sound playback is done asynchronously, with the same
     * behavior and content as the sounds played by
     * {@link android.hardware.Camera#takePicture Camera.takePicture},
     * {@link android.media.MediaRecorder#start MediaRecorder.start}, and
     * {@link android.media.MediaRecorder#stop MediaRecorder.stop}.</p>
     *
     * <p>Using this method makes it easy to match the default device sounds
     * when recording or capturing data through the preview callbacks, or when
     * implementing custom camera-like features in your
     * application.</p>
     *
     * <p>If the sound has not been loaded by {@link #load} before calling play,
     * play will load the sound at the cost of some additional latency before
     * sound playback begins. </p>
     *
     * @param soundName The type of sound to play, selected from
     *         SHUTTER_CLICK, FOCUS_COMPLETE, START_VIDEO_RECORDING, or
     *         STOP_VIDEO_RECORDING.
     * @see android.hardware.Camera#takePicture
     * @see android.media.MediaRecorder
     * @see #SHUTTER_CLICK
     * @see #FOCUS_COMPLETE
     * @see #START_VIDEO_RECORDING
     * @see #STOP_VIDEO_RECORDING
     */
    public synchronized void play(int soundName) {
        if (soundName < 0 || soundName >= SOUND_FILES.length) {
            throw new RuntimeException("Unknown sound requested: " + soundName);
        }
        if (mSoundIds[soundName] == SOUND_NOT_LOADED) {
            mSoundIdToPlay =
                    mSoundPool.load(SOUND_FILES[soundName], 1);
            mSoundIds[soundName] = mSoundIdToPlay;
        } else {
            mSoundPool.play(mSoundIds[soundName], 1.0f, 1.0f, 0, 0, 1.0f);
        }
    }

    private SoundPool.OnLoadCompleteListener mLoadCompleteListener =
            new SoundPool.OnLoadCompleteListener() {
        public void onLoadComplete(SoundPool soundPool,
                int sampleId, int status) {
            if (status == 0) {
                if (mSoundIdToPlay == sampleId) {
                    soundPool.play(sampleId, 1.0f, 1.0f, 0, 0, 1.0f);
                    mSoundIdToPlay = SOUND_NOT_LOADED;
                }
            } else {
                Log.e(TAG, "Unable to load sound for playback (status: " +
                        status + ")");
            }
        }
    };

    /**
     * Free up all audio resources used by this MediaActionSound instance
     */
    public void release() {
        mSoundPool.release();
        mSoundPool = null;
    }
}
+5 −4
Original line number Diff line number Diff line
@@ -34,7 +34,7 @@ import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.PixelFormat;
import android.graphics.PointF;
import android.hardware.CameraSound;
import android.media.MediaActionSound;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Environment;
@@ -256,7 +256,7 @@ class GlobalScreenshot {
    private float mBgPadding;
    private float mBgPaddingScale;

    private CameraSound mCameraSound;
    private MediaActionSound mCameraSound;


    /**
@@ -309,7 +309,8 @@ class GlobalScreenshot {
        mBgPaddingScale = mBgPadding /  mDisplayMetrics.widthPixels;

        // Setup the Camera shutter sound
        mCameraSound = new CameraSound();
        mCameraSound = new MediaActionSound();
        mCameraSound.load(MediaActionSound.SHUTTER_CLICK);
    }

    /**
@@ -422,7 +423,7 @@ class GlobalScreenshot {
            @Override
            public void run() {
                // Play the shutter sound to notify that we've taken a screenshot
                mCameraSound.playSound(CameraSound.SHUTTER_CLICK);
                mCameraSound.play(MediaActionSound.SHUTTER_CLICK);

                mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
                mScreenshotView.buildLayer();