summaryrefslogtreecommitdiffstats
path: root/core/java/android/view/VolumePanel.java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android/view/VolumePanel.java')
-rw-r--r--core/java/android/view/VolumePanel.java341
1 files changed, 341 insertions, 0 deletions
diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java
new file mode 100644
index 0000000..24f4853
--- /dev/null
+++ b/core/java/android/view/VolumePanel.java
@@ -0,0 +1,341 @@
+/*
+ * 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.media.ToneGenerator;
+import android.media.AudioManager;
+import android.media.AudioService;
+import android.content.Context;
+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 final String RINGTONE_VOLUME_TEXT;
+ private final String MUSIC_VOLUME_TEXT;
+ private final String INCALL_VOLUME_TEXT;
+ private final String ALARM_VOLUME_TEXT;
+ private final String UNKNOWN_VOLUME_TEXT;
+
+ protected Context mContext;
+ protected AudioService mAudioService;
+
+ private Toast mToast;
+ private View mView;
+ private TextView mMessage;
+ private ImageView mOtherStreamIcon;
+ private ImageView mRingerStreamIcon;
+ private ProgressBar mLevel;
+
+ // Synchronize when accessing this
+ private ToneGenerator mToneGenerators[];
+ private Vibrator mVibrator;
+
+ public VolumePanel(Context context, AudioService volumeService) {
+ mContext = context;
+ mAudioService = volumeService;
+ mToast = new Toast(context);
+
+ RINGTONE_VOLUME_TEXT = context.getString(com.android.internal.R.string.volume_ringtone);
+ MUSIC_VOLUME_TEXT = context.getString(com.android.internal.R.string.volume_music);
+ INCALL_VOLUME_TEXT = context.getString(com.android.internal.R.string.volume_call);
+ ALARM_VOLUME_TEXT = context.getString(com.android.internal.R.string.volume_alarm);
+ UNKNOWN_VOLUME_TEXT = context.getString(com.android.internal.R.string.volume_unknown);
+
+ 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);
+ mOtherStreamIcon = (ImageView) view.findViewById(com.android.internal.R.id.other_stream_icon);
+ mRingerStreamIcon = (ImageView) view.findViewById(com.android.internal.R.id.ringer_stream_icon);
+ mLevel = (ProgressBar) view.findViewById(com.android.internal.R.id.level);
+
+ mToneGenerators = new ToneGenerator[AudioManager.NUM_STREAMS];
+ 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);
+ String message = UNKNOWN_VOLUME_TEXT;
+
+ if (LOGD) {
+ Log.d(TAG, "onShowVolumeChanged(streamType: " + streamType
+ + ", flags: " + flags + "), index: " + index);
+ }
+
+ switch (streamType) {
+
+ case AudioManager.STREAM_RING: {
+ message = RINGTONE_VOLUME_TEXT;
+ setRingerIcon(index);
+ break;
+ }
+
+ case AudioManager.STREAM_MUSIC: {
+ message = MUSIC_VOLUME_TEXT;
+ setOtherIcon(index);
+ break;
+ }
+
+ case AudioManager.STREAM_VOICE_CALL: {
+ message = INCALL_VOLUME_TEXT;
+ /*
+ * For in-call voice call volume, there is no inaudible volume
+ * level, so never show the mute icon
+ */
+ setOtherIcon(index == 0 ? 1 : index);
+ break;
+ }
+
+ case AudioManager.STREAM_ALARM: {
+ message = ALARM_VOLUME_TEXT;
+ setOtherIcon(index);
+ break;
+ }
+ }
+
+ if (!mMessage.getText().equals(message)) {
+ mMessage.setText(message);
+ }
+
+ int max = mAudioService.getStreamMaxVolume(streamType);
+ 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) {
+ for (int i = AudioManager.NUM_STREAMS - 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];
+ }
+ }
+ }
+
+ private void setOtherIcon(int index) {
+ mRingerStreamIcon.setVisibility(View.GONE);
+ mOtherStreamIcon.setVisibility(View.VISIBLE);
+
+ mOtherStreamIcon.setImageResource(index == 0
+ ? com.android.internal.R.drawable.ic_volume_off_small
+ : com.android.internal.R.drawable.ic_volume_small);
+ }
+
+ private void setRingerIcon(int index) {
+ mOtherStreamIcon.setVisibility(View.GONE);
+ mRingerStreamIcon.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;
+ }
+ mRingerStreamIcon.setImageResource(icon);
+ }
+
+ protected void onFreeResources() {
+ // We'll keep the views, just ditch the cached drawable and hence
+ // bitmaps
+ mOtherStreamIcon.setImageDrawable(null);
+ mRingerStreamIcon.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;
+ }
+
+ }
+ }
+
+}