auto import from //depot/cupcake/@135843
diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java
new file mode 100644
index 0000000..a573983
--- /dev/null
+++ b/core/java/android/view/VolumePanel.java
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 2007 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.view;
+
+import android.bluetooth.HeadsetBase;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.media.AudioManager;
+import android.media.AudioService;
+import android.media.AudioSystem;
+import android.media.ToneGenerator;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Vibrator;
+import android.util.Config;
+import android.util.Log;
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import android.widget.Toast;
+
+/**
+ * Handle the volume up and down keys.
+ *
+ * This code really should be moved elsewhere.
+ *
+ * @hide
+ */
+public class VolumePanel extends Handler
+{
+    private static final String TAG = "VolumePanel";
+    private static boolean LOGD = false || Config.LOGD;
+
+    /**
+     * The delay before playing a sound. This small period exists so the user
+     * can press another key (non-volume keys, too) to have it NOT be audible.
+     * <p>
+     * PhoneWindow will implement this part.
+     */
+    public static final int PLAY_SOUND_DELAY = 300;
+
+    /**
+     * The delay before vibrating. This small period exists so if the user is
+     * moving to silent mode, it will not emit a short vibrate (it normally
+     * would since vibrate is between normal mode and silent mode using hardware
+     * keys).
+     */
+    public static final int VIBRATE_DELAY = 300;
+
+    private static final int VIBRATE_DURATION = 300;
+    private static final int BEEP_DURATION = 150;
+    private static final int MAX_VOLUME = 100;
+    private static final int FREE_DELAY = 10000;
+
+    private static final int MSG_VOLUME_CHANGED = 0;
+    private static final int MSG_FREE_RESOURCES = 1;
+    private static final int MSG_PLAY_SOUND = 2;
+    private static final int MSG_STOP_SOUNDS = 3;
+    private static final int MSG_VIBRATE = 4;
+
+    private static final int RINGTONE_VOLUME_TEXT = com.android.internal.R.string.volume_ringtone;
+    private static final int MUSIC_VOLUME_TEXT = com.android.internal.R.string.volume_music;
+    private static final int INCALL_VOLUME_TEXT = com.android.internal.R.string.volume_call;
+    private static final int ALARM_VOLUME_TEXT = com.android.internal.R.string.volume_alarm;
+    private static final int UNKNOWN_VOLUME_TEXT = com.android.internal.R.string.volume_unknown;
+    private static final int NOTIFICATION_VOLUME_TEXT =
+            com.android.internal.R.string.volume_notification;
+    private static final int BLUETOOTH_INCALL_VOLUME_TEXT =
+            com.android.internal.R.string.volume_bluetooth_call;
+
+    protected Context mContext;
+    private AudioManager mAudioManager;
+    protected AudioService mAudioService;
+
+    private final Toast mToast;
+    private final View mView;
+    private final TextView mMessage;
+    private final TextView mAdditionalMessage;
+    private final ImageView mSmallStreamIcon;
+    private final ImageView mLargeStreamIcon;
+    private final ProgressBar mLevel;
+
+    // Synchronize when accessing this
+    private ToneGenerator mToneGenerators[];
+    private Vibrator mVibrator;
+
+    public VolumePanel(Context context, AudioService volumeService) {
+        mContext = context;
+        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        mAudioService = volumeService;
+        mToast = new Toast(context);
+
+        LayoutInflater inflater = (LayoutInflater) context
+                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        View view = mView = inflater.inflate(com.android.internal.R.layout.volume_adjust, null);
+        mMessage = (TextView) view.findViewById(com.android.internal.R.id.message);
+        mAdditionalMessage =
+                (TextView) view.findViewById(com.android.internal.R.id.additional_message);
+        mSmallStreamIcon = (ImageView) view.findViewById(com.android.internal.R.id.other_stream_icon);
+        mLargeStreamIcon = (ImageView) view.findViewById(com.android.internal.R.id.ringer_stream_icon);
+        mLevel = (ProgressBar) view.findViewById(com.android.internal.R.id.level);
+
+        mToneGenerators = new ToneGenerator[AudioSystem.getNumStreamTypes()];
+        mVibrator = new Vibrator();
+    }
+
+    public void postVolumeChanged(int streamType, int flags) {
+        if (hasMessages(MSG_VOLUME_CHANGED)) return;
+        removeMessages(MSG_FREE_RESOURCES);
+        obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget();
+    }
+
+    /**
+     * Override this if you have other work to do when the volume changes (for
+     * example, vibrating, playing a sound, etc.). Make sure to call through to
+     * the superclass implementation.
+     */
+    protected void onVolumeChanged(int streamType, int flags) {
+
+        if (LOGD) Log.d(TAG, "onVolumeChanged(streamType: " + streamType + ", flags: " + flags + ")");
+
+        if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
+            onShowVolumeChanged(streamType, flags);
+        }
+
+        if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0) {
+            removeMessages(MSG_PLAY_SOUND);
+            sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);
+        }
+
+        if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) {
+            removeMessages(MSG_PLAY_SOUND);
+            removeMessages(MSG_VIBRATE);
+            onStopSounds();
+        }
+
+        removeMessages(MSG_FREE_RESOURCES);
+        sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
+    }
+
+    protected void onShowVolumeChanged(int streamType, int flags) {
+        int index = mAudioService.getStreamVolume(streamType);
+        int message = UNKNOWN_VOLUME_TEXT;
+        int additionalMessage = 0;
+
+        if (LOGD) {
+            Log.d(TAG, "onShowVolumeChanged(streamType: " + streamType
+                    + ", flags: " + flags + "), index: " + index);
+        }
+
+        // get max volume for progress bar
+        int max = mAudioService.getStreamMaxVolume(streamType);
+
+        switch (streamType) {
+
+            case AudioManager.STREAM_RING: {
+                message = RINGTONE_VOLUME_TEXT;
+                setRingerIcon(index);
+                break;
+            }
+
+            case AudioManager.STREAM_MUSIC: {
+                message = MUSIC_VOLUME_TEXT;
+                if (mAudioManager.isBluetoothA2dpOn()) {
+                    additionalMessage =
+                        com.android.internal.R.string.volume_music_hint_playing_through_bluetooth;
+                    setLargeIcon(com.android.internal.R.drawable.ic_volume_bluetooth_ad2p);
+                } else {
+                    setSmallIcon(index);
+                }
+                break;
+            }
+
+            case AudioManager.STREAM_VOICE_CALL: {
+                /*
+                 * For in-call voice call volume, there is no inaudible volume.
+                 * Rescale the UI control so the progress bar doesn't go all
+                 * the way to zero and don't show the mute icon.
+                 */
+                index++;
+                max++;
+                message = INCALL_VOLUME_TEXT;
+                setSmallIcon(index);
+                break;
+            }
+
+            case AudioManager.STREAM_ALARM: {
+                message = ALARM_VOLUME_TEXT;
+                setSmallIcon(index);
+                break;
+            }
+
+            case AudioManager.STREAM_NOTIFICATION: {
+                message = NOTIFICATION_VOLUME_TEXT;
+                setSmallIcon(index);
+                break;
+            }
+
+            case AudioManager.STREAM_BLUETOOTH_SCO: {
+                /*
+                 * For in-call voice call volume, there is no inaudible volume.
+                 * Rescale the UI control so the progress bar doesn't go all
+                 * the way to zero and don't show the mute icon.
+                 */
+                index++;
+                max++;
+                message = BLUETOOTH_INCALL_VOLUME_TEXT;
+                setLargeIcon(com.android.internal.R.drawable.ic_volume_bluetooth_in_call);
+                break;
+            }
+        }
+
+        String messageString = Resources.getSystem().getString(message);
+        if (!mMessage.getText().equals(messageString)) {
+            mMessage.setText(messageString);
+        }
+
+        if (additionalMessage == 0) {
+            mAdditionalMessage.setVisibility(View.GONE);
+        } else {
+            mAdditionalMessage.setVisibility(View.VISIBLE);
+            mAdditionalMessage.setText(Resources.getSystem().getString(additionalMessage));
+        }
+
+        if (max != mLevel.getMax()) {
+            mLevel.setMax(max);
+        }
+        mLevel.setProgress(index);
+
+        mToast.setView(mView);
+        mToast.setDuration(Toast.LENGTH_SHORT);
+        mToast.setGravity(Gravity.TOP, 0, 0);
+        mToast.show();
+
+        // Do a little vibrate if applicable (only when going into vibrate mode)
+        if ((flags & AudioManager.FLAG_VIBRATE) != 0 &&
+                mAudioService.isStreamAffectedByRingerMode(streamType) &&
+                mAudioService.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE &&
+                mAudioService.shouldVibrate(AudioManager.VIBRATE_TYPE_RINGER)) {
+            sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY);
+        }
+
+    }
+
+    protected void onPlaySound(int streamType, int flags) {
+
+        if (hasMessages(MSG_STOP_SOUNDS)) {
+            removeMessages(MSG_STOP_SOUNDS);
+            // Force stop right now
+            onStopSounds();
+        }
+
+        synchronized (this) {
+            ToneGenerator toneGen = getOrCreateToneGenerator(streamType);
+            toneGen.startTone(ToneGenerator.TONE_PROP_BEEP);
+        }
+
+        sendMessageDelayed(obtainMessage(MSG_STOP_SOUNDS), BEEP_DURATION);
+    }
+
+    protected void onStopSounds() {
+
+        synchronized (this) {
+            int numStreamTypes = AudioSystem.getNumStreamTypes();
+            for (int i = numStreamTypes - 1; i >= 0; i--) {
+                ToneGenerator toneGen = mToneGenerators[i];
+                if (toneGen != null) {
+                    toneGen.stopTone();
+                }
+            }
+        }
+    }
+
+    protected void onVibrate() {
+
+        // Make sure we ended up in vibrate ringer mode
+        if (mAudioService.getRingerMode() != AudioManager.RINGER_MODE_VIBRATE) {
+            return;
+        }
+
+        mVibrator.vibrate(VIBRATE_DURATION);
+    }
+
+    /**
+     * Lock on this VolumePanel instance as long as you use the returned ToneGenerator.
+     */
+    private ToneGenerator getOrCreateToneGenerator(int streamType) {
+        synchronized (this) {
+            if (mToneGenerators[streamType] == null) {
+                return mToneGenerators[streamType] = new ToneGenerator(streamType, MAX_VOLUME);
+            } else {
+                return mToneGenerators[streamType];
+            }
+        }
+    }
+
+    /**
+     * Makes the small icon visible, and hides the large icon.
+     *
+     * @param index The volume index, where 0 means muted.
+     */
+    private void setSmallIcon(int index) {
+        mLargeStreamIcon.setVisibility(View.GONE);
+        mSmallStreamIcon.setVisibility(View.VISIBLE);
+
+        mSmallStreamIcon.setImageResource(index == 0
+                ? com.android.internal.R.drawable.ic_volume_off_small
+                : com.android.internal.R.drawable.ic_volume_small);
+    }
+
+    /**
+     * Makes the large image view visible with the given icon.
+     *
+     * @param resId The icon to display.
+     */
+    private void setLargeIcon(int resId) {
+        mSmallStreamIcon.setVisibility(View.GONE);
+        mLargeStreamIcon.setVisibility(View.VISIBLE);
+        mLargeStreamIcon.setImageResource(resId);
+    }
+
+    /**
+     * Makes the ringer icon visible with an icon that is chosen
+     * based on the current ringer mode.
+     *
+     * @param index
+     */
+    private void setRingerIcon(int index) {
+        mSmallStreamIcon.setVisibility(View.GONE);
+        mLargeStreamIcon.setVisibility(View.VISIBLE);
+
+        int ringerMode = mAudioService.getRingerMode();
+        int icon;
+
+        if (LOGD) Log.d(TAG, "setRingerIcon(index: " + index+ "), ringerMode: " + ringerMode);
+
+        if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
+            icon = com.android.internal.R.drawable.ic_volume_off;
+        } else if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
+            icon = com.android.internal.R.drawable.ic_vibrate;
+        } else {
+            icon = com.android.internal.R.drawable.ic_volume;
+        }
+        mLargeStreamIcon.setImageResource(icon);
+    }
+
+    protected void onFreeResources() {
+        // We'll keep the views, just ditch the cached drawable and hence
+        // bitmaps
+        mSmallStreamIcon.setImageDrawable(null);
+        mLargeStreamIcon.setImageDrawable(null);
+
+        synchronized (this) {
+            for (int i = mToneGenerators.length - 1; i >= 0; i--) {
+                if (mToneGenerators[i] != null) {
+                    mToneGenerators[i].release();
+                }
+                mToneGenerators[i] = null;
+            }
+        }
+    }
+
+    @Override
+    public void handleMessage(Message msg) {
+        switch (msg.what) {
+
+            case MSG_VOLUME_CHANGED: {
+                onVolumeChanged(msg.arg1, msg.arg2);
+                break;
+            }
+
+            case MSG_FREE_RESOURCES: {
+                onFreeResources();
+                break;
+            }
+
+            case MSG_STOP_SOUNDS: {
+                onStopSounds();
+                break;
+            }
+
+            case MSG_PLAY_SOUND: {
+                onPlaySound(msg.arg1, msg.arg2);
+                break;
+            }
+
+            case MSG_VIBRATE: {
+                onVibrate();
+                break;
+            }
+
+        }
+    }
+
+}