blob: fae0e011aac63bdc7f4cffc2e087af32823d4def [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
Eric Laurentc34dcc12012-09-10 13:51:52 -070019import android.app.AlertDialog;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080020import android.app.Dialog;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080021import android.content.BroadcastReceiver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import android.content.Context;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080023import android.content.DialogInterface;
John Spurlock86005342014-05-23 11:58:00 -040024import android.content.DialogInterface.OnDismissListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.content.Intent;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080026import android.content.IntentFilter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.content.res.Resources;
28import android.media.AudioManager;
29import android.media.AudioService;
30import android.media.AudioSystem;
Marco Nelissen69f593c2009-07-28 09:55:04 -070031import android.media.RingtoneManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032import android.media.ToneGenerator;
Marco Nelissen69f593c2009-07-28 09:55:04 -070033import android.net.Uri;
John Spurlock3bd4fee2014-05-29 20:51:09 -040034import android.os.AsyncTask;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035import android.os.Handler;
36import android.os.Message;
37import android.os.Vibrator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038import android.util.Log;
John Spurlock3346a802014-05-20 16:25:37 -040039import android.view.Gravity;
40import android.view.LayoutInflater;
41import android.view.MotionEvent;
42import android.view.View;
43import android.view.ViewGroup;
John Spurlock86005342014-05-23 11:58:00 -040044import android.view.ViewStub;
John Spurlock3346a802014-05-20 16:25:37 -040045import 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
John Spurlock86005342014-05-23 11:58:00 -040052import com.android.internal.R;
53import com.android.systemui.statusbar.policy.ZenModeController;
54
Amith Yamasani2bbdd772011-02-02 18:54:13 -080055import java.util.HashMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056
57/**
John Spurlock3346a802014-05-20 16:25:37 -040058 * Handles the user interface for the volume keys.
Dianne Hackborne8ecde12011-08-03 18:55:19 -070059 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060 * @hide
61 */
John Spurlock3346a802014-05-20 16:25:37 -040062public class VolumePanel extends Handler {
Marco Nelissen69f593c2009-07-28 09:55:04 -070063 private static boolean LOGD = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064
John Spurlock3346a802014-05-20 16:25:37 -040065 private static final int PLAY_SOUND_DELAY = AudioService.PLAY_SOUND_DELAY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066
67 /**
68 * The delay before vibrating. This small period exists so if the user is
69 * moving to silent mode, it will not emit a short vibrate (it normally
70 * would since vibrate is between normal mode and silent mode using hardware
71 * keys).
72 */
73 public static final int VIBRATE_DELAY = 300;
74
75 private static final int VIBRATE_DURATION = 300;
76 private static final int BEEP_DURATION = 150;
77 private static final int MAX_VOLUME = 100;
78 private static final int FREE_DELAY = 10000;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080079 private static final int TIMEOUT_DELAY = 3000;
John Spurlock3bd4fee2014-05-29 20:51:09 -040080 private static final int TIMEOUT_DELAY_EXPANDED = 10000;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081
82 private static final int MSG_VOLUME_CHANGED = 0;
83 private static final int MSG_FREE_RESOURCES = 1;
84 private static final int MSG_PLAY_SOUND = 2;
85 private static final int MSG_STOP_SOUNDS = 3;
86 private static final int MSG_VIBRATE = 4;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080087 private static final int MSG_TIMEOUT = 5;
88 private static final int MSG_RINGER_MODE_CHANGED = 6;
Mike Lockwoodce952c82011-11-14 10:47:42 -080089 private static final int MSG_MUTE_CHANGED = 7;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -070090 private static final int MSG_REMOTE_VOLUME_CHANGED = 8;
91 private static final int MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN = 9;
92 private static final int MSG_SLIDER_VISIBILITY_CHANGED = 10;
Eric Laurentc34dcc12012-09-10 13:51:52 -070093 private static final int MSG_DISPLAY_SAFE_VOLUME_WARNING = 11;
John Spurlock86005342014-05-23 11:58:00 -040094 private static final int MSG_LAYOUT_DIRECTION = 12;
95 private static final int MSG_ZEN_MODE_CHANGED = 13;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096
Mike Lockwood8dc1dab2011-10-27 09:52:41 -040097 // Pseudo stream type for master volume
Mike Lockwood47676902011-11-08 10:31:21 -080098 private static final int STREAM_MASTER = -100;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -070099 // Pseudo stream type for remote volume is defined in AudioService.STREAM_REMOTE_MUSIC
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400100
John Spurlock86005342014-05-23 11:58:00 -0400101 private final String mTag;
John Spurlock3346a802014-05-20 16:25:37 -0400102 protected final Context mContext;
103 private final AudioManager mAudioManager;
John Spurlock86005342014-05-23 11:58:00 -0400104 private final ZenModeController mZenController;
Marco Nelissen69f593c2009-07-28 09:55:04 -0700105 private boolean mRingIsSilent;
Amith Yamasani71def772011-10-12 12:25:24 -0700106 private boolean mVoiceCapable;
John Spurlock86005342014-05-23 11:58:00 -0400107 private boolean mZenModeCapable;
John Spurlock3bd4fee2014-05-29 20:51:09 -0400108 private int mTimeoutDelay = TIMEOUT_DELAY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109
Christopher Tatec4b78d22012-05-22 13:57:58 -0700110 // True if we want to play tones on the system stream when the master stream is specified.
111 private final boolean mPlayMasterStreamTones;
112
John Spurlock86005342014-05-23 11:58:00 -0400113
114 /** Volume panel content view */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115 private final View mView;
John Spurlock86005342014-05-23 11:58:00 -0400116 /** Dialog hosting the panel, if not embedded */
117 private final Dialog mDialog;
118 /** Parent view hosting the panel, if embedded */
119 private final ViewGroup mParent;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800120
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -0700121 /** The visible portion of the volume overlay */
122 private final ViewGroup mPanel;
John Spurlock86005342014-05-23 11:58:00 -0400123 /** Contains the slider and its touchable icons */
124 private final ViewGroup mSliderPanel;
125 /** The button that expands the dialog to show the zen panel */
126 private final ImageView mExpandButton;
127 /** Dummy divider icon that needs to vanish with the expand button */
128 private final View mExpandDivider;
129 /** The zen mode configuration panel view stub */
130 private final ViewStub mZenPanelStub;
131 /** The zen mode configuration panel view, once inflated */
132 private ZenModePanel mZenPanel;
133 /** Dummy divider icon that needs to vanish with the zen panel */
134 private final View mZenPanelDivider;
135
136 private ZenModePanel.Callback mZenPanelCallback;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800137
138 /** Currently active stream that shows up at the top of the list of sliders */
139 private int mActiveStreamType = -1;
140 /** All the slider controls mapped by stream type */
141 private HashMap<Integer,StreamControl> mStreamControls;
142
Amith Yamasani71def772011-10-12 12:25:24 -0700143 private enum StreamResources {
144 BluetoothSCOStream(AudioManager.STREAM_BLUETOOTH_SCO,
145 R.string.volume_icon_description_bluetooth,
146 R.drawable.ic_audio_bt,
147 R.drawable.ic_audio_bt,
148 false),
149 RingerStream(AudioManager.STREAM_RING,
150 R.string.volume_icon_description_ringer,
John Spurlock86005342014-05-23 11:58:00 -0400151 com.android.systemui.R.drawable.ic_ringer_audible,
152 com.android.systemui.R.drawable.ic_ringer_silent,
Amith Yamasani71def772011-10-12 12:25:24 -0700153 false),
154 VoiceStream(AudioManager.STREAM_VOICE_CALL,
155 R.string.volume_icon_description_incall,
156 R.drawable.ic_audio_phone,
157 R.drawable.ic_audio_phone,
158 false),
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700159 AlarmStream(AudioManager.STREAM_ALARM,
160 R.string.volume_alarm,
161 R.drawable.ic_audio_alarm,
162 R.drawable.ic_audio_alarm_mute,
163 false),
Amith Yamasani71def772011-10-12 12:25:24 -0700164 MediaStream(AudioManager.STREAM_MUSIC,
165 R.string.volume_icon_description_media,
166 R.drawable.ic_audio_vol,
167 R.drawable.ic_audio_vol_mute,
168 true),
169 NotificationStream(AudioManager.STREAM_NOTIFICATION,
170 R.string.volume_icon_description_notification,
John Spurlock86005342014-05-23 11:58:00 -0400171 com.android.systemui.R.drawable.ic_ringer_audible,
172 com.android.systemui.R.drawable.ic_ringer_silent,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400173 true),
174 // for now, use media resources for master volume
175 MasterStream(STREAM_MASTER,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700176 R.string.volume_icon_description_media, //FIXME should have its own description
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400177 R.drawable.ic_audio_vol,
178 R.drawable.ic_audio_vol_mute,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700179 false),
180 RemoteStream(AudioService.STREAM_REMOTE_MUSIC,
181 R.string.volume_icon_description_media, //FIXME should have its own description
182 R.drawable.ic_media_route_on_holo_dark,
183 R.drawable.ic_media_route_disabled_holo_dark,
184 false);// will be dynamically updated
Amith Yamasani71def772011-10-12 12:25:24 -0700185
186 int streamType;
187 int descRes;
188 int iconRes;
189 int iconMuteRes;
190 // RING, VOICE_CALL & BLUETOOTH_SCO are hidden unless explicitly requested
191 boolean show;
192
193 StreamResources(int streamType, int descRes, int iconRes, int iconMuteRes, boolean show) {
194 this.streamType = streamType;
195 this.descRes = descRes;
196 this.iconRes = iconRes;
197 this.iconMuteRes = iconMuteRes;
198 this.show = show;
199 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700200 }
Amith Yamasani71def772011-10-12 12:25:24 -0700201
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800202 // List of stream types and their order
Amith Yamasani71def772011-10-12 12:25:24 -0700203 private static final StreamResources[] STREAMS = {
204 StreamResources.BluetoothSCOStream,
205 StreamResources.RingerStream,
206 StreamResources.VoiceStream,
207 StreamResources.MediaStream,
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700208 StreamResources.NotificationStream,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400209 StreamResources.AlarmStream,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700210 StreamResources.MasterStream,
211 StreamResources.RemoteStream
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800212 };
213
214 /** Object that contains data for each slider */
215 private class StreamControl {
216 int streamType;
217 ViewGroup group;
218 ImageView icon;
219 SeekBar seekbarView;
220 int iconRes;
221 int iconMuteRes;
222 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800223
224 // Synchronize when accessing this
225 private ToneGenerator mToneGenerators[];
226 private Vibrator mVibrator;
227
Eric Laurentc34dcc12012-09-10 13:51:52 -0700228 private static AlertDialog sConfirmSafeVolumeDialog;
Eric Laurent0516a9e2012-09-19 11:53:03 -0700229 private static Object sConfirmSafeVolumeLock = new Object();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700230
231 private static class WarningDialogReceiver extends BroadcastReceiver
232 implements DialogInterface.OnDismissListener {
Eric Laurentfde16d52012-12-03 14:42:39 -0800233 private final Context mContext;
234 private final Dialog mDialog;
235 private final VolumePanel mVolumePanel;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700236
Eric Laurentfde16d52012-12-03 14:42:39 -0800237 WarningDialogReceiver(Context context, Dialog dialog, VolumePanel volumePanel) {
Eric Laurentc34dcc12012-09-10 13:51:52 -0700238 mContext = context;
239 mDialog = dialog;
Eric Laurentfde16d52012-12-03 14:42:39 -0800240 mVolumePanel = volumePanel;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700241 IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
242 context.registerReceiver(this, filter);
243 }
244
245 @Override
246 public void onReceive(Context context, Intent intent) {
247 mDialog.cancel();
Eric Laurentfde16d52012-12-03 14:42:39 -0800248 cleanUp();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700249 }
250
Alan Viverette494fb7b2014-04-10 18:12:56 -0700251 @Override
Eric Laurentc34dcc12012-09-10 13:51:52 -0700252 public void onDismiss(DialogInterface unused) {
253 mContext.unregisterReceiver(this);
Eric Laurentfde16d52012-12-03 14:42:39 -0800254 cleanUp();
255 }
256
257 private void cleanUp() {
Eric Laurent0516a9e2012-09-19 11:53:03 -0700258 synchronized (sConfirmSafeVolumeLock) {
259 sConfirmSafeVolumeDialog = null;
260 }
Eric Laurentfde16d52012-12-03 14:42:39 -0800261 mVolumePanel.forceTimeout();
262 mVolumePanel.updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700263 }
264 }
265
266
John Spurlock86005342014-05-23 11:58:00 -0400267 public VolumePanel(Context context, ViewGroup parent, ZenModeController zenController) {
268 mTag = String.format("VolumePanel%s.%08x", parent == null ? "Dialog" : "", hashCode());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800269 mContext = context;
John Spurlock86005342014-05-23 11:58:00 -0400270 mParent = parent;
271 mZenController = zenController;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800272 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800273
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400274 // For now, only show master volume if master volume is supported
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700275 final Resources res = context.getResources();
276 final boolean useMasterVolume = res.getBoolean(R.bool.config_useMasterVolume);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400277 if (useMasterVolume) {
278 for (int i = 0; i < STREAMS.length; i++) {
279 StreamResources streamRes = STREAMS[i];
280 streamRes.show = (streamRes.streamType == STREAM_MASTER);
281 }
282 }
John Spurlock86005342014-05-23 11:58:00 -0400283 if (LOGD) Log.d(mTag, String.format("new VolumePanel hasParent=%s", parent != null));
284 final int layoutId = com.android.systemui.R.layout.volume_panel;
285 if (parent == null) {
286 // dialog mode
287 mDialog = new Dialog(context) {
288 @Override
289 public boolean onTouchEvent(MotionEvent event) {
290 if (isShowing() && event.getAction() == MotionEvent.ACTION_OUTSIDE &&
291 sConfirmSafeVolumeDialog == null) {
292 forceTimeout();
293 return true;
294 }
295 return false;
Amith Yamasani284e6302011-09-16 18:24:47 -0700296 }
John Spurlock86005342014-05-23 11:58:00 -0400297 };
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700298
John Spurlock86005342014-05-23 11:58:00 -0400299 // Change some window properties
300 final Window window = mDialog.getWindow();
301 final LayoutParams lp = window.getAttributes();
302 lp.token = null;
303 // Offset from the top
304 lp.y = res.getDimensionPixelOffset(com.android.systemui.R.dimen.volume_panel_top);
305 lp.width = res.getDimensionPixelSize(com.android.systemui.R.dimen.volume_panel_width);
306 lp.type = LayoutParams.TYPE_VOLUME_OVERLAY;
307 lp.windowAnimations = R.style.Animation_VolumePanel;
308 window.setBackgroundDrawableResource(com.android.systemui.R.drawable.qs_panel_background);
309 window.setAttributes(lp);
310 window.setGravity(Gravity.TOP);
311 window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
312 window.requestFeature(Window.FEATURE_NO_TITLE);
313 window.addFlags(LayoutParams.FLAG_NOT_FOCUSABLE
314 | LayoutParams.FLAG_NOT_TOUCH_MODAL
315 | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
316 mDialog.setCanceledOnTouchOutside(true);
317 mDialog.setContentView(layoutId);
318 mDialog.setOnDismissListener(new OnDismissListener() {
319 @Override
320 public void onDismiss(DialogInterface dialog) {
321 mActiveStreamType = -1;
322 mAudioManager.forceVolumeControlStream(mActiveStreamType);
323 }
324 });
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700325
John Spurlock86005342014-05-23 11:58:00 -0400326 mDialog.create();
Alan Viverette494fb7b2014-04-10 18:12:56 -0700327
John Spurlock86005342014-05-23 11:58:00 -0400328 mView = window.findViewById(R.id.content);
329 mView.setOnTouchListener(new View.OnTouchListener() {
330 @Override
331 public boolean onTouch(View v, MotionEvent event) {
332 resetTimeout();
333 return false;
334 }
335 });
Alan Viverette494fb7b2014-04-10 18:12:56 -0700336
John Spurlock86005342014-05-23 11:58:00 -0400337 } else {
338 // embedded mode
339 mDialog = null;
340 mView = LayoutInflater.from(mContext).inflate(layoutId, parent, true);
341 }
342 mPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.visible_panel);
343 mSliderPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.slider_panel);
344 mExpandButton = (ImageView) mView.findViewById(com.android.systemui.R.id.expand_button);
345 mExpandDivider = mView.findViewById(com.android.systemui.R.id.expand_button_divider);
346 mZenPanelStub = (ViewStub)mView.findViewById(com.android.systemui.R.id.zen_panel_stub);
347 mZenPanelDivider = mView.findViewById(com.android.systemui.R.id.zen_panel_divider);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800348
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800349 mToneGenerators = new ToneGenerator[AudioSystem.getNumStreamTypes()];
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700350 mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
Amith Yamasani71def772011-10-12 12:25:24 -0700351 mVoiceCapable = context.getResources().getBoolean(R.bool.config_voice_capable);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700352
John Spurlock86005342014-05-23 11:58:00 -0400353 mZenModeCapable = !useMasterVolume && mZenController != null;
354 mZenPanelDivider.setVisibility(View.GONE);
355 mExpandButton.setOnClickListener(mClickListener);
356 updateZenMode(mZenController == null ? false : mZenController.isZen());
357 mZenController.addCallback(mZenCallback);
Amith Yamasani42722bf2011-07-22 10:34:27 -0700358
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700359 final boolean masterVolumeOnly = res.getBoolean(R.bool.config_useMasterVolume);
360 final boolean masterVolumeKeySounds = res.getBoolean(R.bool.config_useVolumeKeySounds);
Christopher Tatec4b78d22012-05-22 13:57:58 -0700361 mPlayMasterStreamTones = masterVolumeOnly && masterVolumeKeySounds;
362
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800363 listenToRingerMode();
364 }
365
John Spurlock86005342014-05-23 11:58:00 -0400366 private void setLayoutDirection(int layoutDirection) {
Fabrice Di Meglio8c028842013-01-09 18:20:38 -0800367 mPanel.setLayoutDirection(layoutDirection);
368 updateStates();
369 }
370
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800371 private void listenToRingerMode() {
372 final IntentFilter filter = new IntentFilter();
373 filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
374 mContext.registerReceiver(new BroadcastReceiver() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700375 @Override
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800376 public void onReceive(Context context, Intent intent) {
377 final String action = intent.getAction();
378
379 if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) {
380 removeMessages(MSG_RINGER_MODE_CHANGED);
381 sendMessage(obtainMessage(MSG_RINGER_MODE_CHANGED));
382 }
383 }
384 }, filter);
385 }
386
387 private boolean isMuted(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400388 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700389 return mAudioManager.isMasterMute();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700390 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
John Spurlock3346a802014-05-20 16:25:37 -0400391 return (mAudioManager.getRemoteStreamVolume() <= 0);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400392 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700393 return mAudioManager.isStreamMute(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400394 }
395 }
396
397 private int getStreamMaxVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400398 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700399 return mAudioManager.getMasterMaxVolume();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700400 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
John Spurlock3346a802014-05-20 16:25:37 -0400401 return mAudioManager.getRemoteStreamMaxVolume();
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400402 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700403 return mAudioManager.getStreamMaxVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400404 }
405 }
406
407 private int getStreamVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400408 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700409 return mAudioManager.getMasterVolume();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700410 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
John Spurlock3346a802014-05-20 16:25:37 -0400411 return mAudioManager.getRemoteStreamVolume();
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400412 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700413 return mAudioManager.getStreamVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400414 }
415 }
416
417 private void setStreamVolume(int streamType, int index, int flags) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400418 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700419 mAudioManager.setMasterVolume(index, flags);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700420 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
John Spurlock3346a802014-05-20 16:25:37 -0400421 mAudioManager.setRemoteStreamVolume(index);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400422 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700423 mAudioManager.setStreamVolume(streamType, index, flags);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400424 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800425 }
426
427 private void createSliders() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700428 final Resources res = mContext.getResources();
429 final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
430 Context.LAYOUT_INFLATER_SERVICE);
431
Amith Yamasani71def772011-10-12 12:25:24 -0700432 mStreamControls = new HashMap<Integer, StreamControl>(STREAMS.length);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700433
Amith Yamasani71def772011-10-12 12:25:24 -0700434 for (int i = 0; i < STREAMS.length; i++) {
435 StreamResources streamRes = STREAMS[i];
Alan Viverette494fb7b2014-04-10 18:12:56 -0700436
437 final int streamType = streamRes.streamType;
Alan Viverette494fb7b2014-04-10 18:12:56 -0700438
439 final StreamControl sc = new StreamControl();
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700440 sc.streamType = streamType;
John Spurlock86005342014-05-23 11:58:00 -0400441 sc.group = (ViewGroup) inflater.inflate(
442 com.android.systemui.R.layout.volume_panel_item, null);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800443 sc.group.setTag(sc);
John Spurlock86005342014-05-23 11:58:00 -0400444 sc.icon = (ImageView) sc.group.findViewById(com.android.systemui.R.id.stream_icon);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800445 sc.icon.setTag(sc);
Amith Yamasani71def772011-10-12 12:25:24 -0700446 sc.icon.setContentDescription(res.getString(streamRes.descRes));
447 sc.iconRes = streamRes.iconRes;
448 sc.iconMuteRes = streamRes.iconMuteRes;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800449 sc.icon.setImageResource(sc.iconRes);
John Spurlock86005342014-05-23 11:58:00 -0400450 sc.seekbarView = (SeekBar) sc.group.findViewById(com.android.systemui.R.id.seekbar);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700451 final int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO ||
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700452 streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400453 sc.seekbarView.setMax(getStreamMaxVolume(streamType) + plusOne);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700454 sc.seekbarView.setOnSeekBarChangeListener(mSeekListener);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800455 sc.seekbarView.setTag(sc);
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700456 mStreamControls.put(streamType, sc);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800457 }
458 }
459
460 private void reorderSliders(int activeStreamType) {
John Spurlock86005342014-05-23 11:58:00 -0400461 mSliderPanel.removeAllViews();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800462
Alan Viverette494fb7b2014-04-10 18:12:56 -0700463 final StreamControl active = mStreamControls.get(activeStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800464 if (active == null) {
465 Log.e("VolumePanel", "Missing stream type! - " + activeStreamType);
466 mActiveStreamType = -1;
467 } else {
John Spurlock86005342014-05-23 11:58:00 -0400468 mSliderPanel.addView(active.group);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800469 mActiveStreamType = activeStreamType;
470 active.group.setVisibility(View.VISIBLE);
471 updateSlider(active);
John Spurlock86005342014-05-23 11:58:00 -0400472 updateZenMode(mZenController == null ? false : mZenController.isZen());
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800473 }
474 }
475
476 /** Update the mute and progress state of a slider */
477 private void updateSlider(StreamControl sc) {
Eric Laurent8c787522012-05-14 14:09:43 -0700478 sc.seekbarView.setProgress(getStreamVolume(sc.streamType));
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800479 final boolean muted = isMuted(sc.streamType);
Fabrice Di Meglio8c028842013-01-09 18:20:38 -0800480 // Force reloading the image resource
481 sc.icon.setImageDrawable(null);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800482 sc.icon.setImageResource(muted ? sc.iconMuteRes : sc.iconRes);
Eric Laurent8a51aca2013-03-25 18:36:35 -0700483 if (((sc.streamType == AudioManager.STREAM_RING) ||
484 (sc.streamType == AudioManager.STREAM_NOTIFICATION)) &&
Eric Laurent8c787522012-05-14 14:09:43 -0700485 mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
John Spurlock86005342014-05-23 11:58:00 -0400486 sc.icon.setImageResource(com.android.systemui.R.drawable.ic_ringer_vibrate);
Amith Yamasanic696a532011-10-28 17:02:37 -0700487 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700488 if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
489 // never disable touch interactions for remote playback, the muting is not tied to
490 // the state of the phone.
491 sc.seekbarView.setEnabled(true);
Eric Laurentfde16d52012-12-03 14:42:39 -0800492 } else if ((sc.streamType != mAudioManager.getMasterStreamType() && muted) ||
493 (sConfirmSafeVolumeDialog != null)) {
Eric Laurent8c787522012-05-14 14:09:43 -0700494 sc.seekbarView.setEnabled(false);
495 } else {
496 sc.seekbarView.setEnabled(true);
497 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800498 }
499
John Spurlock86005342014-05-23 11:58:00 -0400500 public void setZenModePanelCallback(ZenModePanel.Callback callback) {
501 mZenPanelCallback = callback;
502 }
503
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800504 private void expand() {
John Spurlock86005342014-05-23 11:58:00 -0400505 if (LOGD) Log.d(mTag, "expand mZenPanel=" + mZenPanel);
506 if (mZenPanel == null) {
507 mZenPanel = (ZenModePanel) mZenPanelStub.inflate();
508 mZenPanel.init(mZenController);
509 mZenPanel.setCallback(new ZenModePanel.Callback() {
510 @Override
511 public void onMoreSettings() {
512 if (mZenPanelCallback != null) {
513 mZenPanelCallback.onMoreSettings();
514 }
515 }
516
517 @Override
518 public void onInteraction() {
John Spurlock3bd4fee2014-05-29 20:51:09 -0400519 resetTimeout();
John Spurlock86005342014-05-23 11:58:00 -0400520 if (mZenPanelCallback != null) {
521 mZenPanelCallback.onInteraction();
522 }
523 }
524 });
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800525 }
John Spurlock86005342014-05-23 11:58:00 -0400526 mZenPanel.setVisibility(View.VISIBLE);
527 mZenPanelDivider.setVisibility(View.VISIBLE);
John Spurlock3bd4fee2014-05-29 20:51:09 -0400528 mTimeoutDelay = TIMEOUT_DELAY_EXPANDED;
529 resetTimeout();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800530 }
531
532 private void collapse() {
John Spurlock86005342014-05-23 11:58:00 -0400533 if (LOGD) Log.d(mTag, "collapse mZenPanel=" + mZenPanel);
534 if (mZenPanel != null) {
535 mZenPanel.setVisibility(View.GONE);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800536 }
John Spurlock86005342014-05-23 11:58:00 -0400537 mZenPanelDivider.setVisibility(View.GONE);
John Spurlock3bd4fee2014-05-29 20:51:09 -0400538 mTimeoutDelay = TIMEOUT_DELAY;
539 resetTimeout();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800540 }
541
Eric Laurentfde16d52012-12-03 14:42:39 -0800542 public void updateStates() {
John Spurlock86005342014-05-23 11:58:00 -0400543 final int count = mSliderPanel.getChildCount();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800544 for (int i = 0; i < count; i++) {
John Spurlock86005342014-05-23 11:58:00 -0400545 StreamControl sc = (StreamControl) mSliderPanel.getChildAt(i).getTag();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800546 updateSlider(sc);
547 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800548 }
549
John Spurlock86005342014-05-23 11:58:00 -0400550 private void updateZenMode(boolean zen) {
551 if (mZenModeCapable) {
552 final boolean show = mActiveStreamType == AudioManager.STREAM_NOTIFICATION
553 || mActiveStreamType == AudioManager.STREAM_RING;
554 mExpandButton.setVisibility(show ? View.VISIBLE : View.GONE);
555 mExpandDivider.setVisibility(show ? View.VISIBLE : View.GONE);
556 mExpandButton.setImageResource(zen ? com.android.systemui.R.drawable.ic_vol_zen_on
557 : com.android.systemui.R.drawable.ic_vol_zen_off);
558 } else {
559 mExpandButton.setVisibility(View.GONE);
560 mExpandDivider.setVisibility(View.GONE);
561 }
562 }
563
564 public void postZenModeChanged(boolean zen) {
565 removeMessages(MSG_ZEN_MODE_CHANGED);
566 obtainMessage(MSG_ZEN_MODE_CHANGED, zen ? 1 : 0).sendToTarget();
567 }
568
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800569 public void postVolumeChanged(int streamType, int flags) {
570 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700571 synchronized (this) {
572 if (mStreamControls == null) {
573 createSliders();
574 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800575 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800576 removeMessages(MSG_FREE_RESOURCES);
577 obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget();
578 }
579
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700580 public void postRemoteVolumeChanged(int streamType, int flags) {
581 if (hasMessages(MSG_REMOTE_VOLUME_CHANGED)) return;
582 synchronized (this) {
583 if (mStreamControls == null) {
584 createSliders();
585 }
586 }
587 removeMessages(MSG_FREE_RESOURCES);
588 obtainMessage(MSG_REMOTE_VOLUME_CHANGED, streamType, flags).sendToTarget();
589 }
590
591 public void postRemoteSliderVisibility(boolean visible) {
592 obtainMessage(MSG_SLIDER_VISIBILITY_CHANGED,
593 AudioService.STREAM_REMOTE_MUSIC, visible ? 1 : 0).sendToTarget();
594 }
595
596 /**
597 * Called by AudioService when it has received new remote playback information that
598 * would affect the VolumePanel display (mainly volumes). The difference with
599 * {@link #postRemoteVolumeChanged(int, int)} is that the handling of the posted message
600 * (MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN) will only update the volume slider if it is being
601 * displayed.
602 * This special code path is due to the fact that remote volume updates arrive to AudioService
603 * asynchronously. So after AudioService has sent the volume update (which should be treated
604 * as a request to update the volume), the application will likely set a new volume. If the UI
605 * is still up, we need to refresh the display to show this new value.
606 */
607 public void postHasNewRemotePlaybackInfo() {
608 if (hasMessages(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN)) return;
609 // don't create or prevent resources to be freed, if they disappear, this update came too
610 // late and shouldn't warrant the panel to be displayed longer
611 obtainMessage(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN).sendToTarget();
612 }
613
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400614 public void postMasterVolumeChanged(int flags) {
615 postVolumeChanged(STREAM_MASTER, flags);
616 }
617
Mike Lockwoodce952c82011-11-14 10:47:42 -0800618 public void postMuteChanged(int streamType, int flags) {
619 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700620 synchronized (this) {
621 if (mStreamControls == null) {
622 createSliders();
623 }
Mike Lockwoodce952c82011-11-14 10:47:42 -0800624 }
625 removeMessages(MSG_FREE_RESOURCES);
626 obtainMessage(MSG_MUTE_CHANGED, streamType, flags).sendToTarget();
627 }
628
629 public void postMasterMuteChanged(int flags) {
630 postMuteChanged(STREAM_MASTER, flags);
631 }
632
Eric Laurentfde16d52012-12-03 14:42:39 -0800633 public void postDisplaySafeVolumeWarning(int flags) {
Eric Laurent0516a9e2012-09-19 11:53:03 -0700634 if (hasMessages(MSG_DISPLAY_SAFE_VOLUME_WARNING)) return;
Eric Laurentfde16d52012-12-03 14:42:39 -0800635 obtainMessage(MSG_DISPLAY_SAFE_VOLUME_WARNING, flags, 0).sendToTarget();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700636 }
637
John Spurlock3346a802014-05-20 16:25:37 -0400638 public void postDismiss() {
John Spurlock86005342014-05-23 11:58:00 -0400639 forceTimeout();
640 }
641
642 public void postLayoutDirection(int layoutDirection) {
643 removeMessages(MSG_LAYOUT_DIRECTION);
644 obtainMessage(MSG_LAYOUT_DIRECTION, layoutDirection).sendToTarget();
John Spurlock3346a802014-05-20 16:25:37 -0400645 }
646
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800647 /**
648 * Override this if you have other work to do when the volume changes (for
649 * example, vibrating, playing a sound, etc.). Make sure to call through to
650 * the superclass implementation.
651 */
652 protected void onVolumeChanged(int streamType, int flags) {
653
John Spurlock86005342014-05-23 11:58:00 -0400654 if (LOGD) Log.d(mTag, "onVolumeChanged(streamType: " + streamType + ", flags: " + flags + ")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800655
656 if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
Amith Yamasania6549862012-05-30 17:29:28 -0700657 synchronized (this) {
658 if (mActiveStreamType != streamType) {
659 reorderSliders(streamType);
660 }
661 onShowVolumeChanged(streamType, flags);
Amith Yamasanie3361b82011-02-10 18:20:50 -0800662 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800663 }
664
Marco Nelissen69f593c2009-07-28 09:55:04 -0700665 if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800666 removeMessages(MSG_PLAY_SOUND);
667 sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);
668 }
669
670 if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) {
671 removeMessages(MSG_PLAY_SOUND);
672 removeMessages(MSG_VIBRATE);
673 onStopSounds();
674 }
675
676 removeMessages(MSG_FREE_RESOURCES);
677 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800678 resetTimeout();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800679 }
680
Mike Lockwoodce952c82011-11-14 10:47:42 -0800681 protected void onMuteChanged(int streamType, int flags) {
682
John Spurlock86005342014-05-23 11:58:00 -0400683 if (LOGD) Log.d(mTag, "onMuteChanged(streamType: " + streamType + ", flags: " + flags + ")");
Mike Lockwoodce952c82011-11-14 10:47:42 -0800684
685 StreamControl sc = mStreamControls.get(streamType);
686 if (sc != null) {
687 sc.icon.setImageResource(isMuted(sc.streamType) ? sc.iconMuteRes : sc.iconRes);
688 }
689
690 onVolumeChanged(streamType, flags);
691 }
692
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800693 protected void onShowVolumeChanged(int streamType, int flags) {
Eric Laurent8c787522012-05-14 14:09:43 -0700694 int index = getStreamVolume(streamType);
Eric Laurentd72d51c2011-02-03 18:47:47 -0800695
Marco Nelissen69f593c2009-07-28 09:55:04 -0700696 mRingIsSilent = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800697
698 if (LOGD) {
John Spurlock86005342014-05-23 11:58:00 -0400699 Log.d(mTag, "onShowVolumeChanged(streamType: " + streamType
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800700 + ", flags: " + flags + "), index: " + index);
701 }
702
703 // get max volume for progress bar
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800704
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400705 int max = getStreamMaxVolume(streamType);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800706
707 switch (streamType) {
708
709 case AudioManager.STREAM_RING: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800710// setRingerIcon();
Marco Nelissen69f593c2009-07-28 09:55:04 -0700711 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
712 mContext, RingtoneManager.TYPE_RINGTONE);
713 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700714 mRingIsSilent = true;
715 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800716 break;
717 }
718
719 case AudioManager.STREAM_MUSIC: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800720 // Special case for when Bluetooth is active for music
Glenn Kasten8b4b97a2011-02-04 13:54:26 -0800721 if ((mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC) &
722 (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP |
723 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
724 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800725 setMusicIcon(R.drawable.ic_audio_bt, R.drawable.ic_audio_bt_mute);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800726 } else {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800727 setMusicIcon(R.drawable.ic_audio_vol, R.drawable.ic_audio_vol_mute);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800728 }
729 break;
730 }
731
732 case AudioManager.STREAM_VOICE_CALL: {
733 /*
734 * For in-call voice call volume, there is no inaudible volume.
735 * Rescale the UI control so the progress bar doesn't go all
736 * the way to zero and don't show the mute icon.
737 */
738 index++;
739 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800740 break;
741 }
742
743 case AudioManager.STREAM_ALARM: {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800744 break;
745 }
746
747 case AudioManager.STREAM_NOTIFICATION: {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700748 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
749 mContext, RingtoneManager.TYPE_NOTIFICATION);
750 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700751 mRingIsSilent = true;
752 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800753 break;
754 }
755
756 case AudioManager.STREAM_BLUETOOTH_SCO: {
757 /*
758 * For in-call voice call volume, there is no inaudible volume.
759 * Rescale the UI control so the progress bar doesn't go all
760 * the way to zero and don't show the mute icon.
761 */
762 index++;
763 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800764 break;
765 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700766
767 case AudioService.STREAM_REMOTE_MUSIC: {
John Spurlock86005342014-05-23 11:58:00 -0400768 if (LOGD) { Log.d(mTag, "showing remote volume "+index+" over "+ max); }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700769 break;
770 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800771 }
772
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800773 StreamControl sc = mStreamControls.get(streamType);
774 if (sc != null) {
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700775 if (sc.seekbarView.getMax() != max) {
776 sc.seekbarView.setMax(max);
777 }
Eric Laurent4bbcc652012-09-24 14:26:30 -0700778
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800779 sc.seekbarView.setProgress(index);
Eric Laurent4bbcc652012-09-24 14:26:30 -0700780 if (((flags & AudioManager.FLAG_FIXED_VOLUME) != 0) ||
781 (streamType != mAudioManager.getMasterStreamType() &&
782 streamType != AudioService.STREAM_REMOTE_MUSIC &&
Eric Laurentfde16d52012-12-03 14:42:39 -0800783 isMuted(streamType)) ||
784 sConfirmSafeVolumeDialog != null) {
Eric Laurent8c787522012-05-14 14:09:43 -0700785 sc.seekbarView.setEnabled(false);
786 } else {
787 sc.seekbarView.setEnabled(true);
788 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800789 }
790
John Spurlock86005342014-05-23 11:58:00 -0400791 if (!isShowing()) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700792 int stream = (streamType == AudioService.STREAM_REMOTE_MUSIC) ? -1 : streamType;
793 // when the stream is for remote playback, use -1 to reset the stream type evaluation
794 mAudioManager.forceVolumeControlStream(stream);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700795
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800796 // Showing dialog - use collapsed state
John Spurlock86005342014-05-23 11:58:00 -0400797 if (mZenModeCapable) {
Amith Yamasani42722bf2011-07-22 10:34:27 -0700798 collapse();
799 }
John Spurlock86005342014-05-23 11:58:00 -0400800 if (mDialog != null) {
801 mDialog.show();
802 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800803 }
804
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800805 // Do a little vibrate if applicable (only when going into vibrate mode)
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700806 if ((streamType != AudioService.STREAM_REMOTE_MUSIC) &&
807 ((flags & AudioManager.FLAG_VIBRATE) != 0) &&
John Spurlock3346a802014-05-20 16:25:37 -0400808 mAudioManager.isStreamAffectedByRingerMode(streamType) &&
Eric Laurent8c787522012-05-14 14:09:43 -0700809 mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800810 sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY);
811 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800812 }
813
John Spurlock86005342014-05-23 11:58:00 -0400814 private boolean isShowing() {
815 return mDialog != null ? mDialog.isShowing() : mParent.isAttachedToWindow();
816 }
817
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800818 protected void onPlaySound(int streamType, int flags) {
819
820 if (hasMessages(MSG_STOP_SOUNDS)) {
821 removeMessages(MSG_STOP_SOUNDS);
822 // Force stop right now
823 onStopSounds();
824 }
825
826 synchronized (this) {
827 ToneGenerator toneGen = getOrCreateToneGenerator(streamType);
Eric Laurent733a42b2011-01-19 10:41:57 -0800828 if (toneGen != null) {
829 toneGen.startTone(ToneGenerator.TONE_PROP_BEEP);
830 sendMessageDelayed(obtainMessage(MSG_STOP_SOUNDS), BEEP_DURATION);
831 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800832 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800833 }
834
835 protected void onStopSounds() {
836
837 synchronized (this) {
838 int numStreamTypes = AudioSystem.getNumStreamTypes();
839 for (int i = numStreamTypes - 1; i >= 0; i--) {
840 ToneGenerator toneGen = mToneGenerators[i];
841 if (toneGen != null) {
842 toneGen.stopTone();
843 }
844 }
845 }
846 }
847
848 protected void onVibrate() {
849
850 // Make sure we ended up in vibrate ringer mode
Eric Laurent8c787522012-05-14 14:09:43 -0700851 if (mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800852 return;
853 }
854
John Spurlockf9e1a0b2014-03-19 22:09:06 -0400855 mVibrator.vibrate(VIBRATE_DURATION, AudioManager.STREAM_SYSTEM);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800856 }
857
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700858 protected void onRemoteVolumeChanged(int streamType, int flags) {
859 // streamType is the real stream type being affected, but for the UI sliders, we
860 // refer to AudioService.STREAM_REMOTE_MUSIC. We still play the beeps on the real
861 // stream type.
John Spurlock86005342014-05-23 11:58:00 -0400862 if (LOGD) Log.d(mTag, "onRemoteVolumeChanged(stream:"+streamType+", flags: " + flags + ")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700863
John Spurlock86005342014-05-23 11:58:00 -0400864 if (((flags & AudioManager.FLAG_SHOW_UI) != 0) || isShowing()) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700865 synchronized (this) {
866 if (mActiveStreamType != AudioService.STREAM_REMOTE_MUSIC) {
867 reorderSliders(AudioService.STREAM_REMOTE_MUSIC);
868 }
869 onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, flags);
870 }
871 } else {
John Spurlock86005342014-05-23 11:58:00 -0400872 if (LOGD) Log.d(mTag, "not calling onShowVolumeChanged(), no FLAG_SHOW_UI or no UI");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700873 }
874
875 if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {
876 removeMessages(MSG_PLAY_SOUND);
877 sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);
878 }
879
880 if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) {
881 removeMessages(MSG_PLAY_SOUND);
882 removeMessages(MSG_VIBRATE);
883 onStopSounds();
884 }
885
886 removeMessages(MSG_FREE_RESOURCES);
887 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700888 resetTimeout();
889 }
890
891 protected void onRemoteVolumeUpdateIfShown() {
John Spurlock86005342014-05-23 11:58:00 -0400892 if (LOGD) Log.d(mTag, "onRemoteVolumeUpdateIfShown()");
893 if (isShowing()
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700894 && (mActiveStreamType == AudioService.STREAM_REMOTE_MUSIC)
895 && (mStreamControls != null)) {
896 onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, 0);
897 }
898 }
899
900
901 /**
902 * Handler for MSG_SLIDER_VISIBILITY_CHANGED
903 * Hide or show a slider
904 * @param streamType can be a valid stream type value, or VolumePanel.STREAM_MASTER,
905 * or AudioService.STREAM_REMOTE_MUSIC
906 * @param visible
907 */
908 synchronized protected void onSliderVisibilityChanged(int streamType, int visible) {
John Spurlock86005342014-05-23 11:58:00 -0400909 if (LOGD) Log.d(mTag, "onSliderVisibilityChanged(stream="+streamType+", visi="+visible+")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700910 boolean isVisible = (visible == 1);
911 for (int i = STREAMS.length - 1 ; i >= 0 ; i--) {
912 StreamResources streamRes = STREAMS[i];
913 if (streamRes.streamType == streamType) {
914 streamRes.show = isVisible;
915 if (!isVisible && (mActiveStreamType == streamType)) {
916 mActiveStreamType = -1;
917 }
918 break;
919 }
920 }
921 }
922
Eric Laurentfde16d52012-12-03 14:42:39 -0800923 protected void onDisplaySafeVolumeWarning(int flags) {
John Spurlock86005342014-05-23 11:58:00 -0400924 if ((flags & AudioManager.FLAG_SHOW_UI) != 0 || isShowing()) {
Eric Laurentfde16d52012-12-03 14:42:39 -0800925 synchronized (sConfirmSafeVolumeLock) {
926 if (sConfirmSafeVolumeDialog != null) {
927 return;
928 }
929 sConfirmSafeVolumeDialog = new AlertDialog.Builder(mContext)
930 .setMessage(com.android.internal.R.string.safe_media_volume_warning)
931 .setPositiveButton(com.android.internal.R.string.yes,
932 new DialogInterface.OnClickListener() {
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700933 @Override
Eric Laurentfde16d52012-12-03 14:42:39 -0800934 public void onClick(DialogInterface dialog, int which) {
John Spurlock3346a802014-05-20 16:25:37 -0400935 mAudioManager.disableSafeMediaVolume();
Eric Laurentfde16d52012-12-03 14:42:39 -0800936 }
937 })
938 .setNegativeButton(com.android.internal.R.string.no, null)
939 .setIconAttribute(android.R.attr.alertDialogIcon)
940 .create();
941 final WarningDialogReceiver warning = new WarningDialogReceiver(mContext,
942 sConfirmSafeVolumeDialog, this);
Eric Laurent0516a9e2012-09-19 11:53:03 -0700943
Eric Laurentfde16d52012-12-03 14:42:39 -0800944 sConfirmSafeVolumeDialog.setOnDismissListener(warning);
945 sConfirmSafeVolumeDialog.getWindow().setType(
946 WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
947 sConfirmSafeVolumeDialog.show();
948 }
949 updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700950 }
Eric Laurentfde16d52012-12-03 14:42:39 -0800951 resetTimeout();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700952 }
953
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800954 /**
955 * Lock on this VolumePanel instance as long as you use the returned ToneGenerator.
956 */
957 private ToneGenerator getOrCreateToneGenerator(int streamType) {
Christopher Tatec4b78d22012-05-22 13:57:58 -0700958 if (streamType == STREAM_MASTER) {
959 // For devices that use the master volume setting only but still want to
960 // play a volume-changed tone, direct the master volume pseudostream to
961 // the system stream's tone generator.
962 if (mPlayMasterStreamTones) {
963 streamType = AudioManager.STREAM_SYSTEM;
964 } else {
965 return null;
966 }
967 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800968 synchronized (this) {
969 if (mToneGenerators[streamType] == null) {
Eric Laurent733a42b2011-01-19 10:41:57 -0800970 try {
971 mToneGenerators[streamType] = new ToneGenerator(streamType, MAX_VOLUME);
972 } catch (RuntimeException e) {
973 if (LOGD) {
John Spurlock86005342014-05-23 11:58:00 -0400974 Log.d(mTag, "ToneGenerator constructor failed with "
Eric Laurent733a42b2011-01-19 10:41:57 -0800975 + "RuntimeException: " + e);
976 }
977 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800978 }
Eric Laurent733a42b2011-01-19 10:41:57 -0800979 return mToneGenerators[streamType];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800980 }
981 }
982
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800983
984 /**
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800985 * Switch between icons because Bluetooth music is same as music volume, but with
986 * different icons.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800987 */
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800988 private void setMusicIcon(int resId, int resMuteId) {
989 StreamControl sc = mStreamControls.get(AudioManager.STREAM_MUSIC);
990 if (sc != null) {
991 sc.iconRes = resId;
992 sc.iconMuteRes = resMuteId;
993 sc.icon.setImageResource(isMuted(sc.streamType) ? sc.iconMuteRes : sc.iconRes);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800994 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800995 }
996
997 protected void onFreeResources() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800998 synchronized (this) {
999 for (int i = mToneGenerators.length - 1; i >= 0; i--) {
1000 if (mToneGenerators[i] != null) {
1001 mToneGenerators[i].release();
1002 }
1003 mToneGenerators[i] = null;
1004 }
1005 }
1006 }
1007
1008 @Override
1009 public void handleMessage(Message msg) {
1010 switch (msg.what) {
1011
1012 case MSG_VOLUME_CHANGED: {
1013 onVolumeChanged(msg.arg1, msg.arg2);
1014 break;
1015 }
1016
Mike Lockwoodce952c82011-11-14 10:47:42 -08001017 case MSG_MUTE_CHANGED: {
1018 onMuteChanged(msg.arg1, msg.arg2);
1019 break;
1020 }
1021
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001022 case MSG_FREE_RESOURCES: {
1023 onFreeResources();
1024 break;
1025 }
1026
1027 case MSG_STOP_SOUNDS: {
1028 onStopSounds();
1029 break;
1030 }
1031
1032 case MSG_PLAY_SOUND: {
1033 onPlaySound(msg.arg1, msg.arg2);
1034 break;
1035 }
1036
1037 case MSG_VIBRATE: {
1038 onVibrate();
1039 break;
1040 }
1041
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001042 case MSG_TIMEOUT: {
John Spurlock86005342014-05-23 11:58:00 -04001043 if (isShowing()) {
1044 if (mDialog != null) {
1045 mDialog.dismiss();
John Spurlockf71205c2014-05-29 10:17:51 -04001046 mActiveStreamType = -1;
John Spurlock86005342014-05-23 11:58:00 -04001047 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001048 }
Eric Laurentfde16d52012-12-03 14:42:39 -08001049 synchronized (sConfirmSafeVolumeLock) {
1050 if (sConfirmSafeVolumeDialog != null) {
1051 sConfirmSafeVolumeDialog.dismiss();
1052 }
1053 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001054 break;
1055 }
1056 case MSG_RINGER_MODE_CHANGED: {
John Spurlock86005342014-05-23 11:58:00 -04001057 if (isShowing()) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001058 updateStates();
1059 }
1060 break;
1061 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001062
1063 case MSG_REMOTE_VOLUME_CHANGED: {
1064 onRemoteVolumeChanged(msg.arg1, msg.arg2);
1065 break;
1066 }
1067
1068 case MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN:
1069 onRemoteVolumeUpdateIfShown();
1070 break;
1071
1072 case MSG_SLIDER_VISIBILITY_CHANGED:
1073 onSliderVisibilityChanged(msg.arg1, msg.arg2);
1074 break;
Eric Laurentc34dcc12012-09-10 13:51:52 -07001075
1076 case MSG_DISPLAY_SAFE_VOLUME_WARNING:
Eric Laurentfde16d52012-12-03 14:42:39 -08001077 onDisplaySafeVolumeWarning(msg.arg1);
Eric Laurentc34dcc12012-09-10 13:51:52 -07001078 break;
John Spurlock86005342014-05-23 11:58:00 -04001079
1080 case MSG_LAYOUT_DIRECTION:
1081 setLayoutDirection(msg.arg1);
1082 break;
1083
1084 case MSG_ZEN_MODE_CHANGED:
1085 updateZenMode(msg.arg1 != 0);
1086 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001087 }
1088 }
1089
John Spurlock86005342014-05-23 11:58:00 -04001090 public void resetTimeout() {
1091 if (LOGD) Log.d(mTag, "resetTimeout at " + System.currentTimeMillis());
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001092 removeMessages(MSG_TIMEOUT);
John Spurlock3bd4fee2014-05-29 20:51:09 -04001093 sendEmptyMessageDelayed(MSG_TIMEOUT, mTimeoutDelay);
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001094 }
1095
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001096 private void forceTimeout() {
1097 removeMessages(MSG_TIMEOUT);
John Spurlock86005342014-05-23 11:58:00 -04001098 sendEmptyMessage(MSG_TIMEOUT);
1099 }
1100
1101 public ZenModeController getZenController() {
1102 return mZenController;
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001103 }
1104
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001105 private final OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
1106 @Override
1107 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
1108 final Object tag = seekBar.getTag();
1109 if (fromUser && tag instanceof StreamControl) {
1110 StreamControl sc = (StreamControl) tag;
1111 if (getStreamVolume(sc.streamType) != progress) {
1112 setStreamVolume(sc.streamType, progress, 0);
1113 }
1114 }
1115 resetTimeout();
1116 }
1117
1118 @Override
1119 public void onStartTrackingTouch(SeekBar seekBar) {
1120 }
1121
1122 @Override
1123 public void onStopTrackingTouch(SeekBar seekBar) {
1124 final Object tag = seekBar.getTag();
1125 if (tag instanceof StreamControl) {
1126 StreamControl sc = (StreamControl) tag;
1127 // Because remote volume updates are asynchronous, AudioService
1128 // might have received a new remote volume value since the
1129 // finger adjusted the slider. So when the progress of the
1130 // slider isn't being tracked anymore, adjust the slider to the
1131 // last "published" remote volume value, so the UI reflects the
1132 // actual volume.
1133 if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
1134 seekBar.setProgress(getStreamVolume(AudioService.STREAM_REMOTE_MUSIC));
1135 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001136 }
1137 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001138 };
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001139
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001140 private final View.OnClickListener mClickListener = new View.OnClickListener() {
1141 @Override
1142 public void onClick(View v) {
John Spurlock86005342014-05-23 11:58:00 -04001143 if (v == mExpandButton && mZenController != null) {
1144 final boolean newZen = !mZenController.isZen();
John Spurlock3bd4fee2014-05-29 20:51:09 -04001145 AsyncTask.execute(new Runnable() {
1146 @Override
1147 public void run() {
1148 mZenController.setZen(newZen);
1149 }
1150 });
John Spurlock86005342014-05-23 11:58:00 -04001151 if (newZen) {
1152 expand();
1153 } else {
1154 collapse();
1155 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001156 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001157 resetTimeout();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001158 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001159 };
John Spurlock86005342014-05-23 11:58:00 -04001160
1161 private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
1162 public void onZenChanged(boolean zen) {
1163 updateZenMode(zen);
1164 }
1165 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001166}