blob: 4730e595e347d69a2e60c8361ee726a30341b11d [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
17package android.view;
18
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;
Jean-Michel Trivifa9a6982013-06-27 16:22:58 -070035import android.media.VolumeController;
Marco Nelissen69f593c2009-07-28 09:55:04 -070036import android.net.Uri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037import android.os.Handler;
38import android.os.Message;
39import android.os.Vibrator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import android.util.Log;
Amith Yamasani284e6302011-09-16 18:24:47 -070041import android.view.WindowManager.LayoutParams;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042import android.widget.ImageView;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080043import android.widget.SeekBar;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080044import android.widget.SeekBar.OnSeekBarChangeListener;
45
46import java.util.HashMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047
48/**
49 * Handle the volume up and down keys.
50 *
51 * This code really should be moved elsewhere.
52 *
Dianne Hackborne8ecde12011-08-03 18:55:19 -070053 * Seriously, it really really should be moved elsewhere. This is used by
54 * android.media.AudioService, which actually runs in the system process, to
55 * show the volume dialog when the user changes the volume. What a mess.
56 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057 * @hide
58 */
Alan Viverettee8ebaf32014-04-11 15:44:15 -070059public class VolumePanel extends Handler implements VolumeController {
60 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
63 /**
64 * The delay before playing a sound. This small period exists so the user
65 * can press another key (non-volume keys, too) to have it NOT be audible.
66 * <p>
67 * PhoneWindow will implement this part.
68 */
69 public static final int PLAY_SOUND_DELAY = 300;
70
71 /**
72 * The delay before vibrating. This small period exists so if the user is
73 * moving to silent mode, it will not emit a short vibrate (it normally
74 * would since vibrate is between normal mode and silent mode using hardware
75 * keys).
76 */
77 public static final int VIBRATE_DELAY = 300;
78
79 private static final int VIBRATE_DURATION = 300;
80 private static final int BEEP_DURATION = 150;
81 private static final int MAX_VOLUME = 100;
82 private static final int FREE_DELAY = 10000;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080083 private static final int TIMEOUT_DELAY = 3000;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084
85 private static final int MSG_VOLUME_CHANGED = 0;
86 private static final int MSG_FREE_RESOURCES = 1;
87 private static final int MSG_PLAY_SOUND = 2;
88 private static final int MSG_STOP_SOUNDS = 3;
89 private static final int MSG_VIBRATE = 4;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080090 private static final int MSG_TIMEOUT = 5;
91 private static final int MSG_RINGER_MODE_CHANGED = 6;
Mike Lockwoodce952c82011-11-14 10:47:42 -080092 private static final int MSG_MUTE_CHANGED = 7;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -070093 private static final int MSG_REMOTE_VOLUME_CHANGED = 8;
94 private static final int MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN = 9;
95 private static final int MSG_SLIDER_VISIBILITY_CHANGED = 10;
Eric Laurentc34dcc12012-09-10 13:51:52 -070096 private static final int MSG_DISPLAY_SAFE_VOLUME_WARNING = 11;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097
Mike Lockwood8dc1dab2011-10-27 09:52:41 -040098 // Pseudo stream type for master volume
Mike Lockwood47676902011-11-08 10:31:21 -080099 private static final int STREAM_MASTER = -100;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700100 // Pseudo stream type for remote volume is defined in AudioService.STREAM_REMOTE_MUSIC
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400101
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102 protected Context mContext;
103 private AudioManager mAudioManager;
104 protected AudioService mAudioService;
Marco Nelissen69f593c2009-07-28 09:55:04 -0700105 private boolean mRingIsSilent;
Amith Yamasani42722bf2011-07-22 10:34:27 -0700106 private boolean mShowCombinedVolumes;
Amith Yamasani71def772011-10-12 12:25:24 -0700107 private boolean mVoiceCapable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800108
Christopher Tatec4b78d22012-05-22 13:57:58 -0700109 // True if we want to play tones on the system stream when the master stream is specified.
110 private final boolean mPlayMasterStreamTones;
111
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800112 /** Dialog containing all the sliders */
113 private final Dialog mDialog;
114 /** Dialog's content view */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115 private final View mView;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800116
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -0700117 /** The visible portion of the volume overlay */
118 private final ViewGroup mPanel;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800119 /** Contains the sliders and their touchable icons */
120 private final ViewGroup mSliderGroup;
121 /** The button that expands the dialog to show all sliders */
122 private final View mMoreButton;
123 /** Dummy divider icon that needs to vanish with the more button */
124 private final View mDivider;
125
126 /** Currently active stream that shows up at the top of the list of sliders */
127 private int mActiveStreamType = -1;
128 /** All the slider controls mapped by stream type */
129 private HashMap<Integer,StreamControl> mStreamControls;
130
Amith Yamasani71def772011-10-12 12:25:24 -0700131 private enum StreamResources {
132 BluetoothSCOStream(AudioManager.STREAM_BLUETOOTH_SCO,
133 R.string.volume_icon_description_bluetooth,
134 R.drawable.ic_audio_bt,
135 R.drawable.ic_audio_bt,
136 false),
137 RingerStream(AudioManager.STREAM_RING,
138 R.string.volume_icon_description_ringer,
139 R.drawable.ic_audio_ring_notif,
140 R.drawable.ic_audio_ring_notif_mute,
141 false),
142 VoiceStream(AudioManager.STREAM_VOICE_CALL,
143 R.string.volume_icon_description_incall,
144 R.drawable.ic_audio_phone,
145 R.drawable.ic_audio_phone,
146 false),
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700147 AlarmStream(AudioManager.STREAM_ALARM,
148 R.string.volume_alarm,
149 R.drawable.ic_audio_alarm,
150 R.drawable.ic_audio_alarm_mute,
151 false),
Amith Yamasani71def772011-10-12 12:25:24 -0700152 MediaStream(AudioManager.STREAM_MUSIC,
153 R.string.volume_icon_description_media,
154 R.drawable.ic_audio_vol,
155 R.drawable.ic_audio_vol_mute,
156 true),
157 NotificationStream(AudioManager.STREAM_NOTIFICATION,
158 R.string.volume_icon_description_notification,
159 R.drawable.ic_audio_notification,
160 R.drawable.ic_audio_notification_mute,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400161 true),
162 // for now, use media resources for master volume
163 MasterStream(STREAM_MASTER,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700164 R.string.volume_icon_description_media, //FIXME should have its own description
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400165 R.drawable.ic_audio_vol,
166 R.drawable.ic_audio_vol_mute,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700167 false),
168 RemoteStream(AudioService.STREAM_REMOTE_MUSIC,
169 R.string.volume_icon_description_media, //FIXME should have its own description
170 R.drawable.ic_media_route_on_holo_dark,
171 R.drawable.ic_media_route_disabled_holo_dark,
172 false);// will be dynamically updated
Amith Yamasani71def772011-10-12 12:25:24 -0700173
174 int streamType;
175 int descRes;
176 int iconRes;
177 int iconMuteRes;
178 // RING, VOICE_CALL & BLUETOOTH_SCO are hidden unless explicitly requested
179 boolean show;
180
181 StreamResources(int streamType, int descRes, int iconRes, int iconMuteRes, boolean show) {
182 this.streamType = streamType;
183 this.descRes = descRes;
184 this.iconRes = iconRes;
185 this.iconMuteRes = iconMuteRes;
186 this.show = show;
187 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700188 }
Amith Yamasani71def772011-10-12 12:25:24 -0700189
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800190 // List of stream types and their order
Amith Yamasani71def772011-10-12 12:25:24 -0700191 private static final StreamResources[] STREAMS = {
192 StreamResources.BluetoothSCOStream,
193 StreamResources.RingerStream,
194 StreamResources.VoiceStream,
195 StreamResources.MediaStream,
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700196 StreamResources.NotificationStream,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400197 StreamResources.AlarmStream,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700198 StreamResources.MasterStream,
199 StreamResources.RemoteStream
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800200 };
201
202 /** Object that contains data for each slider */
203 private class StreamControl {
204 int streamType;
205 ViewGroup group;
206 ImageView icon;
207 SeekBar seekbarView;
208 int iconRes;
209 int iconMuteRes;
210 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800211
212 // Synchronize when accessing this
213 private ToneGenerator mToneGenerators[];
214 private Vibrator mVibrator;
215
Eric Laurentc34dcc12012-09-10 13:51:52 -0700216 private static AlertDialog sConfirmSafeVolumeDialog;
Eric Laurent0516a9e2012-09-19 11:53:03 -0700217 private static Object sConfirmSafeVolumeLock = new Object();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700218
219 private static class WarningDialogReceiver extends BroadcastReceiver
220 implements DialogInterface.OnDismissListener {
Eric Laurentfde16d52012-12-03 14:42:39 -0800221 private final Context mContext;
222 private final Dialog mDialog;
223 private final VolumePanel mVolumePanel;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700224
Eric Laurentfde16d52012-12-03 14:42:39 -0800225 WarningDialogReceiver(Context context, Dialog dialog, VolumePanel volumePanel) {
Eric Laurentc34dcc12012-09-10 13:51:52 -0700226 mContext = context;
227 mDialog = dialog;
Eric Laurentfde16d52012-12-03 14:42:39 -0800228 mVolumePanel = volumePanel;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700229 IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
230 context.registerReceiver(this, filter);
231 }
232
233 @Override
234 public void onReceive(Context context, Intent intent) {
235 mDialog.cancel();
Eric Laurentfde16d52012-12-03 14:42:39 -0800236 cleanUp();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700237 }
238
Alan Viverette494fb7b2014-04-10 18:12:56 -0700239 @Override
Eric Laurentc34dcc12012-09-10 13:51:52 -0700240 public void onDismiss(DialogInterface unused) {
241 mContext.unregisterReceiver(this);
Eric Laurentfde16d52012-12-03 14:42:39 -0800242 cleanUp();
243 }
244
245 private void cleanUp() {
Eric Laurent0516a9e2012-09-19 11:53:03 -0700246 synchronized (sConfirmSafeVolumeLock) {
247 sConfirmSafeVolumeDialog = null;
248 }
Eric Laurentfde16d52012-12-03 14:42:39 -0800249 mVolumePanel.forceTimeout();
250 mVolumePanel.updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700251 }
252 }
253
254
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700255 public VolumePanel(Context context, AudioService volumeService) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800256 mContext = context;
257 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
258 mAudioService = volumeService;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800259
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400260 // For now, only show master volume if master volume is supported
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700261 final Resources res = context.getResources();
262 final boolean useMasterVolume = res.getBoolean(R.bool.config_useMasterVolume);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400263 if (useMasterVolume) {
264 for (int i = 0; i < STREAMS.length; i++) {
265 StreamResources streamRes = STREAMS[i];
266 streamRes.show = (streamRes.streamType == STREAM_MASTER);
267 }
268 }
269
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700270 mDialog = new Dialog(context) {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700271 @Override
Amith Yamasani284e6302011-09-16 18:24:47 -0700272 public boolean onTouchEvent(MotionEvent event) {
Eric Laurentfde16d52012-12-03 14:42:39 -0800273 if (isShowing() && event.getAction() == MotionEvent.ACTION_OUTSIDE &&
274 sConfirmSafeVolumeDialog == null) {
Amith Yamasani284e6302011-09-16 18:24:47 -0700275 forceTimeout();
276 return true;
277 }
278 return false;
279 }
280 };
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700281
282 // Change some window properties
283 final Window window = mDialog.getWindow();
284 final LayoutParams lp = window.getAttributes();
285 lp.token = null;
286 // Offset from the top
287 lp.y = res.getDimensionPixelOffset(R.dimen.volume_panel_top);
288 lp.type = LayoutParams.TYPE_VOLUME_OVERLAY;
289 lp.windowAnimations = R.style.Animation_VolumePanel;
290 window.setAttributes(lp);
291 window.setGravity(Gravity.TOP);
292 window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
293 window.requestFeature(Window.FEATURE_NO_TITLE);
294 window.addFlags(LayoutParams.FLAG_NOT_FOCUSABLE
295 | LayoutParams.FLAG_NOT_TOUCH_MODAL
296 | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
297
298 mDialog.setCanceledOnTouchOutside(true);
299 mDialog.setContentView(R.layout.volume_adjust);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800300 mDialog.setOnDismissListener(new OnDismissListener() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700301 @Override
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800302 public void onDismiss(DialogInterface dialog) {
303 mActiveStreamType = -1;
Eric Laurent402f7f22011-02-04 12:30:32 -0800304 mAudioManager.forceVolumeControlStream(mActiveStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800305 }
306 });
Alan Viverette494fb7b2014-04-10 18:12:56 -0700307
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700308 mDialog.create();
Alan Viverette494fb7b2014-04-10 18:12:56 -0700309
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700310 mView = window.findViewById(R.id.content);
311 mView.setOnTouchListener(new View.OnTouchListener() {
312 @Override
313 public boolean onTouch(View v, MotionEvent event) {
314 resetTimeout();
315 return false;
316 }
317 });
318
319 mPanel = (ViewGroup) mView.findViewById(R.id.visible_panel);
320 mSliderGroup = (ViewGroup) mView.findViewById(R.id.slider_group);
321 mMoreButton = mView.findViewById(R.id.expand_button);
322 mDivider = mView.findViewById(R.id.expand_button_divider);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800323
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800324 mToneGenerators = new ToneGenerator[AudioSystem.getNumStreamTypes()];
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700325 mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
Amith Yamasani71def772011-10-12 12:25:24 -0700326 mVoiceCapable = context.getResources().getBoolean(R.bool.config_voice_capable);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700327
328 // If we don't want to show multiple volumes, hide the settings button
329 // and divider.
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400330 mShowCombinedVolumes = !mVoiceCapable && !useMasterVolume;
Amith Yamasani42722bf2011-07-22 10:34:27 -0700331 if (!mShowCombinedVolumes) {
332 mMoreButton.setVisibility(View.GONE);
333 mDivider.setVisibility(View.GONE);
334 } else {
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700335 mMoreButton.setOnClickListener(mClickListener);
Amith Yamasani42722bf2011-07-22 10:34:27 -0700336 }
337
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700338 final boolean masterVolumeOnly = res.getBoolean(R.bool.config_useMasterVolume);
339 final boolean masterVolumeKeySounds = res.getBoolean(R.bool.config_useVolumeKeySounds);
Christopher Tatec4b78d22012-05-22 13:57:58 -0700340 mPlayMasterStreamTones = masterVolumeOnly && masterVolumeKeySounds;
341
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800342 listenToRingerMode();
343 }
344
Fabrice Di Meglio8c028842013-01-09 18:20:38 -0800345 public void setLayoutDirection(int layoutDirection) {
346 mPanel.setLayoutDirection(layoutDirection);
347 updateStates();
348 }
349
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800350 private void listenToRingerMode() {
351 final IntentFilter filter = new IntentFilter();
352 filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
353 mContext.registerReceiver(new BroadcastReceiver() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700354 @Override
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800355 public void onReceive(Context context, Intent intent) {
356 final String action = intent.getAction();
357
358 if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) {
359 removeMessages(MSG_RINGER_MODE_CHANGED);
360 sendMessage(obtainMessage(MSG_RINGER_MODE_CHANGED));
361 }
362 }
363 }, filter);
364 }
365
366 private boolean isMuted(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400367 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700368 return mAudioManager.isMasterMute();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700369 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
370 return (mAudioService.getRemoteStreamVolume() <= 0);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400371 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700372 return mAudioManager.isStreamMute(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400373 }
374 }
375
376 private int getStreamMaxVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400377 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700378 return mAudioManager.getMasterMaxVolume();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700379 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
380 return mAudioService.getRemoteStreamMaxVolume();
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400381 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700382 return mAudioManager.getStreamMaxVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400383 }
384 }
385
386 private int getStreamVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400387 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700388 return mAudioManager.getMasterVolume();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700389 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
390 return mAudioService.getRemoteStreamVolume();
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400391 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700392 return mAudioManager.getStreamVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400393 }
394 }
395
396 private void setStreamVolume(int streamType, int index, int flags) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400397 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700398 mAudioManager.setMasterVolume(index, flags);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700399 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
400 mAudioService.setRemoteStreamVolume(index);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400401 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700402 mAudioManager.setStreamVolume(streamType, index, flags);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400403 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800404 }
405
406 private void createSliders() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700407 final Resources res = mContext.getResources();
408 final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
409 Context.LAYOUT_INFLATER_SERVICE);
410
Amith Yamasani71def772011-10-12 12:25:24 -0700411 mStreamControls = new HashMap<Integer, StreamControl>(STREAMS.length);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700412
Amith Yamasani71def772011-10-12 12:25:24 -0700413 for (int i = 0; i < STREAMS.length; i++) {
414 StreamResources streamRes = STREAMS[i];
Alan Viverette494fb7b2014-04-10 18:12:56 -0700415
416 final int streamType = streamRes.streamType;
Amith Yamasani71def772011-10-12 12:25:24 -0700417 if (mVoiceCapable && streamRes == StreamResources.NotificationStream) {
418 streamRes = StreamResources.RingerStream;
419 }
Alan Viverette494fb7b2014-04-10 18:12:56 -0700420
421 final StreamControl sc = new StreamControl();
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700422 sc.streamType = streamType;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800423 sc.group = (ViewGroup) inflater.inflate(R.layout.volume_adjust_item, null);
424 sc.group.setTag(sc);
425 sc.icon = (ImageView) sc.group.findViewById(R.id.stream_icon);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800426 sc.icon.setTag(sc);
Amith Yamasani71def772011-10-12 12:25:24 -0700427 sc.icon.setContentDescription(res.getString(streamRes.descRes));
428 sc.iconRes = streamRes.iconRes;
429 sc.iconMuteRes = streamRes.iconMuteRes;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800430 sc.icon.setImageResource(sc.iconRes);
431 sc.seekbarView = (SeekBar) sc.group.findViewById(R.id.seekbar);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700432 final int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO ||
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700433 streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400434 sc.seekbarView.setMax(getStreamMaxVolume(streamType) + plusOne);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700435 sc.seekbarView.setOnSeekBarChangeListener(mSeekListener);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800436 sc.seekbarView.setTag(sc);
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700437 mStreamControls.put(streamType, sc);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800438 }
439 }
440
441 private void reorderSliders(int activeStreamType) {
442 mSliderGroup.removeAllViews();
443
Alan Viverette494fb7b2014-04-10 18:12:56 -0700444 final StreamControl active = mStreamControls.get(activeStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800445 if (active == null) {
446 Log.e("VolumePanel", "Missing stream type! - " + activeStreamType);
447 mActiveStreamType = -1;
448 } else {
449 mSliderGroup.addView(active.group);
450 mActiveStreamType = activeStreamType;
451 active.group.setVisibility(View.VISIBLE);
452 updateSlider(active);
453 }
454
Amith Yamasani42722bf2011-07-22 10:34:27 -0700455 addOtherVolumes();
456 }
457
458 private void addOtherVolumes() {
459 if (!mShowCombinedVolumes) return;
460
Amith Yamasani71def772011-10-12 12:25:24 -0700461 for (int i = 0; i < STREAMS.length; i++) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800462 // Skip the phone specific ones and the active one
Amith Yamasani71def772011-10-12 12:25:24 -0700463 final int streamType = STREAMS[i].streamType;
464 if (!STREAMS[i].show || streamType == mActiveStreamType) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800465 continue;
466 }
467 StreamControl sc = mStreamControls.get(streamType);
468 mSliderGroup.addView(sc.group);
469 updateSlider(sc);
470 }
471 }
472
473 /** Update the mute and progress state of a slider */
474 private void updateSlider(StreamControl sc) {
Eric Laurent8c787522012-05-14 14:09:43 -0700475 sc.seekbarView.setProgress(getStreamVolume(sc.streamType));
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800476 final boolean muted = isMuted(sc.streamType);
Fabrice Di Meglio8c028842013-01-09 18:20:38 -0800477 // Force reloading the image resource
478 sc.icon.setImageDrawable(null);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800479 sc.icon.setImageResource(muted ? sc.iconMuteRes : sc.iconRes);
Eric Laurent8a51aca2013-03-25 18:36:35 -0700480 if (((sc.streamType == AudioManager.STREAM_RING) ||
481 (sc.streamType == AudioManager.STREAM_NOTIFICATION)) &&
Eric Laurent8c787522012-05-14 14:09:43 -0700482 mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
Amith Yamasanic696a532011-10-28 17:02:37 -0700483 sc.icon.setImageResource(R.drawable.ic_audio_ring_notif_vibrate);
484 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700485 if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
486 // never disable touch interactions for remote playback, the muting is not tied to
487 // the state of the phone.
488 sc.seekbarView.setEnabled(true);
Eric Laurentfde16d52012-12-03 14:42:39 -0800489 } else if ((sc.streamType != mAudioManager.getMasterStreamType() && muted) ||
490 (sConfirmSafeVolumeDialog != null)) {
Eric Laurent8c787522012-05-14 14:09:43 -0700491 sc.seekbarView.setEnabled(false);
492 } else {
493 sc.seekbarView.setEnabled(true);
494 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800495 }
496
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800497 private void expand() {
498 final int count = mSliderGroup.getChildCount();
499 for (int i = 0; i < count; i++) {
500 mSliderGroup.getChildAt(i).setVisibility(View.VISIBLE);
501 }
502 mMoreButton.setVisibility(View.INVISIBLE);
503 mDivider.setVisibility(View.INVISIBLE);
504 }
505
506 private void collapse() {
507 mMoreButton.setVisibility(View.VISIBLE);
508 mDivider.setVisibility(View.VISIBLE);
509 final int count = mSliderGroup.getChildCount();
510 for (int i = 1; i < count; i++) {
511 mSliderGroup.getChildAt(i).setVisibility(View.GONE);
512 }
513 }
514
Eric Laurentfde16d52012-12-03 14:42:39 -0800515 public void updateStates() {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800516 final int count = mSliderGroup.getChildCount();
517 for (int i = 0; i < count; i++) {
518 StreamControl sc = (StreamControl) mSliderGroup.getChildAt(i).getTag();
519 updateSlider(sc);
520 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800521 }
522
523 public void postVolumeChanged(int streamType, int flags) {
524 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700525 synchronized (this) {
526 if (mStreamControls == null) {
527 createSliders();
528 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800529 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800530 removeMessages(MSG_FREE_RESOURCES);
531 obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget();
532 }
533
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700534 @Override
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700535 public void postRemoteVolumeChanged(int streamType, int flags) {
536 if (hasMessages(MSG_REMOTE_VOLUME_CHANGED)) return;
537 synchronized (this) {
538 if (mStreamControls == null) {
539 createSliders();
540 }
541 }
542 removeMessages(MSG_FREE_RESOURCES);
543 obtainMessage(MSG_REMOTE_VOLUME_CHANGED, streamType, flags).sendToTarget();
544 }
545
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700546 @Override
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700547 public void postRemoteSliderVisibility(boolean visible) {
548 obtainMessage(MSG_SLIDER_VISIBILITY_CHANGED,
549 AudioService.STREAM_REMOTE_MUSIC, visible ? 1 : 0).sendToTarget();
550 }
551
552 /**
553 * Called by AudioService when it has received new remote playback information that
554 * would affect the VolumePanel display (mainly volumes). The difference with
555 * {@link #postRemoteVolumeChanged(int, int)} is that the handling of the posted message
556 * (MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN) will only update the volume slider if it is being
557 * displayed.
558 * This special code path is due to the fact that remote volume updates arrive to AudioService
559 * asynchronously. So after AudioService has sent the volume update (which should be treated
560 * as a request to update the volume), the application will likely set a new volume. If the UI
561 * is still up, we need to refresh the display to show this new value.
562 */
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700563 @Override
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700564 public void postHasNewRemotePlaybackInfo() {
565 if (hasMessages(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN)) return;
566 // don't create or prevent resources to be freed, if they disappear, this update came too
567 // late and shouldn't warrant the panel to be displayed longer
568 obtainMessage(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN).sendToTarget();
569 }
570
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400571 public void postMasterVolumeChanged(int flags) {
572 postVolumeChanged(STREAM_MASTER, flags);
573 }
574
Mike Lockwoodce952c82011-11-14 10:47:42 -0800575 public void postMuteChanged(int streamType, int flags) {
576 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700577 synchronized (this) {
578 if (mStreamControls == null) {
579 createSliders();
580 }
Mike Lockwoodce952c82011-11-14 10:47:42 -0800581 }
582 removeMessages(MSG_FREE_RESOURCES);
583 obtainMessage(MSG_MUTE_CHANGED, streamType, flags).sendToTarget();
584 }
585
586 public void postMasterMuteChanged(int flags) {
587 postMuteChanged(STREAM_MASTER, flags);
588 }
589
Eric Laurentfde16d52012-12-03 14:42:39 -0800590 public void postDisplaySafeVolumeWarning(int flags) {
Eric Laurent0516a9e2012-09-19 11:53:03 -0700591 if (hasMessages(MSG_DISPLAY_SAFE_VOLUME_WARNING)) return;
Eric Laurentfde16d52012-12-03 14:42:39 -0800592 obtainMessage(MSG_DISPLAY_SAFE_VOLUME_WARNING, flags, 0).sendToTarget();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700593 }
594
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800595 /**
596 * Override this if you have other work to do when the volume changes (for
597 * example, vibrating, playing a sound, etc.). Make sure to call through to
598 * the superclass implementation.
599 */
600 protected void onVolumeChanged(int streamType, int flags) {
601
602 if (LOGD) Log.d(TAG, "onVolumeChanged(streamType: " + streamType + ", flags: " + flags + ")");
603
604 if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
Amith Yamasania6549862012-05-30 17:29:28 -0700605 synchronized (this) {
606 if (mActiveStreamType != streamType) {
607 reorderSliders(streamType);
608 }
609 onShowVolumeChanged(streamType, flags);
Amith Yamasanie3361b82011-02-10 18:20:50 -0800610 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800611 }
612
Marco Nelissen69f593c2009-07-28 09:55:04 -0700613 if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800614 removeMessages(MSG_PLAY_SOUND);
615 sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);
616 }
617
618 if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) {
619 removeMessages(MSG_PLAY_SOUND);
620 removeMessages(MSG_VIBRATE);
621 onStopSounds();
622 }
623
624 removeMessages(MSG_FREE_RESOURCES);
625 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800626 resetTimeout();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800627 }
628
Mike Lockwoodce952c82011-11-14 10:47:42 -0800629 protected void onMuteChanged(int streamType, int flags) {
630
631 if (LOGD) Log.d(TAG, "onMuteChanged(streamType: " + streamType + ", flags: " + flags + ")");
632
633 StreamControl sc = mStreamControls.get(streamType);
634 if (sc != null) {
635 sc.icon.setImageResource(isMuted(sc.streamType) ? sc.iconMuteRes : sc.iconRes);
636 }
637
638 onVolumeChanged(streamType, flags);
639 }
640
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800641 protected void onShowVolumeChanged(int streamType, int flags) {
Eric Laurent8c787522012-05-14 14:09:43 -0700642 int index = getStreamVolume(streamType);
Eric Laurentd72d51c2011-02-03 18:47:47 -0800643
Marco Nelissen69f593c2009-07-28 09:55:04 -0700644 mRingIsSilent = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800645
646 if (LOGD) {
647 Log.d(TAG, "onShowVolumeChanged(streamType: " + streamType
648 + ", flags: " + flags + "), index: " + index);
649 }
650
651 // get max volume for progress bar
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800652
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400653 int max = getStreamMaxVolume(streamType);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800654
655 switch (streamType) {
656
657 case AudioManager.STREAM_RING: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800658// setRingerIcon();
Marco Nelissen69f593c2009-07-28 09:55:04 -0700659 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
660 mContext, RingtoneManager.TYPE_RINGTONE);
661 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700662 mRingIsSilent = true;
663 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800664 break;
665 }
666
667 case AudioManager.STREAM_MUSIC: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800668 // Special case for when Bluetooth is active for music
Glenn Kasten8b4b97a2011-02-04 13:54:26 -0800669 if ((mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC) &
670 (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP |
671 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
672 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800673 setMusicIcon(R.drawable.ic_audio_bt, R.drawable.ic_audio_bt_mute);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800674 } else {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800675 setMusicIcon(R.drawable.ic_audio_vol, R.drawable.ic_audio_vol_mute);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800676 }
677 break;
678 }
679
680 case AudioManager.STREAM_VOICE_CALL: {
681 /*
682 * For in-call voice call volume, there is no inaudible volume.
683 * Rescale the UI control so the progress bar doesn't go all
684 * the way to zero and don't show the mute icon.
685 */
686 index++;
687 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800688 break;
689 }
690
691 case AudioManager.STREAM_ALARM: {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800692 break;
693 }
694
695 case AudioManager.STREAM_NOTIFICATION: {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700696 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
697 mContext, RingtoneManager.TYPE_NOTIFICATION);
698 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700699 mRingIsSilent = true;
700 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800701 break;
702 }
703
704 case AudioManager.STREAM_BLUETOOTH_SCO: {
705 /*
706 * For in-call voice call volume, there is no inaudible volume.
707 * Rescale the UI control so the progress bar doesn't go all
708 * the way to zero and don't show the mute icon.
709 */
710 index++;
711 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800712 break;
713 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700714
715 case AudioService.STREAM_REMOTE_MUSIC: {
716 if (LOGD) { Log.d(TAG, "showing remote volume "+index+" over "+ max); }
717 break;
718 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800719 }
720
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800721 StreamControl sc = mStreamControls.get(streamType);
722 if (sc != null) {
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700723 if (sc.seekbarView.getMax() != max) {
724 sc.seekbarView.setMax(max);
725 }
Eric Laurent4bbcc652012-09-24 14:26:30 -0700726
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800727 sc.seekbarView.setProgress(index);
Eric Laurent4bbcc652012-09-24 14:26:30 -0700728 if (((flags & AudioManager.FLAG_FIXED_VOLUME) != 0) ||
729 (streamType != mAudioManager.getMasterStreamType() &&
730 streamType != AudioService.STREAM_REMOTE_MUSIC &&
Eric Laurentfde16d52012-12-03 14:42:39 -0800731 isMuted(streamType)) ||
732 sConfirmSafeVolumeDialog != null) {
Eric Laurent8c787522012-05-14 14:09:43 -0700733 sc.seekbarView.setEnabled(false);
734 } else {
735 sc.seekbarView.setEnabled(true);
736 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800737 }
738
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800739 if (!mDialog.isShowing()) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700740 int stream = (streamType == AudioService.STREAM_REMOTE_MUSIC) ? -1 : streamType;
741 // when the stream is for remote playback, use -1 to reset the stream type evaluation
742 mAudioManager.forceVolumeControlStream(stream);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700743
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800744 // Showing dialog - use collapsed state
Amith Yamasani42722bf2011-07-22 10:34:27 -0700745 if (mShowCombinedVolumes) {
746 collapse();
747 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800748 mDialog.show();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800749 }
750
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800751 // Do a little vibrate if applicable (only when going into vibrate mode)
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700752 if ((streamType != AudioService.STREAM_REMOTE_MUSIC) &&
753 ((flags & AudioManager.FLAG_VIBRATE) != 0) &&
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800754 mAudioService.isStreamAffectedByRingerMode(streamType) &&
Eric Laurent8c787522012-05-14 14:09:43 -0700755 mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800756 sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY);
757 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800758 }
759
760 protected void onPlaySound(int streamType, int flags) {
761
762 if (hasMessages(MSG_STOP_SOUNDS)) {
763 removeMessages(MSG_STOP_SOUNDS);
764 // Force stop right now
765 onStopSounds();
766 }
767
768 synchronized (this) {
769 ToneGenerator toneGen = getOrCreateToneGenerator(streamType);
Eric Laurent733a42b2011-01-19 10:41:57 -0800770 if (toneGen != null) {
771 toneGen.startTone(ToneGenerator.TONE_PROP_BEEP);
772 sendMessageDelayed(obtainMessage(MSG_STOP_SOUNDS), BEEP_DURATION);
773 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800774 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800775 }
776
777 protected void onStopSounds() {
778
779 synchronized (this) {
780 int numStreamTypes = AudioSystem.getNumStreamTypes();
781 for (int i = numStreamTypes - 1; i >= 0; i--) {
782 ToneGenerator toneGen = mToneGenerators[i];
783 if (toneGen != null) {
784 toneGen.stopTone();
785 }
786 }
787 }
788 }
789
790 protected void onVibrate() {
791
792 // Make sure we ended up in vibrate ringer mode
Eric Laurent8c787522012-05-14 14:09:43 -0700793 if (mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800794 return;
795 }
796
John Spurlockf9e1a0b2014-03-19 22:09:06 -0400797 mVibrator.vibrate(VIBRATE_DURATION, AudioManager.STREAM_SYSTEM);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800798 }
799
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700800 protected void onRemoteVolumeChanged(int streamType, int flags) {
801 // streamType is the real stream type being affected, but for the UI sliders, we
802 // refer to AudioService.STREAM_REMOTE_MUSIC. We still play the beeps on the real
803 // stream type.
804 if (LOGD) Log.d(TAG, "onRemoteVolumeChanged(stream:"+streamType+", flags: " + flags + ")");
805
806 if (((flags & AudioManager.FLAG_SHOW_UI) != 0) || mDialog.isShowing()) {
807 synchronized (this) {
808 if (mActiveStreamType != AudioService.STREAM_REMOTE_MUSIC) {
809 reorderSliders(AudioService.STREAM_REMOTE_MUSIC);
810 }
811 onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, flags);
812 }
813 } else {
814 if (LOGD) Log.d(TAG, "not calling onShowVolumeChanged(), no FLAG_SHOW_UI or no UI");
815 }
816
817 if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {
818 removeMessages(MSG_PLAY_SOUND);
819 sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);
820 }
821
822 if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) {
823 removeMessages(MSG_PLAY_SOUND);
824 removeMessages(MSG_VIBRATE);
825 onStopSounds();
826 }
827
828 removeMessages(MSG_FREE_RESOURCES);
829 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700830 resetTimeout();
831 }
832
833 protected void onRemoteVolumeUpdateIfShown() {
834 if (LOGD) Log.d(TAG, "onRemoteVolumeUpdateIfShown()");
835 if (mDialog.isShowing()
836 && (mActiveStreamType == AudioService.STREAM_REMOTE_MUSIC)
837 && (mStreamControls != null)) {
838 onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, 0);
839 }
840 }
841
842
843 /**
844 * Handler for MSG_SLIDER_VISIBILITY_CHANGED
845 * Hide or show a slider
846 * @param streamType can be a valid stream type value, or VolumePanel.STREAM_MASTER,
847 * or AudioService.STREAM_REMOTE_MUSIC
848 * @param visible
849 */
850 synchronized protected void onSliderVisibilityChanged(int streamType, int visible) {
851 if (LOGD) Log.d(TAG, "onSliderVisibilityChanged(stream="+streamType+", visi="+visible+")");
852 boolean isVisible = (visible == 1);
853 for (int i = STREAMS.length - 1 ; i >= 0 ; i--) {
854 StreamResources streamRes = STREAMS[i];
855 if (streamRes.streamType == streamType) {
856 streamRes.show = isVisible;
857 if (!isVisible && (mActiveStreamType == streamType)) {
858 mActiveStreamType = -1;
859 }
860 break;
861 }
862 }
863 }
864
Eric Laurentfde16d52012-12-03 14:42:39 -0800865 protected void onDisplaySafeVolumeWarning(int flags) {
866 if ((flags & AudioManager.FLAG_SHOW_UI) != 0 || mDialog.isShowing()) {
867 synchronized (sConfirmSafeVolumeLock) {
868 if (sConfirmSafeVolumeDialog != null) {
869 return;
870 }
871 sConfirmSafeVolumeDialog = new AlertDialog.Builder(mContext)
872 .setMessage(com.android.internal.R.string.safe_media_volume_warning)
873 .setPositiveButton(com.android.internal.R.string.yes,
874 new DialogInterface.OnClickListener() {
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700875 @Override
Eric Laurentfde16d52012-12-03 14:42:39 -0800876 public void onClick(DialogInterface dialog, int which) {
877 mAudioService.disableSafeMediaVolume();
878 }
879 })
880 .setNegativeButton(com.android.internal.R.string.no, null)
881 .setIconAttribute(android.R.attr.alertDialogIcon)
882 .create();
883 final WarningDialogReceiver warning = new WarningDialogReceiver(mContext,
884 sConfirmSafeVolumeDialog, this);
Eric Laurent0516a9e2012-09-19 11:53:03 -0700885
Eric Laurentfde16d52012-12-03 14:42:39 -0800886 sConfirmSafeVolumeDialog.setOnDismissListener(warning);
887 sConfirmSafeVolumeDialog.getWindow().setType(
888 WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
889 sConfirmSafeVolumeDialog.show();
890 }
891 updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700892 }
Eric Laurentfde16d52012-12-03 14:42:39 -0800893 resetTimeout();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700894 }
895
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800896 /**
897 * Lock on this VolumePanel instance as long as you use the returned ToneGenerator.
898 */
899 private ToneGenerator getOrCreateToneGenerator(int streamType) {
Christopher Tatec4b78d22012-05-22 13:57:58 -0700900 if (streamType == STREAM_MASTER) {
901 // For devices that use the master volume setting only but still want to
902 // play a volume-changed tone, direct the master volume pseudostream to
903 // the system stream's tone generator.
904 if (mPlayMasterStreamTones) {
905 streamType = AudioManager.STREAM_SYSTEM;
906 } else {
907 return null;
908 }
909 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800910 synchronized (this) {
911 if (mToneGenerators[streamType] == null) {
Eric Laurent733a42b2011-01-19 10:41:57 -0800912 try {
913 mToneGenerators[streamType] = new ToneGenerator(streamType, MAX_VOLUME);
914 } catch (RuntimeException e) {
915 if (LOGD) {
916 Log.d(TAG, "ToneGenerator constructor failed with "
917 + "RuntimeException: " + e);
918 }
919 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800920 }
Eric Laurent733a42b2011-01-19 10:41:57 -0800921 return mToneGenerators[streamType];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800922 }
923 }
924
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800925
926 /**
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800927 * Switch between icons because Bluetooth music is same as music volume, but with
928 * different icons.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800929 */
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800930 private void setMusicIcon(int resId, int resMuteId) {
931 StreamControl sc = mStreamControls.get(AudioManager.STREAM_MUSIC);
932 if (sc != null) {
933 sc.iconRes = resId;
934 sc.iconMuteRes = resMuteId;
935 sc.icon.setImageResource(isMuted(sc.streamType) ? sc.iconMuteRes : sc.iconRes);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800936 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800937 }
938
939 protected void onFreeResources() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800940 synchronized (this) {
941 for (int i = mToneGenerators.length - 1; i >= 0; i--) {
942 if (mToneGenerators[i] != null) {
943 mToneGenerators[i].release();
944 }
945 mToneGenerators[i] = null;
946 }
947 }
948 }
949
950 @Override
951 public void handleMessage(Message msg) {
952 switch (msg.what) {
953
954 case MSG_VOLUME_CHANGED: {
955 onVolumeChanged(msg.arg1, msg.arg2);
956 break;
957 }
958
Mike Lockwoodce952c82011-11-14 10:47:42 -0800959 case MSG_MUTE_CHANGED: {
960 onMuteChanged(msg.arg1, msg.arg2);
961 break;
962 }
963
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800964 case MSG_FREE_RESOURCES: {
965 onFreeResources();
966 break;
967 }
968
969 case MSG_STOP_SOUNDS: {
970 onStopSounds();
971 break;
972 }
973
974 case MSG_PLAY_SOUND: {
975 onPlaySound(msg.arg1, msg.arg2);
976 break;
977 }
978
979 case MSG_VIBRATE: {
980 onVibrate();
981 break;
982 }
983
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800984 case MSG_TIMEOUT: {
985 if (mDialog.isShowing()) {
986 mDialog.dismiss();
987 mActiveStreamType = -1;
988 }
Eric Laurentfde16d52012-12-03 14:42:39 -0800989 synchronized (sConfirmSafeVolumeLock) {
990 if (sConfirmSafeVolumeDialog != null) {
991 sConfirmSafeVolumeDialog.dismiss();
992 }
993 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800994 break;
995 }
996 case MSG_RINGER_MODE_CHANGED: {
997 if (mDialog.isShowing()) {
998 updateStates();
999 }
1000 break;
1001 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001002
1003 case MSG_REMOTE_VOLUME_CHANGED: {
1004 onRemoteVolumeChanged(msg.arg1, msg.arg2);
1005 break;
1006 }
1007
1008 case MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN:
1009 onRemoteVolumeUpdateIfShown();
1010 break;
1011
1012 case MSG_SLIDER_VISIBILITY_CHANGED:
1013 onSliderVisibilityChanged(msg.arg1, msg.arg2);
1014 break;
Eric Laurentc34dcc12012-09-10 13:51:52 -07001015
1016 case MSG_DISPLAY_SAFE_VOLUME_WARNING:
Eric Laurentfde16d52012-12-03 14:42:39 -08001017 onDisplaySafeVolumeWarning(msg.arg1);
Eric Laurentc34dcc12012-09-10 13:51:52 -07001018 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001019 }
1020 }
1021
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001022 private void resetTimeout() {
1023 removeMessages(MSG_TIMEOUT);
1024 sendMessageDelayed(obtainMessage(MSG_TIMEOUT), TIMEOUT_DELAY);
1025 }
1026
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001027 private void forceTimeout() {
1028 removeMessages(MSG_TIMEOUT);
1029 sendMessage(obtainMessage(MSG_TIMEOUT));
1030 }
1031
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001032 private final OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
1033 @Override
1034 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
1035 final Object tag = seekBar.getTag();
1036 if (fromUser && tag instanceof StreamControl) {
1037 StreamControl sc = (StreamControl) tag;
1038 if (getStreamVolume(sc.streamType) != progress) {
1039 setStreamVolume(sc.streamType, progress, 0);
1040 }
1041 }
1042 resetTimeout();
1043 }
1044
1045 @Override
1046 public void onStartTrackingTouch(SeekBar seekBar) {
1047 }
1048
1049 @Override
1050 public void onStopTrackingTouch(SeekBar seekBar) {
1051 final Object tag = seekBar.getTag();
1052 if (tag instanceof StreamControl) {
1053 StreamControl sc = (StreamControl) tag;
1054 // Because remote volume updates are asynchronous, AudioService
1055 // might have received a new remote volume value since the
1056 // finger adjusted the slider. So when the progress of the
1057 // slider isn't being tracked anymore, adjust the slider to the
1058 // last "published" remote volume value, so the UI reflects the
1059 // actual volume.
1060 if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
1061 seekBar.setProgress(getStreamVolume(AudioService.STREAM_REMOTE_MUSIC));
1062 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001063 }
1064 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001065 };
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001066
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001067 private final View.OnClickListener mClickListener = new View.OnClickListener() {
1068 @Override
1069 public void onClick(View v) {
1070 if (v == mMoreButton) {
1071 expand();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001072 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001073 resetTimeout();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001074 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001075 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001076}