blob: 8657e07823cce3b3f85dfbe3f5673ce4191188da [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
John Spurlock3346a802014-05-20 16:25:37 -040017package com.android.systemui.volume;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080018
Amith Yamasani2bbdd772011-02-02 18:54:13 -080019import com.android.internal.R;
20
Eric Laurentc34dcc12012-09-10 13:51:52 -070021import android.app.AlertDialog;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080022import android.app.Dialog;
23import android.content.DialogInterface.OnDismissListener;
24import android.content.BroadcastReceiver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.content.Context;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080026import android.content.DialogInterface;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.content.Intent;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080028import android.content.IntentFilter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.content.res.Resources;
30import android.media.AudioManager;
31import android.media.AudioService;
32import android.media.AudioSystem;
Marco Nelissen69f593c2009-07-28 09:55:04 -070033import android.media.RingtoneManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.media.ToneGenerator;
Marco Nelissen69f593c2009-07-28 09:55:04 -070035import android.net.Uri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.os.Handler;
37import android.os.Message;
38import android.os.Vibrator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.util.Log;
John Spurlock3346a802014-05-20 16:25:37 -040040import android.view.Gravity;
41import android.view.LayoutInflater;
42import android.view.MotionEvent;
43import android.view.View;
44import android.view.ViewGroup;
45import android.view.Window;
46import android.view.WindowManager;
Amith Yamasani284e6302011-09-16 18:24:47 -070047import android.view.WindowManager.LayoutParams;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048import android.widget.ImageView;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080049import android.widget.SeekBar;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080050import android.widget.SeekBar.OnSeekBarChangeListener;
51
52import java.util.HashMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053
54/**
John Spurlock3346a802014-05-20 16:25:37 -040055 * Handles the user interface for the volume keys.
Dianne Hackborne8ecde12011-08-03 18:55:19 -070056 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057 * @hide
58 */
John Spurlock3346a802014-05-20 16:25:37 -040059public class VolumePanel extends Handler {
Alan Viverettee8ebaf32014-04-11 15:44:15 -070060 private static final String TAG = VolumePanel.class.getSimpleName();
Marco Nelissen69f593c2009-07-28 09:55:04 -070061 private static boolean LOGD = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062
John Spurlock3346a802014-05-20 16:25:37 -040063 private static final int PLAY_SOUND_DELAY = AudioService.PLAY_SOUND_DELAY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064
65 /**
66 * The delay before vibrating. This small period exists so if the user is
67 * moving to silent mode, it will not emit a short vibrate (it normally
68 * would since vibrate is between normal mode and silent mode using hardware
69 * keys).
70 */
71 public static final int VIBRATE_DELAY = 300;
72
73 private static final int VIBRATE_DURATION = 300;
74 private static final int BEEP_DURATION = 150;
75 private static final int MAX_VOLUME = 100;
76 private static final int FREE_DELAY = 10000;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080077 private static final int TIMEOUT_DELAY = 3000;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078
79 private static final int MSG_VOLUME_CHANGED = 0;
80 private static final int MSG_FREE_RESOURCES = 1;
81 private static final int MSG_PLAY_SOUND = 2;
82 private static final int MSG_STOP_SOUNDS = 3;
83 private static final int MSG_VIBRATE = 4;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080084 private static final int MSG_TIMEOUT = 5;
85 private static final int MSG_RINGER_MODE_CHANGED = 6;
Mike Lockwoodce952c82011-11-14 10:47:42 -080086 private static final int MSG_MUTE_CHANGED = 7;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -070087 private static final int MSG_REMOTE_VOLUME_CHANGED = 8;
88 private static final int MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN = 9;
89 private static final int MSG_SLIDER_VISIBILITY_CHANGED = 10;
Eric Laurentc34dcc12012-09-10 13:51:52 -070090 private static final int MSG_DISPLAY_SAFE_VOLUME_WARNING = 11;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080091
Mike Lockwood8dc1dab2011-10-27 09:52:41 -040092 // Pseudo stream type for master volume
Mike Lockwood47676902011-11-08 10:31:21 -080093 private static final int STREAM_MASTER = -100;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -070094 // Pseudo stream type for remote volume is defined in AudioService.STREAM_REMOTE_MUSIC
Mike Lockwood8dc1dab2011-10-27 09:52:41 -040095
John Spurlock3346a802014-05-20 16:25:37 -040096 protected final Context mContext;
97 private final AudioManager mAudioManager;
Marco Nelissen69f593c2009-07-28 09:55:04 -070098 private boolean mRingIsSilent;
Amith Yamasani42722bf2011-07-22 10:34:27 -070099 private boolean mShowCombinedVolumes;
Amith Yamasani71def772011-10-12 12:25:24 -0700100 private boolean mVoiceCapable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101
Christopher Tatec4b78d22012-05-22 13:57:58 -0700102 // True if we want to play tones on the system stream when the master stream is specified.
103 private final boolean mPlayMasterStreamTones;
104
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800105 /** Dialog containing all the sliders */
106 private final Dialog mDialog;
107 /** Dialog's content view */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800108 private final View mView;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800109
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -0700110 /** The visible portion of the volume overlay */
111 private final ViewGroup mPanel;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800112 /** Contains the sliders and their touchable icons */
113 private final ViewGroup mSliderGroup;
114 /** The button that expands the dialog to show all sliders */
115 private final View mMoreButton;
116 /** Dummy divider icon that needs to vanish with the more button */
117 private final View mDivider;
118
119 /** Currently active stream that shows up at the top of the list of sliders */
120 private int mActiveStreamType = -1;
121 /** All the slider controls mapped by stream type */
122 private HashMap<Integer,StreamControl> mStreamControls;
123
Amith Yamasani71def772011-10-12 12:25:24 -0700124 private enum StreamResources {
125 BluetoothSCOStream(AudioManager.STREAM_BLUETOOTH_SCO,
126 R.string.volume_icon_description_bluetooth,
127 R.drawable.ic_audio_bt,
128 R.drawable.ic_audio_bt,
129 false),
130 RingerStream(AudioManager.STREAM_RING,
131 R.string.volume_icon_description_ringer,
132 R.drawable.ic_audio_ring_notif,
133 R.drawable.ic_audio_ring_notif_mute,
134 false),
135 VoiceStream(AudioManager.STREAM_VOICE_CALL,
136 R.string.volume_icon_description_incall,
137 R.drawable.ic_audio_phone,
138 R.drawable.ic_audio_phone,
139 false),
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700140 AlarmStream(AudioManager.STREAM_ALARM,
141 R.string.volume_alarm,
142 R.drawable.ic_audio_alarm,
143 R.drawable.ic_audio_alarm_mute,
144 false),
Amith Yamasani71def772011-10-12 12:25:24 -0700145 MediaStream(AudioManager.STREAM_MUSIC,
146 R.string.volume_icon_description_media,
147 R.drawable.ic_audio_vol,
148 R.drawable.ic_audio_vol_mute,
149 true),
150 NotificationStream(AudioManager.STREAM_NOTIFICATION,
151 R.string.volume_icon_description_notification,
152 R.drawable.ic_audio_notification,
153 R.drawable.ic_audio_notification_mute,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400154 true),
155 // for now, use media resources for master volume
156 MasterStream(STREAM_MASTER,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700157 R.string.volume_icon_description_media, //FIXME should have its own description
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400158 R.drawable.ic_audio_vol,
159 R.drawable.ic_audio_vol_mute,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700160 false),
161 RemoteStream(AudioService.STREAM_REMOTE_MUSIC,
162 R.string.volume_icon_description_media, //FIXME should have its own description
163 R.drawable.ic_media_route_on_holo_dark,
164 R.drawable.ic_media_route_disabled_holo_dark,
165 false);// will be dynamically updated
Amith Yamasani71def772011-10-12 12:25:24 -0700166
167 int streamType;
168 int descRes;
169 int iconRes;
170 int iconMuteRes;
171 // RING, VOICE_CALL & BLUETOOTH_SCO are hidden unless explicitly requested
172 boolean show;
173
174 StreamResources(int streamType, int descRes, int iconRes, int iconMuteRes, boolean show) {
175 this.streamType = streamType;
176 this.descRes = descRes;
177 this.iconRes = iconRes;
178 this.iconMuteRes = iconMuteRes;
179 this.show = show;
180 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700181 }
Amith Yamasani71def772011-10-12 12:25:24 -0700182
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800183 // List of stream types and their order
Amith Yamasani71def772011-10-12 12:25:24 -0700184 private static final StreamResources[] STREAMS = {
185 StreamResources.BluetoothSCOStream,
186 StreamResources.RingerStream,
187 StreamResources.VoiceStream,
188 StreamResources.MediaStream,
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700189 StreamResources.NotificationStream,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400190 StreamResources.AlarmStream,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700191 StreamResources.MasterStream,
192 StreamResources.RemoteStream
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800193 };
194
195 /** Object that contains data for each slider */
196 private class StreamControl {
197 int streamType;
198 ViewGroup group;
199 ImageView icon;
200 SeekBar seekbarView;
201 int iconRes;
202 int iconMuteRes;
203 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204
205 // Synchronize when accessing this
206 private ToneGenerator mToneGenerators[];
207 private Vibrator mVibrator;
208
Eric Laurentc34dcc12012-09-10 13:51:52 -0700209 private static AlertDialog sConfirmSafeVolumeDialog;
Eric Laurent0516a9e2012-09-19 11:53:03 -0700210 private static Object sConfirmSafeVolumeLock = new Object();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700211
212 private static class WarningDialogReceiver extends BroadcastReceiver
213 implements DialogInterface.OnDismissListener {
Eric Laurentfde16d52012-12-03 14:42:39 -0800214 private final Context mContext;
215 private final Dialog mDialog;
216 private final VolumePanel mVolumePanel;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700217
Eric Laurentfde16d52012-12-03 14:42:39 -0800218 WarningDialogReceiver(Context context, Dialog dialog, VolumePanel volumePanel) {
Eric Laurentc34dcc12012-09-10 13:51:52 -0700219 mContext = context;
220 mDialog = dialog;
Eric Laurentfde16d52012-12-03 14:42:39 -0800221 mVolumePanel = volumePanel;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700222 IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
223 context.registerReceiver(this, filter);
224 }
225
226 @Override
227 public void onReceive(Context context, Intent intent) {
228 mDialog.cancel();
Eric Laurentfde16d52012-12-03 14:42:39 -0800229 cleanUp();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700230 }
231
Alan Viverette494fb7b2014-04-10 18:12:56 -0700232 @Override
Eric Laurentc34dcc12012-09-10 13:51:52 -0700233 public void onDismiss(DialogInterface unused) {
234 mContext.unregisterReceiver(this);
Eric Laurentfde16d52012-12-03 14:42:39 -0800235 cleanUp();
236 }
237
238 private void cleanUp() {
Eric Laurent0516a9e2012-09-19 11:53:03 -0700239 synchronized (sConfirmSafeVolumeLock) {
240 sConfirmSafeVolumeDialog = null;
241 }
Eric Laurentfde16d52012-12-03 14:42:39 -0800242 mVolumePanel.forceTimeout();
243 mVolumePanel.updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700244 }
245 }
246
247
John Spurlock3346a802014-05-20 16:25:37 -0400248 public VolumePanel(Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800249 mContext = context;
250 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800251
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400252 // For now, only show master volume if master volume is supported
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700253 final Resources res = context.getResources();
254 final boolean useMasterVolume = res.getBoolean(R.bool.config_useMasterVolume);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400255 if (useMasterVolume) {
256 for (int i = 0; i < STREAMS.length; i++) {
257 StreamResources streamRes = STREAMS[i];
258 streamRes.show = (streamRes.streamType == STREAM_MASTER);
259 }
260 }
261
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700262 mDialog = new Dialog(context) {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700263 @Override
Amith Yamasani284e6302011-09-16 18:24:47 -0700264 public boolean onTouchEvent(MotionEvent event) {
Eric Laurentfde16d52012-12-03 14:42:39 -0800265 if (isShowing() && event.getAction() == MotionEvent.ACTION_OUTSIDE &&
266 sConfirmSafeVolumeDialog == null) {
Amith Yamasani284e6302011-09-16 18:24:47 -0700267 forceTimeout();
268 return true;
269 }
270 return false;
271 }
272 };
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700273
274 // Change some window properties
275 final Window window = mDialog.getWindow();
276 final LayoutParams lp = window.getAttributes();
277 lp.token = null;
278 // Offset from the top
279 lp.y = res.getDimensionPixelOffset(R.dimen.volume_panel_top);
280 lp.type = LayoutParams.TYPE_VOLUME_OVERLAY;
281 lp.windowAnimations = R.style.Animation_VolumePanel;
282 window.setAttributes(lp);
283 window.setGravity(Gravity.TOP);
284 window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
285 window.requestFeature(Window.FEATURE_NO_TITLE);
286 window.addFlags(LayoutParams.FLAG_NOT_FOCUSABLE
287 | LayoutParams.FLAG_NOT_TOUCH_MODAL
288 | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
289
290 mDialog.setCanceledOnTouchOutside(true);
291 mDialog.setContentView(R.layout.volume_adjust);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800292 mDialog.setOnDismissListener(new OnDismissListener() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700293 @Override
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800294 public void onDismiss(DialogInterface dialog) {
295 mActiveStreamType = -1;
Eric Laurent402f7f22011-02-04 12:30:32 -0800296 mAudioManager.forceVolumeControlStream(mActiveStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800297 }
298 });
Alan Viverette494fb7b2014-04-10 18:12:56 -0700299
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700300 mDialog.create();
Alan Viverette494fb7b2014-04-10 18:12:56 -0700301
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700302 mView = window.findViewById(R.id.content);
303 mView.setOnTouchListener(new View.OnTouchListener() {
304 @Override
305 public boolean onTouch(View v, MotionEvent event) {
306 resetTimeout();
307 return false;
308 }
309 });
310
311 mPanel = (ViewGroup) mView.findViewById(R.id.visible_panel);
312 mSliderGroup = (ViewGroup) mView.findViewById(R.id.slider_group);
313 mMoreButton = mView.findViewById(R.id.expand_button);
314 mDivider = mView.findViewById(R.id.expand_button_divider);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800315
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800316 mToneGenerators = new ToneGenerator[AudioSystem.getNumStreamTypes()];
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700317 mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
Amith Yamasani71def772011-10-12 12:25:24 -0700318 mVoiceCapable = context.getResources().getBoolean(R.bool.config_voice_capable);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700319
320 // If we don't want to show multiple volumes, hide the settings button
321 // and divider.
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400322 mShowCombinedVolumes = !mVoiceCapable && !useMasterVolume;
Amith Yamasani42722bf2011-07-22 10:34:27 -0700323 if (!mShowCombinedVolumes) {
324 mMoreButton.setVisibility(View.GONE);
325 mDivider.setVisibility(View.GONE);
326 } else {
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700327 mMoreButton.setOnClickListener(mClickListener);
Amith Yamasani42722bf2011-07-22 10:34:27 -0700328 }
329
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700330 final boolean masterVolumeOnly = res.getBoolean(R.bool.config_useMasterVolume);
331 final boolean masterVolumeKeySounds = res.getBoolean(R.bool.config_useVolumeKeySounds);
Christopher Tatec4b78d22012-05-22 13:57:58 -0700332 mPlayMasterStreamTones = masterVolumeOnly && masterVolumeKeySounds;
333
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800334 listenToRingerMode();
335 }
336
Fabrice Di Meglio8c028842013-01-09 18:20:38 -0800337 public void setLayoutDirection(int layoutDirection) {
338 mPanel.setLayoutDirection(layoutDirection);
339 updateStates();
340 }
341
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800342 private void listenToRingerMode() {
343 final IntentFilter filter = new IntentFilter();
344 filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
345 mContext.registerReceiver(new BroadcastReceiver() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700346 @Override
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800347 public void onReceive(Context context, Intent intent) {
348 final String action = intent.getAction();
349
350 if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) {
351 removeMessages(MSG_RINGER_MODE_CHANGED);
352 sendMessage(obtainMessage(MSG_RINGER_MODE_CHANGED));
353 }
354 }
355 }, filter);
356 }
357
358 private boolean isMuted(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400359 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700360 return mAudioManager.isMasterMute();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700361 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
John Spurlock3346a802014-05-20 16:25:37 -0400362 return (mAudioManager.getRemoteStreamVolume() <= 0);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400363 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700364 return mAudioManager.isStreamMute(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400365 }
366 }
367
368 private int getStreamMaxVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400369 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700370 return mAudioManager.getMasterMaxVolume();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700371 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
John Spurlock3346a802014-05-20 16:25:37 -0400372 return mAudioManager.getRemoteStreamMaxVolume();
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400373 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700374 return mAudioManager.getStreamMaxVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400375 }
376 }
377
378 private int getStreamVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400379 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700380 return mAudioManager.getMasterVolume();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700381 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
John Spurlock3346a802014-05-20 16:25:37 -0400382 return mAudioManager.getRemoteStreamVolume();
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400383 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700384 return mAudioManager.getStreamVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400385 }
386 }
387
388 private void setStreamVolume(int streamType, int index, int flags) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400389 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700390 mAudioManager.setMasterVolume(index, flags);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700391 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
John Spurlock3346a802014-05-20 16:25:37 -0400392 mAudioManager.setRemoteStreamVolume(index);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400393 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700394 mAudioManager.setStreamVolume(streamType, index, flags);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400395 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800396 }
397
398 private void createSliders() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700399 final Resources res = mContext.getResources();
400 final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
401 Context.LAYOUT_INFLATER_SERVICE);
402
Amith Yamasani71def772011-10-12 12:25:24 -0700403 mStreamControls = new HashMap<Integer, StreamControl>(STREAMS.length);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700404
Amith Yamasani71def772011-10-12 12:25:24 -0700405 for (int i = 0; i < STREAMS.length; i++) {
406 StreamResources streamRes = STREAMS[i];
Alan Viverette494fb7b2014-04-10 18:12:56 -0700407
408 final int streamType = streamRes.streamType;
Amith Yamasani71def772011-10-12 12:25:24 -0700409 if (mVoiceCapable && streamRes == StreamResources.NotificationStream) {
410 streamRes = StreamResources.RingerStream;
411 }
Alan Viverette494fb7b2014-04-10 18:12:56 -0700412
413 final StreamControl sc = new StreamControl();
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700414 sc.streamType = streamType;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800415 sc.group = (ViewGroup) inflater.inflate(R.layout.volume_adjust_item, null);
416 sc.group.setTag(sc);
417 sc.icon = (ImageView) sc.group.findViewById(R.id.stream_icon);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800418 sc.icon.setTag(sc);
Amith Yamasani71def772011-10-12 12:25:24 -0700419 sc.icon.setContentDescription(res.getString(streamRes.descRes));
420 sc.iconRes = streamRes.iconRes;
421 sc.iconMuteRes = streamRes.iconMuteRes;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800422 sc.icon.setImageResource(sc.iconRes);
423 sc.seekbarView = (SeekBar) sc.group.findViewById(R.id.seekbar);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700424 final int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO ||
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700425 streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400426 sc.seekbarView.setMax(getStreamMaxVolume(streamType) + plusOne);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700427 sc.seekbarView.setOnSeekBarChangeListener(mSeekListener);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800428 sc.seekbarView.setTag(sc);
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700429 mStreamControls.put(streamType, sc);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800430 }
431 }
432
433 private void reorderSliders(int activeStreamType) {
434 mSliderGroup.removeAllViews();
435
Alan Viverette494fb7b2014-04-10 18:12:56 -0700436 final StreamControl active = mStreamControls.get(activeStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800437 if (active == null) {
438 Log.e("VolumePanel", "Missing stream type! - " + activeStreamType);
439 mActiveStreamType = -1;
440 } else {
441 mSliderGroup.addView(active.group);
442 mActiveStreamType = activeStreamType;
443 active.group.setVisibility(View.VISIBLE);
444 updateSlider(active);
445 }
446
Amith Yamasani42722bf2011-07-22 10:34:27 -0700447 addOtherVolumes();
448 }
449
450 private void addOtherVolumes() {
451 if (!mShowCombinedVolumes) return;
452
Amith Yamasani71def772011-10-12 12:25:24 -0700453 for (int i = 0; i < STREAMS.length; i++) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800454 // Skip the phone specific ones and the active one
Amith Yamasani71def772011-10-12 12:25:24 -0700455 final int streamType = STREAMS[i].streamType;
456 if (!STREAMS[i].show || streamType == mActiveStreamType) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800457 continue;
458 }
459 StreamControl sc = mStreamControls.get(streamType);
460 mSliderGroup.addView(sc.group);
461 updateSlider(sc);
462 }
463 }
464
465 /** Update the mute and progress state of a slider */
466 private void updateSlider(StreamControl sc) {
Eric Laurent8c787522012-05-14 14:09:43 -0700467 sc.seekbarView.setProgress(getStreamVolume(sc.streamType));
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800468 final boolean muted = isMuted(sc.streamType);
Fabrice Di Meglio8c028842013-01-09 18:20:38 -0800469 // Force reloading the image resource
470 sc.icon.setImageDrawable(null);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800471 sc.icon.setImageResource(muted ? sc.iconMuteRes : sc.iconRes);
Eric Laurent8a51aca2013-03-25 18:36:35 -0700472 if (((sc.streamType == AudioManager.STREAM_RING) ||
473 (sc.streamType == AudioManager.STREAM_NOTIFICATION)) &&
Eric Laurent8c787522012-05-14 14:09:43 -0700474 mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
Amith Yamasanic696a532011-10-28 17:02:37 -0700475 sc.icon.setImageResource(R.drawable.ic_audio_ring_notif_vibrate);
476 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700477 if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
478 // never disable touch interactions for remote playback, the muting is not tied to
479 // the state of the phone.
480 sc.seekbarView.setEnabled(true);
Eric Laurentfde16d52012-12-03 14:42:39 -0800481 } else if ((sc.streamType != mAudioManager.getMasterStreamType() && muted) ||
482 (sConfirmSafeVolumeDialog != null)) {
Eric Laurent8c787522012-05-14 14:09:43 -0700483 sc.seekbarView.setEnabled(false);
484 } else {
485 sc.seekbarView.setEnabled(true);
486 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800487 }
488
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800489 private void expand() {
490 final int count = mSliderGroup.getChildCount();
491 for (int i = 0; i < count; i++) {
492 mSliderGroup.getChildAt(i).setVisibility(View.VISIBLE);
493 }
494 mMoreButton.setVisibility(View.INVISIBLE);
495 mDivider.setVisibility(View.INVISIBLE);
496 }
497
498 private void collapse() {
499 mMoreButton.setVisibility(View.VISIBLE);
500 mDivider.setVisibility(View.VISIBLE);
501 final int count = mSliderGroup.getChildCount();
502 for (int i = 1; i < count; i++) {
503 mSliderGroup.getChildAt(i).setVisibility(View.GONE);
504 }
505 }
506
Eric Laurentfde16d52012-12-03 14:42:39 -0800507 public void updateStates() {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800508 final int count = mSliderGroup.getChildCount();
509 for (int i = 0; i < count; i++) {
510 StreamControl sc = (StreamControl) mSliderGroup.getChildAt(i).getTag();
511 updateSlider(sc);
512 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800513 }
514
515 public void postVolumeChanged(int streamType, int flags) {
516 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700517 synchronized (this) {
518 if (mStreamControls == null) {
519 createSliders();
520 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800521 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800522 removeMessages(MSG_FREE_RESOURCES);
523 obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget();
524 }
525
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700526 public void postRemoteVolumeChanged(int streamType, int flags) {
527 if (hasMessages(MSG_REMOTE_VOLUME_CHANGED)) return;
528 synchronized (this) {
529 if (mStreamControls == null) {
530 createSliders();
531 }
532 }
533 removeMessages(MSG_FREE_RESOURCES);
534 obtainMessage(MSG_REMOTE_VOLUME_CHANGED, streamType, flags).sendToTarget();
535 }
536
537 public void postRemoteSliderVisibility(boolean visible) {
538 obtainMessage(MSG_SLIDER_VISIBILITY_CHANGED,
539 AudioService.STREAM_REMOTE_MUSIC, visible ? 1 : 0).sendToTarget();
540 }
541
542 /**
543 * Called by AudioService when it has received new remote playback information that
544 * would affect the VolumePanel display (mainly volumes). The difference with
545 * {@link #postRemoteVolumeChanged(int, int)} is that the handling of the posted message
546 * (MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN) will only update the volume slider if it is being
547 * displayed.
548 * This special code path is due to the fact that remote volume updates arrive to AudioService
549 * asynchronously. So after AudioService has sent the volume update (which should be treated
550 * as a request to update the volume), the application will likely set a new volume. If the UI
551 * is still up, we need to refresh the display to show this new value.
552 */
553 public void postHasNewRemotePlaybackInfo() {
554 if (hasMessages(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN)) return;
555 // don't create or prevent resources to be freed, if they disappear, this update came too
556 // late and shouldn't warrant the panel to be displayed longer
557 obtainMessage(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN).sendToTarget();
558 }
559
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400560 public void postMasterVolumeChanged(int flags) {
561 postVolumeChanged(STREAM_MASTER, flags);
562 }
563
Mike Lockwoodce952c82011-11-14 10:47:42 -0800564 public void postMuteChanged(int streamType, int flags) {
565 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700566 synchronized (this) {
567 if (mStreamControls == null) {
568 createSliders();
569 }
Mike Lockwoodce952c82011-11-14 10:47:42 -0800570 }
571 removeMessages(MSG_FREE_RESOURCES);
572 obtainMessage(MSG_MUTE_CHANGED, streamType, flags).sendToTarget();
573 }
574
575 public void postMasterMuteChanged(int flags) {
576 postMuteChanged(STREAM_MASTER, flags);
577 }
578
Eric Laurentfde16d52012-12-03 14:42:39 -0800579 public void postDisplaySafeVolumeWarning(int flags) {
Eric Laurent0516a9e2012-09-19 11:53:03 -0700580 if (hasMessages(MSG_DISPLAY_SAFE_VOLUME_WARNING)) return;
Eric Laurentfde16d52012-12-03 14:42:39 -0800581 obtainMessage(MSG_DISPLAY_SAFE_VOLUME_WARNING, flags, 0).sendToTarget();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700582 }
583
John Spurlock3346a802014-05-20 16:25:37 -0400584 public void postDismiss() {
585 removeMessages(MSG_TIMEOUT);
586 sendEmptyMessage(MSG_TIMEOUT);
587 }
588
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800589 /**
590 * Override this if you have other work to do when the volume changes (for
591 * example, vibrating, playing a sound, etc.). Make sure to call through to
592 * the superclass implementation.
593 */
594 protected void onVolumeChanged(int streamType, int flags) {
595
596 if (LOGD) Log.d(TAG, "onVolumeChanged(streamType: " + streamType + ", flags: " + flags + ")");
597
598 if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
Amith Yamasania6549862012-05-30 17:29:28 -0700599 synchronized (this) {
600 if (mActiveStreamType != streamType) {
601 reorderSliders(streamType);
602 }
603 onShowVolumeChanged(streamType, flags);
Amith Yamasanie3361b82011-02-10 18:20:50 -0800604 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800605 }
606
Marco Nelissen69f593c2009-07-28 09:55:04 -0700607 if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800608 removeMessages(MSG_PLAY_SOUND);
609 sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);
610 }
611
612 if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) {
613 removeMessages(MSG_PLAY_SOUND);
614 removeMessages(MSG_VIBRATE);
615 onStopSounds();
616 }
617
618 removeMessages(MSG_FREE_RESOURCES);
619 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800620 resetTimeout();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800621 }
622
Mike Lockwoodce952c82011-11-14 10:47:42 -0800623 protected void onMuteChanged(int streamType, int flags) {
624
625 if (LOGD) Log.d(TAG, "onMuteChanged(streamType: " + streamType + ", flags: " + flags + ")");
626
627 StreamControl sc = mStreamControls.get(streamType);
628 if (sc != null) {
629 sc.icon.setImageResource(isMuted(sc.streamType) ? sc.iconMuteRes : sc.iconRes);
630 }
631
632 onVolumeChanged(streamType, flags);
633 }
634
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800635 protected void onShowVolumeChanged(int streamType, int flags) {
Eric Laurent8c787522012-05-14 14:09:43 -0700636 int index = getStreamVolume(streamType);
Eric Laurentd72d51c2011-02-03 18:47:47 -0800637
Marco Nelissen69f593c2009-07-28 09:55:04 -0700638 mRingIsSilent = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800639
640 if (LOGD) {
641 Log.d(TAG, "onShowVolumeChanged(streamType: " + streamType
642 + ", flags: " + flags + "), index: " + index);
643 }
644
645 // get max volume for progress bar
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800646
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400647 int max = getStreamMaxVolume(streamType);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800648
649 switch (streamType) {
650
651 case AudioManager.STREAM_RING: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800652// setRingerIcon();
Marco Nelissen69f593c2009-07-28 09:55:04 -0700653 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
654 mContext, RingtoneManager.TYPE_RINGTONE);
655 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700656 mRingIsSilent = true;
657 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800658 break;
659 }
660
661 case AudioManager.STREAM_MUSIC: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800662 // Special case for when Bluetooth is active for music
Glenn Kasten8b4b97a2011-02-04 13:54:26 -0800663 if ((mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC) &
664 (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP |
665 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
666 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800667 setMusicIcon(R.drawable.ic_audio_bt, R.drawable.ic_audio_bt_mute);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800668 } else {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800669 setMusicIcon(R.drawable.ic_audio_vol, R.drawable.ic_audio_vol_mute);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800670 }
671 break;
672 }
673
674 case AudioManager.STREAM_VOICE_CALL: {
675 /*
676 * For in-call voice call volume, there is no inaudible volume.
677 * Rescale the UI control so the progress bar doesn't go all
678 * the way to zero and don't show the mute icon.
679 */
680 index++;
681 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800682 break;
683 }
684
685 case AudioManager.STREAM_ALARM: {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800686 break;
687 }
688
689 case AudioManager.STREAM_NOTIFICATION: {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700690 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
691 mContext, RingtoneManager.TYPE_NOTIFICATION);
692 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700693 mRingIsSilent = true;
694 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800695 break;
696 }
697
698 case AudioManager.STREAM_BLUETOOTH_SCO: {
699 /*
700 * For in-call voice call volume, there is no inaudible volume.
701 * Rescale the UI control so the progress bar doesn't go all
702 * the way to zero and don't show the mute icon.
703 */
704 index++;
705 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800706 break;
707 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700708
709 case AudioService.STREAM_REMOTE_MUSIC: {
710 if (LOGD) { Log.d(TAG, "showing remote volume "+index+" over "+ max); }
711 break;
712 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800713 }
714
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800715 StreamControl sc = mStreamControls.get(streamType);
716 if (sc != null) {
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700717 if (sc.seekbarView.getMax() != max) {
718 sc.seekbarView.setMax(max);
719 }
Eric Laurent4bbcc652012-09-24 14:26:30 -0700720
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800721 sc.seekbarView.setProgress(index);
Eric Laurent4bbcc652012-09-24 14:26:30 -0700722 if (((flags & AudioManager.FLAG_FIXED_VOLUME) != 0) ||
723 (streamType != mAudioManager.getMasterStreamType() &&
724 streamType != AudioService.STREAM_REMOTE_MUSIC &&
Eric Laurentfde16d52012-12-03 14:42:39 -0800725 isMuted(streamType)) ||
726 sConfirmSafeVolumeDialog != null) {
Eric Laurent8c787522012-05-14 14:09:43 -0700727 sc.seekbarView.setEnabled(false);
728 } else {
729 sc.seekbarView.setEnabled(true);
730 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800731 }
732
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800733 if (!mDialog.isShowing()) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700734 int stream = (streamType == AudioService.STREAM_REMOTE_MUSIC) ? -1 : streamType;
735 // when the stream is for remote playback, use -1 to reset the stream type evaluation
736 mAudioManager.forceVolumeControlStream(stream);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700737
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800738 // Showing dialog - use collapsed state
Amith Yamasani42722bf2011-07-22 10:34:27 -0700739 if (mShowCombinedVolumes) {
740 collapse();
741 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800742 mDialog.show();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800743 }
744
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800745 // Do a little vibrate if applicable (only when going into vibrate mode)
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700746 if ((streamType != AudioService.STREAM_REMOTE_MUSIC) &&
747 ((flags & AudioManager.FLAG_VIBRATE) != 0) &&
John Spurlock3346a802014-05-20 16:25:37 -0400748 mAudioManager.isStreamAffectedByRingerMode(streamType) &&
Eric Laurent8c787522012-05-14 14:09:43 -0700749 mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800750 sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY);
751 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800752 }
753
754 protected void onPlaySound(int streamType, int flags) {
755
756 if (hasMessages(MSG_STOP_SOUNDS)) {
757 removeMessages(MSG_STOP_SOUNDS);
758 // Force stop right now
759 onStopSounds();
760 }
761
762 synchronized (this) {
763 ToneGenerator toneGen = getOrCreateToneGenerator(streamType);
Eric Laurent733a42b2011-01-19 10:41:57 -0800764 if (toneGen != null) {
765 toneGen.startTone(ToneGenerator.TONE_PROP_BEEP);
766 sendMessageDelayed(obtainMessage(MSG_STOP_SOUNDS), BEEP_DURATION);
767 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800768 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800769 }
770
771 protected void onStopSounds() {
772
773 synchronized (this) {
774 int numStreamTypes = AudioSystem.getNumStreamTypes();
775 for (int i = numStreamTypes - 1; i >= 0; i--) {
776 ToneGenerator toneGen = mToneGenerators[i];
777 if (toneGen != null) {
778 toneGen.stopTone();
779 }
780 }
781 }
782 }
783
784 protected void onVibrate() {
785
786 // Make sure we ended up in vibrate ringer mode
Eric Laurent8c787522012-05-14 14:09:43 -0700787 if (mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800788 return;
789 }
790
John Spurlockf9e1a0b2014-03-19 22:09:06 -0400791 mVibrator.vibrate(VIBRATE_DURATION, AudioManager.STREAM_SYSTEM);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800792 }
793
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700794 protected void onRemoteVolumeChanged(int streamType, int flags) {
795 // streamType is the real stream type being affected, but for the UI sliders, we
796 // refer to AudioService.STREAM_REMOTE_MUSIC. We still play the beeps on the real
797 // stream type.
798 if (LOGD) Log.d(TAG, "onRemoteVolumeChanged(stream:"+streamType+", flags: " + flags + ")");
799
800 if (((flags & AudioManager.FLAG_SHOW_UI) != 0) || mDialog.isShowing()) {
801 synchronized (this) {
802 if (mActiveStreamType != AudioService.STREAM_REMOTE_MUSIC) {
803 reorderSliders(AudioService.STREAM_REMOTE_MUSIC);
804 }
805 onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, flags);
806 }
807 } else {
808 if (LOGD) Log.d(TAG, "not calling onShowVolumeChanged(), no FLAG_SHOW_UI or no UI");
809 }
810
811 if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {
812 removeMessages(MSG_PLAY_SOUND);
813 sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);
814 }
815
816 if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) {
817 removeMessages(MSG_PLAY_SOUND);
818 removeMessages(MSG_VIBRATE);
819 onStopSounds();
820 }
821
822 removeMessages(MSG_FREE_RESOURCES);
823 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700824 resetTimeout();
825 }
826
827 protected void onRemoteVolumeUpdateIfShown() {
828 if (LOGD) Log.d(TAG, "onRemoteVolumeUpdateIfShown()");
829 if (mDialog.isShowing()
830 && (mActiveStreamType == AudioService.STREAM_REMOTE_MUSIC)
831 && (mStreamControls != null)) {
832 onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, 0);
833 }
834 }
835
836
837 /**
838 * Handler for MSG_SLIDER_VISIBILITY_CHANGED
839 * Hide or show a slider
840 * @param streamType can be a valid stream type value, or VolumePanel.STREAM_MASTER,
841 * or AudioService.STREAM_REMOTE_MUSIC
842 * @param visible
843 */
844 synchronized protected void onSliderVisibilityChanged(int streamType, int visible) {
845 if (LOGD) Log.d(TAG, "onSliderVisibilityChanged(stream="+streamType+", visi="+visible+")");
846 boolean isVisible = (visible == 1);
847 for (int i = STREAMS.length - 1 ; i >= 0 ; i--) {
848 StreamResources streamRes = STREAMS[i];
849 if (streamRes.streamType == streamType) {
850 streamRes.show = isVisible;
851 if (!isVisible && (mActiveStreamType == streamType)) {
852 mActiveStreamType = -1;
853 }
854 break;
855 }
856 }
857 }
858
Eric Laurentfde16d52012-12-03 14:42:39 -0800859 protected void onDisplaySafeVolumeWarning(int flags) {
860 if ((flags & AudioManager.FLAG_SHOW_UI) != 0 || mDialog.isShowing()) {
861 synchronized (sConfirmSafeVolumeLock) {
862 if (sConfirmSafeVolumeDialog != null) {
863 return;
864 }
865 sConfirmSafeVolumeDialog = new AlertDialog.Builder(mContext)
866 .setMessage(com.android.internal.R.string.safe_media_volume_warning)
867 .setPositiveButton(com.android.internal.R.string.yes,
868 new DialogInterface.OnClickListener() {
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700869 @Override
Eric Laurentfde16d52012-12-03 14:42:39 -0800870 public void onClick(DialogInterface dialog, int which) {
John Spurlock3346a802014-05-20 16:25:37 -0400871 mAudioManager.disableSafeMediaVolume();
Eric Laurentfde16d52012-12-03 14:42:39 -0800872 }
873 })
874 .setNegativeButton(com.android.internal.R.string.no, null)
875 .setIconAttribute(android.R.attr.alertDialogIcon)
876 .create();
877 final WarningDialogReceiver warning = new WarningDialogReceiver(mContext,
878 sConfirmSafeVolumeDialog, this);
Eric Laurent0516a9e2012-09-19 11:53:03 -0700879
Eric Laurentfde16d52012-12-03 14:42:39 -0800880 sConfirmSafeVolumeDialog.setOnDismissListener(warning);
881 sConfirmSafeVolumeDialog.getWindow().setType(
882 WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
883 sConfirmSafeVolumeDialog.show();
884 }
885 updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700886 }
Eric Laurentfde16d52012-12-03 14:42:39 -0800887 resetTimeout();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700888 }
889
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800890 /**
891 * Lock on this VolumePanel instance as long as you use the returned ToneGenerator.
892 */
893 private ToneGenerator getOrCreateToneGenerator(int streamType) {
Christopher Tatec4b78d22012-05-22 13:57:58 -0700894 if (streamType == STREAM_MASTER) {
895 // For devices that use the master volume setting only but still want to
896 // play a volume-changed tone, direct the master volume pseudostream to
897 // the system stream's tone generator.
898 if (mPlayMasterStreamTones) {
899 streamType = AudioManager.STREAM_SYSTEM;
900 } else {
901 return null;
902 }
903 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800904 synchronized (this) {
905 if (mToneGenerators[streamType] == null) {
Eric Laurent733a42b2011-01-19 10:41:57 -0800906 try {
907 mToneGenerators[streamType] = new ToneGenerator(streamType, MAX_VOLUME);
908 } catch (RuntimeException e) {
909 if (LOGD) {
910 Log.d(TAG, "ToneGenerator constructor failed with "
911 + "RuntimeException: " + e);
912 }
913 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800914 }
Eric Laurent733a42b2011-01-19 10:41:57 -0800915 return mToneGenerators[streamType];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800916 }
917 }
918
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800919
920 /**
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800921 * Switch between icons because Bluetooth music is same as music volume, but with
922 * different icons.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800923 */
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800924 private void setMusicIcon(int resId, int resMuteId) {
925 StreamControl sc = mStreamControls.get(AudioManager.STREAM_MUSIC);
926 if (sc != null) {
927 sc.iconRes = resId;
928 sc.iconMuteRes = resMuteId;
929 sc.icon.setImageResource(isMuted(sc.streamType) ? sc.iconMuteRes : sc.iconRes);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800930 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800931 }
932
933 protected void onFreeResources() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800934 synchronized (this) {
935 for (int i = mToneGenerators.length - 1; i >= 0; i--) {
936 if (mToneGenerators[i] != null) {
937 mToneGenerators[i].release();
938 }
939 mToneGenerators[i] = null;
940 }
941 }
942 }
943
944 @Override
945 public void handleMessage(Message msg) {
946 switch (msg.what) {
947
948 case MSG_VOLUME_CHANGED: {
949 onVolumeChanged(msg.arg1, msg.arg2);
950 break;
951 }
952
Mike Lockwoodce952c82011-11-14 10:47:42 -0800953 case MSG_MUTE_CHANGED: {
954 onMuteChanged(msg.arg1, msg.arg2);
955 break;
956 }
957
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800958 case MSG_FREE_RESOURCES: {
959 onFreeResources();
960 break;
961 }
962
963 case MSG_STOP_SOUNDS: {
964 onStopSounds();
965 break;
966 }
967
968 case MSG_PLAY_SOUND: {
969 onPlaySound(msg.arg1, msg.arg2);
970 break;
971 }
972
973 case MSG_VIBRATE: {
974 onVibrate();
975 break;
976 }
977
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800978 case MSG_TIMEOUT: {
979 if (mDialog.isShowing()) {
980 mDialog.dismiss();
981 mActiveStreamType = -1;
982 }
Eric Laurentfde16d52012-12-03 14:42:39 -0800983 synchronized (sConfirmSafeVolumeLock) {
984 if (sConfirmSafeVolumeDialog != null) {
985 sConfirmSafeVolumeDialog.dismiss();
986 }
987 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800988 break;
989 }
990 case MSG_RINGER_MODE_CHANGED: {
991 if (mDialog.isShowing()) {
992 updateStates();
993 }
994 break;
995 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700996
997 case MSG_REMOTE_VOLUME_CHANGED: {
998 onRemoteVolumeChanged(msg.arg1, msg.arg2);
999 break;
1000 }
1001
1002 case MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN:
1003 onRemoteVolumeUpdateIfShown();
1004 break;
1005
1006 case MSG_SLIDER_VISIBILITY_CHANGED:
1007 onSliderVisibilityChanged(msg.arg1, msg.arg2);
1008 break;
Eric Laurentc34dcc12012-09-10 13:51:52 -07001009
1010 case MSG_DISPLAY_SAFE_VOLUME_WARNING:
Eric Laurentfde16d52012-12-03 14:42:39 -08001011 onDisplaySafeVolumeWarning(msg.arg1);
Eric Laurentc34dcc12012-09-10 13:51:52 -07001012 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001013 }
1014 }
1015
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001016 private void resetTimeout() {
1017 removeMessages(MSG_TIMEOUT);
1018 sendMessageDelayed(obtainMessage(MSG_TIMEOUT), TIMEOUT_DELAY);
1019 }
1020
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001021 private void forceTimeout() {
1022 removeMessages(MSG_TIMEOUT);
1023 sendMessage(obtainMessage(MSG_TIMEOUT));
1024 }
1025
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001026 private final OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
1027 @Override
1028 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
1029 final Object tag = seekBar.getTag();
1030 if (fromUser && tag instanceof StreamControl) {
1031 StreamControl sc = (StreamControl) tag;
1032 if (getStreamVolume(sc.streamType) != progress) {
1033 setStreamVolume(sc.streamType, progress, 0);
1034 }
1035 }
1036 resetTimeout();
1037 }
1038
1039 @Override
1040 public void onStartTrackingTouch(SeekBar seekBar) {
1041 }
1042
1043 @Override
1044 public void onStopTrackingTouch(SeekBar seekBar) {
1045 final Object tag = seekBar.getTag();
1046 if (tag instanceof StreamControl) {
1047 StreamControl sc = (StreamControl) tag;
1048 // Because remote volume updates are asynchronous, AudioService
1049 // might have received a new remote volume value since the
1050 // finger adjusted the slider. So when the progress of the
1051 // slider isn't being tracked anymore, adjust the slider to the
1052 // last "published" remote volume value, so the UI reflects the
1053 // actual volume.
1054 if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
1055 seekBar.setProgress(getStreamVolume(AudioService.STREAM_REMOTE_MUSIC));
1056 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001057 }
1058 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001059 };
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001060
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001061 private final View.OnClickListener mClickListener = new View.OnClickListener() {
1062 @Override
1063 public void onClick(View v) {
1064 if (v == mMoreButton) {
1065 expand();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001066 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001067 resetTimeout();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001068 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001069 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001070}