blob: 06f4c2e17e13ca6eea70608803dd9a9e206ea691 [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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.os.Handler;
35import android.os.Message;
36import android.os.Vibrator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037import android.util.Log;
John Spurlock3346a802014-05-20 16:25:37 -040038import android.view.Gravity;
39import android.view.LayoutInflater;
40import android.view.MotionEvent;
41import android.view.View;
42import android.view.ViewGroup;
John Spurlock86005342014-05-23 11:58:00 -040043import android.view.ViewStub;
John Spurlock3346a802014-05-20 16:25:37 -040044import android.view.Window;
45import android.view.WindowManager;
Amith Yamasani284e6302011-09-16 18:24:47 -070046import android.view.WindowManager.LayoutParams;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047import android.widget.ImageView;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080048import android.widget.SeekBar;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080049import android.widget.SeekBar.OnSeekBarChangeListener;
50
John Spurlock86005342014-05-23 11:58:00 -040051import com.android.internal.R;
52import com.android.systemui.statusbar.policy.ZenModeController;
53
Amith Yamasani2bbdd772011-02-02 18:54:13 -080054import java.util.HashMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055
56/**
John Spurlock3346a802014-05-20 16:25:37 -040057 * Handles the user interface for the volume keys.
Dianne Hackborne8ecde12011-08-03 18:55:19 -070058 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059 * @hide
60 */
John Spurlock3346a802014-05-20 16:25:37 -040061public class VolumePanel extends Handler {
Marco Nelissen69f593c2009-07-28 09:55:04 -070062 private static boolean LOGD = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063
John Spurlock3346a802014-05-20 16:25:37 -040064 private static final int PLAY_SOUND_DELAY = AudioService.PLAY_SOUND_DELAY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065
66 /**
67 * The delay before vibrating. This small period exists so if the user is
68 * moving to silent mode, it will not emit a short vibrate (it normally
69 * would since vibrate is between normal mode and silent mode using hardware
70 * keys).
71 */
72 public static final int VIBRATE_DELAY = 300;
73
74 private static final int VIBRATE_DURATION = 300;
75 private static final int BEEP_DURATION = 150;
76 private static final int MAX_VOLUME = 100;
77 private static final int FREE_DELAY = 10000;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080078 private static final int TIMEOUT_DELAY = 3000;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079
80 private static final int MSG_VOLUME_CHANGED = 0;
81 private static final int MSG_FREE_RESOURCES = 1;
82 private static final int MSG_PLAY_SOUND = 2;
83 private static final int MSG_STOP_SOUNDS = 3;
84 private static final int MSG_VIBRATE = 4;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080085 private static final int MSG_TIMEOUT = 5;
86 private static final int MSG_RINGER_MODE_CHANGED = 6;
Mike Lockwoodce952c82011-11-14 10:47:42 -080087 private static final int MSG_MUTE_CHANGED = 7;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -070088 private static final int MSG_REMOTE_VOLUME_CHANGED = 8;
89 private static final int MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN = 9;
90 private static final int MSG_SLIDER_VISIBILITY_CHANGED = 10;
Eric Laurentc34dcc12012-09-10 13:51:52 -070091 private static final int MSG_DISPLAY_SAFE_VOLUME_WARNING = 11;
John Spurlock86005342014-05-23 11:58:00 -040092 private static final int MSG_LAYOUT_DIRECTION = 12;
93 private static final int MSG_ZEN_MODE_CHANGED = 13;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080094
Mike Lockwood8dc1dab2011-10-27 09:52:41 -040095 // Pseudo stream type for master volume
Mike Lockwood47676902011-11-08 10:31:21 -080096 private static final int STREAM_MASTER = -100;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -070097 // Pseudo stream type for remote volume is defined in AudioService.STREAM_REMOTE_MUSIC
Mike Lockwood8dc1dab2011-10-27 09:52:41 -040098
John Spurlock86005342014-05-23 11:58:00 -040099 private final String mTag;
John Spurlock3346a802014-05-20 16:25:37 -0400100 protected final Context mContext;
101 private final AudioManager mAudioManager;
John Spurlock86005342014-05-23 11:58:00 -0400102 private final ZenModeController mZenController;
Marco Nelissen69f593c2009-07-28 09:55:04 -0700103 private boolean mRingIsSilent;
Amith Yamasani71def772011-10-12 12:25:24 -0700104 private boolean mVoiceCapable;
John Spurlock86005342014-05-23 11:58:00 -0400105 private boolean mZenModeCapable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800106
Christopher Tatec4b78d22012-05-22 13:57:58 -0700107 // True if we want to play tones on the system stream when the master stream is specified.
108 private final boolean mPlayMasterStreamTones;
109
John Spurlock86005342014-05-23 11:58:00 -0400110
111 /** Volume panel content view */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112 private final View mView;
John Spurlock86005342014-05-23 11:58:00 -0400113 /** Dialog hosting the panel, if not embedded */
114 private final Dialog mDialog;
115 /** Parent view hosting the panel, if embedded */
116 private final ViewGroup mParent;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800117
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -0700118 /** The visible portion of the volume overlay */
119 private final ViewGroup mPanel;
John Spurlock86005342014-05-23 11:58:00 -0400120 /** Contains the slider and its touchable icons */
121 private final ViewGroup mSliderPanel;
122 /** The button that expands the dialog to show the zen panel */
123 private final ImageView mExpandButton;
124 /** Dummy divider icon that needs to vanish with the expand button */
125 private final View mExpandDivider;
126 /** The zen mode configuration panel view stub */
127 private final ViewStub mZenPanelStub;
128 /** The zen mode configuration panel view, once inflated */
129 private ZenModePanel mZenPanel;
130 /** Dummy divider icon that needs to vanish with the zen panel */
131 private final View mZenPanelDivider;
132
133 private ZenModePanel.Callback mZenPanelCallback;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800134
135 /** Currently active stream that shows up at the top of the list of sliders */
136 private int mActiveStreamType = -1;
137 /** All the slider controls mapped by stream type */
138 private HashMap<Integer,StreamControl> mStreamControls;
139
Amith Yamasani71def772011-10-12 12:25:24 -0700140 private enum StreamResources {
141 BluetoothSCOStream(AudioManager.STREAM_BLUETOOTH_SCO,
142 R.string.volume_icon_description_bluetooth,
143 R.drawable.ic_audio_bt,
144 R.drawable.ic_audio_bt,
145 false),
146 RingerStream(AudioManager.STREAM_RING,
147 R.string.volume_icon_description_ringer,
John Spurlock86005342014-05-23 11:58:00 -0400148 com.android.systemui.R.drawable.ic_ringer_audible,
149 com.android.systemui.R.drawable.ic_ringer_silent,
Amith Yamasani71def772011-10-12 12:25:24 -0700150 false),
151 VoiceStream(AudioManager.STREAM_VOICE_CALL,
152 R.string.volume_icon_description_incall,
153 R.drawable.ic_audio_phone,
154 R.drawable.ic_audio_phone,
155 false),
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700156 AlarmStream(AudioManager.STREAM_ALARM,
157 R.string.volume_alarm,
158 R.drawable.ic_audio_alarm,
159 R.drawable.ic_audio_alarm_mute,
160 false),
Amith Yamasani71def772011-10-12 12:25:24 -0700161 MediaStream(AudioManager.STREAM_MUSIC,
162 R.string.volume_icon_description_media,
163 R.drawable.ic_audio_vol,
164 R.drawable.ic_audio_vol_mute,
165 true),
166 NotificationStream(AudioManager.STREAM_NOTIFICATION,
167 R.string.volume_icon_description_notification,
John Spurlock86005342014-05-23 11:58:00 -0400168 com.android.systemui.R.drawable.ic_ringer_audible,
169 com.android.systemui.R.drawable.ic_ringer_silent,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400170 true),
171 // for now, use media resources for master volume
172 MasterStream(STREAM_MASTER,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700173 R.string.volume_icon_description_media, //FIXME should have its own description
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400174 R.drawable.ic_audio_vol,
175 R.drawable.ic_audio_vol_mute,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700176 false),
177 RemoteStream(AudioService.STREAM_REMOTE_MUSIC,
178 R.string.volume_icon_description_media, //FIXME should have its own description
179 R.drawable.ic_media_route_on_holo_dark,
180 R.drawable.ic_media_route_disabled_holo_dark,
181 false);// will be dynamically updated
Amith Yamasani71def772011-10-12 12:25:24 -0700182
183 int streamType;
184 int descRes;
185 int iconRes;
186 int iconMuteRes;
187 // RING, VOICE_CALL & BLUETOOTH_SCO are hidden unless explicitly requested
188 boolean show;
189
190 StreamResources(int streamType, int descRes, int iconRes, int iconMuteRes, boolean show) {
191 this.streamType = streamType;
192 this.descRes = descRes;
193 this.iconRes = iconRes;
194 this.iconMuteRes = iconMuteRes;
195 this.show = show;
196 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700197 }
Amith Yamasani71def772011-10-12 12:25:24 -0700198
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800199 // List of stream types and their order
Amith Yamasani71def772011-10-12 12:25:24 -0700200 private static final StreamResources[] STREAMS = {
201 StreamResources.BluetoothSCOStream,
202 StreamResources.RingerStream,
203 StreamResources.VoiceStream,
204 StreamResources.MediaStream,
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700205 StreamResources.NotificationStream,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400206 StreamResources.AlarmStream,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700207 StreamResources.MasterStream,
208 StreamResources.RemoteStream
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800209 };
210
211 /** Object that contains data for each slider */
212 private class StreamControl {
213 int streamType;
214 ViewGroup group;
215 ImageView icon;
216 SeekBar seekbarView;
217 int iconRes;
218 int iconMuteRes;
219 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800220
221 // Synchronize when accessing this
222 private ToneGenerator mToneGenerators[];
223 private Vibrator mVibrator;
224
Eric Laurentc34dcc12012-09-10 13:51:52 -0700225 private static AlertDialog sConfirmSafeVolumeDialog;
Eric Laurent0516a9e2012-09-19 11:53:03 -0700226 private static Object sConfirmSafeVolumeLock = new Object();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700227
228 private static class WarningDialogReceiver extends BroadcastReceiver
229 implements DialogInterface.OnDismissListener {
Eric Laurentfde16d52012-12-03 14:42:39 -0800230 private final Context mContext;
231 private final Dialog mDialog;
232 private final VolumePanel mVolumePanel;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700233
Eric Laurentfde16d52012-12-03 14:42:39 -0800234 WarningDialogReceiver(Context context, Dialog dialog, VolumePanel volumePanel) {
Eric Laurentc34dcc12012-09-10 13:51:52 -0700235 mContext = context;
236 mDialog = dialog;
Eric Laurentfde16d52012-12-03 14:42:39 -0800237 mVolumePanel = volumePanel;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700238 IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
239 context.registerReceiver(this, filter);
240 }
241
242 @Override
243 public void onReceive(Context context, Intent intent) {
244 mDialog.cancel();
Eric Laurentfde16d52012-12-03 14:42:39 -0800245 cleanUp();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700246 }
247
Alan Viverette494fb7b2014-04-10 18:12:56 -0700248 @Override
Eric Laurentc34dcc12012-09-10 13:51:52 -0700249 public void onDismiss(DialogInterface unused) {
250 mContext.unregisterReceiver(this);
Eric Laurentfde16d52012-12-03 14:42:39 -0800251 cleanUp();
252 }
253
254 private void cleanUp() {
Eric Laurent0516a9e2012-09-19 11:53:03 -0700255 synchronized (sConfirmSafeVolumeLock) {
256 sConfirmSafeVolumeDialog = null;
257 }
Eric Laurentfde16d52012-12-03 14:42:39 -0800258 mVolumePanel.forceTimeout();
259 mVolumePanel.updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700260 }
261 }
262
263
John Spurlock86005342014-05-23 11:58:00 -0400264 public VolumePanel(Context context, ViewGroup parent, ZenModeController zenController) {
265 mTag = String.format("VolumePanel%s.%08x", parent == null ? "Dialog" : "", hashCode());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800266 mContext = context;
John Spurlock86005342014-05-23 11:58:00 -0400267 mParent = parent;
268 mZenController = zenController;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800269 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800270
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400271 // For now, only show master volume if master volume is supported
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700272 final Resources res = context.getResources();
273 final boolean useMasterVolume = res.getBoolean(R.bool.config_useMasterVolume);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400274 if (useMasterVolume) {
275 for (int i = 0; i < STREAMS.length; i++) {
276 StreamResources streamRes = STREAMS[i];
277 streamRes.show = (streamRes.streamType == STREAM_MASTER);
278 }
279 }
John Spurlock86005342014-05-23 11:58:00 -0400280 if (LOGD) Log.d(mTag, String.format("new VolumePanel hasParent=%s", parent != null));
281 final int layoutId = com.android.systemui.R.layout.volume_panel;
282 if (parent == null) {
283 // dialog mode
284 mDialog = new Dialog(context) {
285 @Override
286 public boolean onTouchEvent(MotionEvent event) {
287 if (isShowing() && event.getAction() == MotionEvent.ACTION_OUTSIDE &&
288 sConfirmSafeVolumeDialog == null) {
289 forceTimeout();
290 return true;
291 }
292 return false;
Amith Yamasani284e6302011-09-16 18:24:47 -0700293 }
John Spurlock86005342014-05-23 11:58:00 -0400294 };
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700295
John Spurlock86005342014-05-23 11:58:00 -0400296 // Change some window properties
297 final Window window = mDialog.getWindow();
298 final LayoutParams lp = window.getAttributes();
299 lp.token = null;
300 // Offset from the top
301 lp.y = res.getDimensionPixelOffset(com.android.systemui.R.dimen.volume_panel_top);
302 lp.width = res.getDimensionPixelSize(com.android.systemui.R.dimen.volume_panel_width);
303 lp.type = LayoutParams.TYPE_VOLUME_OVERLAY;
304 lp.windowAnimations = R.style.Animation_VolumePanel;
305 window.setBackgroundDrawableResource(com.android.systemui.R.drawable.qs_panel_background);
306 window.setAttributes(lp);
307 window.setGravity(Gravity.TOP);
308 window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
309 window.requestFeature(Window.FEATURE_NO_TITLE);
310 window.addFlags(LayoutParams.FLAG_NOT_FOCUSABLE
311 | LayoutParams.FLAG_NOT_TOUCH_MODAL
312 | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
313 mDialog.setCanceledOnTouchOutside(true);
314 mDialog.setContentView(layoutId);
315 mDialog.setOnDismissListener(new OnDismissListener() {
316 @Override
317 public void onDismiss(DialogInterface dialog) {
318 mActiveStreamType = -1;
319 mAudioManager.forceVolumeControlStream(mActiveStreamType);
320 }
321 });
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700322
John Spurlock86005342014-05-23 11:58:00 -0400323 mDialog.create();
Alan Viverette494fb7b2014-04-10 18:12:56 -0700324
John Spurlock86005342014-05-23 11:58:00 -0400325 mView = window.findViewById(R.id.content);
326 mView.setOnTouchListener(new View.OnTouchListener() {
327 @Override
328 public boolean onTouch(View v, MotionEvent event) {
329 resetTimeout();
330 return false;
331 }
332 });
Alan Viverette494fb7b2014-04-10 18:12:56 -0700333
John Spurlock86005342014-05-23 11:58:00 -0400334 } else {
335 // embedded mode
336 mDialog = null;
337 mView = LayoutInflater.from(mContext).inflate(layoutId, parent, true);
338 }
339 mPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.visible_panel);
340 mSliderPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.slider_panel);
341 mExpandButton = (ImageView) mView.findViewById(com.android.systemui.R.id.expand_button);
342 mExpandDivider = mView.findViewById(com.android.systemui.R.id.expand_button_divider);
343 mZenPanelStub = (ViewStub)mView.findViewById(com.android.systemui.R.id.zen_panel_stub);
344 mZenPanelDivider = mView.findViewById(com.android.systemui.R.id.zen_panel_divider);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800345
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800346 mToneGenerators = new ToneGenerator[AudioSystem.getNumStreamTypes()];
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700347 mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
Amith Yamasani71def772011-10-12 12:25:24 -0700348 mVoiceCapable = context.getResources().getBoolean(R.bool.config_voice_capable);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700349
John Spurlock86005342014-05-23 11:58:00 -0400350 mZenModeCapable = !useMasterVolume && mZenController != null;
351 mZenPanelDivider.setVisibility(View.GONE);
352 mExpandButton.setOnClickListener(mClickListener);
353 updateZenMode(mZenController == null ? false : mZenController.isZen());
354 mZenController.addCallback(mZenCallback);
Amith Yamasani42722bf2011-07-22 10:34:27 -0700355
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700356 final boolean masterVolumeOnly = res.getBoolean(R.bool.config_useMasterVolume);
357 final boolean masterVolumeKeySounds = res.getBoolean(R.bool.config_useVolumeKeySounds);
Christopher Tatec4b78d22012-05-22 13:57:58 -0700358 mPlayMasterStreamTones = masterVolumeOnly && masterVolumeKeySounds;
359
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800360 listenToRingerMode();
361 }
362
John Spurlock86005342014-05-23 11:58:00 -0400363 private void setLayoutDirection(int layoutDirection) {
Fabrice Di Meglio8c028842013-01-09 18:20:38 -0800364 mPanel.setLayoutDirection(layoutDirection);
365 updateStates();
366 }
367
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800368 private void listenToRingerMode() {
369 final IntentFilter filter = new IntentFilter();
370 filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
371 mContext.registerReceiver(new BroadcastReceiver() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700372 @Override
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800373 public void onReceive(Context context, Intent intent) {
374 final String action = intent.getAction();
375
376 if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) {
377 removeMessages(MSG_RINGER_MODE_CHANGED);
378 sendMessage(obtainMessage(MSG_RINGER_MODE_CHANGED));
379 }
380 }
381 }, filter);
382 }
383
384 private boolean isMuted(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400385 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700386 return mAudioManager.isMasterMute();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700387 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
John Spurlock3346a802014-05-20 16:25:37 -0400388 return (mAudioManager.getRemoteStreamVolume() <= 0);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400389 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700390 return mAudioManager.isStreamMute(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400391 }
392 }
393
394 private int getStreamMaxVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400395 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700396 return mAudioManager.getMasterMaxVolume();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700397 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
John Spurlock3346a802014-05-20 16:25:37 -0400398 return mAudioManager.getRemoteStreamMaxVolume();
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400399 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700400 return mAudioManager.getStreamMaxVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400401 }
402 }
403
404 private int getStreamVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400405 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700406 return mAudioManager.getMasterVolume();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700407 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
John Spurlock3346a802014-05-20 16:25:37 -0400408 return mAudioManager.getRemoteStreamVolume();
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400409 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700410 return mAudioManager.getStreamVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400411 }
412 }
413
414 private void setStreamVolume(int streamType, int index, int flags) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400415 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700416 mAudioManager.setMasterVolume(index, flags);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700417 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
John Spurlock3346a802014-05-20 16:25:37 -0400418 mAudioManager.setRemoteStreamVolume(index);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400419 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700420 mAudioManager.setStreamVolume(streamType, index, flags);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400421 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800422 }
423
424 private void createSliders() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700425 final Resources res = mContext.getResources();
426 final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
427 Context.LAYOUT_INFLATER_SERVICE);
428
Amith Yamasani71def772011-10-12 12:25:24 -0700429 mStreamControls = new HashMap<Integer, StreamControl>(STREAMS.length);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700430
Amith Yamasani71def772011-10-12 12:25:24 -0700431 for (int i = 0; i < STREAMS.length; i++) {
432 StreamResources streamRes = STREAMS[i];
Alan Viverette494fb7b2014-04-10 18:12:56 -0700433
434 final int streamType = streamRes.streamType;
Alan Viverette494fb7b2014-04-10 18:12:56 -0700435
436 final StreamControl sc = new StreamControl();
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700437 sc.streamType = streamType;
John Spurlock86005342014-05-23 11:58:00 -0400438 sc.group = (ViewGroup) inflater.inflate(
439 com.android.systemui.R.layout.volume_panel_item, null);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800440 sc.group.setTag(sc);
John Spurlock86005342014-05-23 11:58:00 -0400441 sc.icon = (ImageView) sc.group.findViewById(com.android.systemui.R.id.stream_icon);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800442 sc.icon.setTag(sc);
Amith Yamasani71def772011-10-12 12:25:24 -0700443 sc.icon.setContentDescription(res.getString(streamRes.descRes));
444 sc.iconRes = streamRes.iconRes;
445 sc.iconMuteRes = streamRes.iconMuteRes;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800446 sc.icon.setImageResource(sc.iconRes);
John Spurlock86005342014-05-23 11:58:00 -0400447 sc.seekbarView = (SeekBar) sc.group.findViewById(com.android.systemui.R.id.seekbar);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700448 final int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO ||
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700449 streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400450 sc.seekbarView.setMax(getStreamMaxVolume(streamType) + plusOne);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700451 sc.seekbarView.setOnSeekBarChangeListener(mSeekListener);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800452 sc.seekbarView.setTag(sc);
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700453 mStreamControls.put(streamType, sc);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800454 }
455 }
456
457 private void reorderSliders(int activeStreamType) {
John Spurlock86005342014-05-23 11:58:00 -0400458 mSliderPanel.removeAllViews();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800459
Alan Viverette494fb7b2014-04-10 18:12:56 -0700460 final StreamControl active = mStreamControls.get(activeStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800461 if (active == null) {
462 Log.e("VolumePanel", "Missing stream type! - " + activeStreamType);
463 mActiveStreamType = -1;
464 } else {
John Spurlock86005342014-05-23 11:58:00 -0400465 mSliderPanel.addView(active.group);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800466 mActiveStreamType = activeStreamType;
467 active.group.setVisibility(View.VISIBLE);
468 updateSlider(active);
John Spurlock86005342014-05-23 11:58:00 -0400469 updateZenMode(mZenController == null ? false : mZenController.isZen());
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800470 }
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) {
John Spurlock86005342014-05-23 11:58:00 -0400483 sc.icon.setImageResource(com.android.systemui.R.drawable.ic_ringer_vibrate);
Amith Yamasanic696a532011-10-28 17:02:37 -0700484 }
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
John Spurlock86005342014-05-23 11:58:00 -0400497 public void setZenModePanelCallback(ZenModePanel.Callback callback) {
498 mZenPanelCallback = callback;
499 }
500
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800501 private void expand() {
John Spurlock86005342014-05-23 11:58:00 -0400502 if (LOGD) Log.d(mTag, "expand mZenPanel=" + mZenPanel);
503 if (mZenPanel == null) {
504 mZenPanel = (ZenModePanel) mZenPanelStub.inflate();
505 mZenPanel.init(mZenController);
506 mZenPanel.setCallback(new ZenModePanel.Callback() {
507 @Override
508 public void onMoreSettings() {
509 if (mZenPanelCallback != null) {
510 mZenPanelCallback.onMoreSettings();
511 }
512 }
513
514 @Override
515 public void onInteraction() {
516 if (mZenPanelCallback != null) {
517 mZenPanelCallback.onInteraction();
518 }
519 }
520 });
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800521 }
John Spurlock86005342014-05-23 11:58:00 -0400522 mZenPanel.setVisibility(View.VISIBLE);
523 mZenPanelDivider.setVisibility(View.VISIBLE);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800524 }
525
526 private void collapse() {
John Spurlock86005342014-05-23 11:58:00 -0400527 if (LOGD) Log.d(mTag, "collapse mZenPanel=" + mZenPanel);
528 if (mZenPanel != null) {
529 mZenPanel.setVisibility(View.GONE);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800530 }
John Spurlock86005342014-05-23 11:58:00 -0400531 mZenPanelDivider.setVisibility(View.GONE);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800532 }
533
Eric Laurentfde16d52012-12-03 14:42:39 -0800534 public void updateStates() {
John Spurlock86005342014-05-23 11:58:00 -0400535 final int count = mSliderPanel.getChildCount();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800536 for (int i = 0; i < count; i++) {
John Spurlock86005342014-05-23 11:58:00 -0400537 StreamControl sc = (StreamControl) mSliderPanel.getChildAt(i).getTag();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800538 updateSlider(sc);
539 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800540 }
541
John Spurlock86005342014-05-23 11:58:00 -0400542 private void updateZenMode(boolean zen) {
543 if (mZenModeCapable) {
544 final boolean show = mActiveStreamType == AudioManager.STREAM_NOTIFICATION
545 || mActiveStreamType == AudioManager.STREAM_RING;
546 mExpandButton.setVisibility(show ? View.VISIBLE : View.GONE);
547 mExpandDivider.setVisibility(show ? View.VISIBLE : View.GONE);
548 mExpandButton.setImageResource(zen ? com.android.systemui.R.drawable.ic_vol_zen_on
549 : com.android.systemui.R.drawable.ic_vol_zen_off);
550 } else {
551 mExpandButton.setVisibility(View.GONE);
552 mExpandDivider.setVisibility(View.GONE);
553 }
554 }
555
556 public void postZenModeChanged(boolean zen) {
557 removeMessages(MSG_ZEN_MODE_CHANGED);
558 obtainMessage(MSG_ZEN_MODE_CHANGED, zen ? 1 : 0).sendToTarget();
559 }
560
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800561 public void postVolumeChanged(int streamType, int flags) {
562 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700563 synchronized (this) {
564 if (mStreamControls == null) {
565 createSliders();
566 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800567 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800568 removeMessages(MSG_FREE_RESOURCES);
569 obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget();
570 }
571
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700572 public void postRemoteVolumeChanged(int streamType, int flags) {
573 if (hasMessages(MSG_REMOTE_VOLUME_CHANGED)) return;
574 synchronized (this) {
575 if (mStreamControls == null) {
576 createSliders();
577 }
578 }
579 removeMessages(MSG_FREE_RESOURCES);
580 obtainMessage(MSG_REMOTE_VOLUME_CHANGED, streamType, flags).sendToTarget();
581 }
582
583 public void postRemoteSliderVisibility(boolean visible) {
584 obtainMessage(MSG_SLIDER_VISIBILITY_CHANGED,
585 AudioService.STREAM_REMOTE_MUSIC, visible ? 1 : 0).sendToTarget();
586 }
587
588 /**
589 * Called by AudioService when it has received new remote playback information that
590 * would affect the VolumePanel display (mainly volumes). The difference with
591 * {@link #postRemoteVolumeChanged(int, int)} is that the handling of the posted message
592 * (MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN) will only update the volume slider if it is being
593 * displayed.
594 * This special code path is due to the fact that remote volume updates arrive to AudioService
595 * asynchronously. So after AudioService has sent the volume update (which should be treated
596 * as a request to update the volume), the application will likely set a new volume. If the UI
597 * is still up, we need to refresh the display to show this new value.
598 */
599 public void postHasNewRemotePlaybackInfo() {
600 if (hasMessages(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN)) return;
601 // don't create or prevent resources to be freed, if they disappear, this update came too
602 // late and shouldn't warrant the panel to be displayed longer
603 obtainMessage(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN).sendToTarget();
604 }
605
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400606 public void postMasterVolumeChanged(int flags) {
607 postVolumeChanged(STREAM_MASTER, flags);
608 }
609
Mike Lockwoodce952c82011-11-14 10:47:42 -0800610 public void postMuteChanged(int streamType, int flags) {
611 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700612 synchronized (this) {
613 if (mStreamControls == null) {
614 createSliders();
615 }
Mike Lockwoodce952c82011-11-14 10:47:42 -0800616 }
617 removeMessages(MSG_FREE_RESOURCES);
618 obtainMessage(MSG_MUTE_CHANGED, streamType, flags).sendToTarget();
619 }
620
621 public void postMasterMuteChanged(int flags) {
622 postMuteChanged(STREAM_MASTER, flags);
623 }
624
Eric Laurentfde16d52012-12-03 14:42:39 -0800625 public void postDisplaySafeVolumeWarning(int flags) {
Eric Laurent0516a9e2012-09-19 11:53:03 -0700626 if (hasMessages(MSG_DISPLAY_SAFE_VOLUME_WARNING)) return;
Eric Laurentfde16d52012-12-03 14:42:39 -0800627 obtainMessage(MSG_DISPLAY_SAFE_VOLUME_WARNING, flags, 0).sendToTarget();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700628 }
629
John Spurlock3346a802014-05-20 16:25:37 -0400630 public void postDismiss() {
John Spurlock86005342014-05-23 11:58:00 -0400631 forceTimeout();
632 }
633
634 public void postLayoutDirection(int layoutDirection) {
635 removeMessages(MSG_LAYOUT_DIRECTION);
636 obtainMessage(MSG_LAYOUT_DIRECTION, layoutDirection).sendToTarget();
John Spurlock3346a802014-05-20 16:25:37 -0400637 }
638
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800639 /**
640 * Override this if you have other work to do when the volume changes (for
641 * example, vibrating, playing a sound, etc.). Make sure to call through to
642 * the superclass implementation.
643 */
644 protected void onVolumeChanged(int streamType, int flags) {
645
John Spurlock86005342014-05-23 11:58:00 -0400646 if (LOGD) Log.d(mTag, "onVolumeChanged(streamType: " + streamType + ", flags: " + flags + ")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800647
648 if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
Amith Yamasania6549862012-05-30 17:29:28 -0700649 synchronized (this) {
650 if (mActiveStreamType != streamType) {
651 reorderSliders(streamType);
652 }
653 onShowVolumeChanged(streamType, flags);
Amith Yamasanie3361b82011-02-10 18:20:50 -0800654 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800655 }
656
Marco Nelissen69f593c2009-07-28 09:55:04 -0700657 if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800658 removeMessages(MSG_PLAY_SOUND);
659 sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);
660 }
661
662 if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) {
663 removeMessages(MSG_PLAY_SOUND);
664 removeMessages(MSG_VIBRATE);
665 onStopSounds();
666 }
667
668 removeMessages(MSG_FREE_RESOURCES);
669 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800670 resetTimeout();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800671 }
672
Mike Lockwoodce952c82011-11-14 10:47:42 -0800673 protected void onMuteChanged(int streamType, int flags) {
674
John Spurlock86005342014-05-23 11:58:00 -0400675 if (LOGD) Log.d(mTag, "onMuteChanged(streamType: " + streamType + ", flags: " + flags + ")");
Mike Lockwoodce952c82011-11-14 10:47:42 -0800676
677 StreamControl sc = mStreamControls.get(streamType);
678 if (sc != null) {
679 sc.icon.setImageResource(isMuted(sc.streamType) ? sc.iconMuteRes : sc.iconRes);
680 }
681
682 onVolumeChanged(streamType, flags);
683 }
684
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800685 protected void onShowVolumeChanged(int streamType, int flags) {
Eric Laurent8c787522012-05-14 14:09:43 -0700686 int index = getStreamVolume(streamType);
Eric Laurentd72d51c2011-02-03 18:47:47 -0800687
Marco Nelissen69f593c2009-07-28 09:55:04 -0700688 mRingIsSilent = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800689
690 if (LOGD) {
John Spurlock86005342014-05-23 11:58:00 -0400691 Log.d(mTag, "onShowVolumeChanged(streamType: " + streamType
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800692 + ", flags: " + flags + "), index: " + index);
693 }
694
695 // get max volume for progress bar
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800696
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400697 int max = getStreamMaxVolume(streamType);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800698
699 switch (streamType) {
700
701 case AudioManager.STREAM_RING: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800702// setRingerIcon();
Marco Nelissen69f593c2009-07-28 09:55:04 -0700703 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
704 mContext, RingtoneManager.TYPE_RINGTONE);
705 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700706 mRingIsSilent = true;
707 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800708 break;
709 }
710
711 case AudioManager.STREAM_MUSIC: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800712 // Special case for when Bluetooth is active for music
Glenn Kasten8b4b97a2011-02-04 13:54:26 -0800713 if ((mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC) &
714 (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP |
715 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
716 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800717 setMusicIcon(R.drawable.ic_audio_bt, R.drawable.ic_audio_bt_mute);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800718 } else {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800719 setMusicIcon(R.drawable.ic_audio_vol, R.drawable.ic_audio_vol_mute);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800720 }
721 break;
722 }
723
724 case AudioManager.STREAM_VOICE_CALL: {
725 /*
726 * For in-call voice call volume, there is no inaudible volume.
727 * Rescale the UI control so the progress bar doesn't go all
728 * the way to zero and don't show the mute icon.
729 */
730 index++;
731 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800732 break;
733 }
734
735 case AudioManager.STREAM_ALARM: {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800736 break;
737 }
738
739 case AudioManager.STREAM_NOTIFICATION: {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700740 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
741 mContext, RingtoneManager.TYPE_NOTIFICATION);
742 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700743 mRingIsSilent = true;
744 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800745 break;
746 }
747
748 case AudioManager.STREAM_BLUETOOTH_SCO: {
749 /*
750 * For in-call voice call volume, there is no inaudible volume.
751 * Rescale the UI control so the progress bar doesn't go all
752 * the way to zero and don't show the mute icon.
753 */
754 index++;
755 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800756 break;
757 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700758
759 case AudioService.STREAM_REMOTE_MUSIC: {
John Spurlock86005342014-05-23 11:58:00 -0400760 if (LOGD) { Log.d(mTag, "showing remote volume "+index+" over "+ max); }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700761 break;
762 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800763 }
764
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800765 StreamControl sc = mStreamControls.get(streamType);
766 if (sc != null) {
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700767 if (sc.seekbarView.getMax() != max) {
768 sc.seekbarView.setMax(max);
769 }
Eric Laurent4bbcc652012-09-24 14:26:30 -0700770
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800771 sc.seekbarView.setProgress(index);
Eric Laurent4bbcc652012-09-24 14:26:30 -0700772 if (((flags & AudioManager.FLAG_FIXED_VOLUME) != 0) ||
773 (streamType != mAudioManager.getMasterStreamType() &&
774 streamType != AudioService.STREAM_REMOTE_MUSIC &&
Eric Laurentfde16d52012-12-03 14:42:39 -0800775 isMuted(streamType)) ||
776 sConfirmSafeVolumeDialog != null) {
Eric Laurent8c787522012-05-14 14:09:43 -0700777 sc.seekbarView.setEnabled(false);
778 } else {
779 sc.seekbarView.setEnabled(true);
780 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800781 }
782
John Spurlock86005342014-05-23 11:58:00 -0400783 if (!isShowing()) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700784 int stream = (streamType == AudioService.STREAM_REMOTE_MUSIC) ? -1 : streamType;
785 // when the stream is for remote playback, use -1 to reset the stream type evaluation
786 mAudioManager.forceVolumeControlStream(stream);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700787
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800788 // Showing dialog - use collapsed state
John Spurlock86005342014-05-23 11:58:00 -0400789 if (mZenModeCapable) {
Amith Yamasani42722bf2011-07-22 10:34:27 -0700790 collapse();
791 }
John Spurlock86005342014-05-23 11:58:00 -0400792 if (mDialog != null) {
793 mDialog.show();
794 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800795 }
796
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800797 // Do a little vibrate if applicable (only when going into vibrate mode)
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700798 if ((streamType != AudioService.STREAM_REMOTE_MUSIC) &&
799 ((flags & AudioManager.FLAG_VIBRATE) != 0) &&
John Spurlock3346a802014-05-20 16:25:37 -0400800 mAudioManager.isStreamAffectedByRingerMode(streamType) &&
Eric Laurent8c787522012-05-14 14:09:43 -0700801 mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800802 sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY);
803 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800804 }
805
John Spurlock86005342014-05-23 11:58:00 -0400806 private boolean isShowing() {
807 return mDialog != null ? mDialog.isShowing() : mParent.isAttachedToWindow();
808 }
809
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800810 protected void onPlaySound(int streamType, int flags) {
811
812 if (hasMessages(MSG_STOP_SOUNDS)) {
813 removeMessages(MSG_STOP_SOUNDS);
814 // Force stop right now
815 onStopSounds();
816 }
817
818 synchronized (this) {
819 ToneGenerator toneGen = getOrCreateToneGenerator(streamType);
Eric Laurent733a42b2011-01-19 10:41:57 -0800820 if (toneGen != null) {
821 toneGen.startTone(ToneGenerator.TONE_PROP_BEEP);
822 sendMessageDelayed(obtainMessage(MSG_STOP_SOUNDS), BEEP_DURATION);
823 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800824 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800825 }
826
827 protected void onStopSounds() {
828
829 synchronized (this) {
830 int numStreamTypes = AudioSystem.getNumStreamTypes();
831 for (int i = numStreamTypes - 1; i >= 0; i--) {
832 ToneGenerator toneGen = mToneGenerators[i];
833 if (toneGen != null) {
834 toneGen.stopTone();
835 }
836 }
837 }
838 }
839
840 protected void onVibrate() {
841
842 // Make sure we ended up in vibrate ringer mode
Eric Laurent8c787522012-05-14 14:09:43 -0700843 if (mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800844 return;
845 }
846
John Spurlockf9e1a0b2014-03-19 22:09:06 -0400847 mVibrator.vibrate(VIBRATE_DURATION, AudioManager.STREAM_SYSTEM);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800848 }
849
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700850 protected void onRemoteVolumeChanged(int streamType, int flags) {
851 // streamType is the real stream type being affected, but for the UI sliders, we
852 // refer to AudioService.STREAM_REMOTE_MUSIC. We still play the beeps on the real
853 // stream type.
John Spurlock86005342014-05-23 11:58:00 -0400854 if (LOGD) Log.d(mTag, "onRemoteVolumeChanged(stream:"+streamType+", flags: " + flags + ")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700855
John Spurlock86005342014-05-23 11:58:00 -0400856 if (((flags & AudioManager.FLAG_SHOW_UI) != 0) || isShowing()) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700857 synchronized (this) {
858 if (mActiveStreamType != AudioService.STREAM_REMOTE_MUSIC) {
859 reorderSliders(AudioService.STREAM_REMOTE_MUSIC);
860 }
861 onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, flags);
862 }
863 } else {
John Spurlock86005342014-05-23 11:58:00 -0400864 if (LOGD) Log.d(mTag, "not calling onShowVolumeChanged(), no FLAG_SHOW_UI or no UI");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700865 }
866
867 if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {
868 removeMessages(MSG_PLAY_SOUND);
869 sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);
870 }
871
872 if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) {
873 removeMessages(MSG_PLAY_SOUND);
874 removeMessages(MSG_VIBRATE);
875 onStopSounds();
876 }
877
878 removeMessages(MSG_FREE_RESOURCES);
879 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700880 resetTimeout();
881 }
882
883 protected void onRemoteVolumeUpdateIfShown() {
John Spurlock86005342014-05-23 11:58:00 -0400884 if (LOGD) Log.d(mTag, "onRemoteVolumeUpdateIfShown()");
885 if (isShowing()
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700886 && (mActiveStreamType == AudioService.STREAM_REMOTE_MUSIC)
887 && (mStreamControls != null)) {
888 onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, 0);
889 }
890 }
891
892
893 /**
894 * Handler for MSG_SLIDER_VISIBILITY_CHANGED
895 * Hide or show a slider
896 * @param streamType can be a valid stream type value, or VolumePanel.STREAM_MASTER,
897 * or AudioService.STREAM_REMOTE_MUSIC
898 * @param visible
899 */
900 synchronized protected void onSliderVisibilityChanged(int streamType, int visible) {
John Spurlock86005342014-05-23 11:58:00 -0400901 if (LOGD) Log.d(mTag, "onSliderVisibilityChanged(stream="+streamType+", visi="+visible+")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700902 boolean isVisible = (visible == 1);
903 for (int i = STREAMS.length - 1 ; i >= 0 ; i--) {
904 StreamResources streamRes = STREAMS[i];
905 if (streamRes.streamType == streamType) {
906 streamRes.show = isVisible;
907 if (!isVisible && (mActiveStreamType == streamType)) {
908 mActiveStreamType = -1;
909 }
910 break;
911 }
912 }
913 }
914
Eric Laurentfde16d52012-12-03 14:42:39 -0800915 protected void onDisplaySafeVolumeWarning(int flags) {
John Spurlock86005342014-05-23 11:58:00 -0400916 if ((flags & AudioManager.FLAG_SHOW_UI) != 0 || isShowing()) {
Eric Laurentfde16d52012-12-03 14:42:39 -0800917 synchronized (sConfirmSafeVolumeLock) {
918 if (sConfirmSafeVolumeDialog != null) {
919 return;
920 }
921 sConfirmSafeVolumeDialog = new AlertDialog.Builder(mContext)
922 .setMessage(com.android.internal.R.string.safe_media_volume_warning)
923 .setPositiveButton(com.android.internal.R.string.yes,
924 new DialogInterface.OnClickListener() {
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700925 @Override
Eric Laurentfde16d52012-12-03 14:42:39 -0800926 public void onClick(DialogInterface dialog, int which) {
John Spurlock3346a802014-05-20 16:25:37 -0400927 mAudioManager.disableSafeMediaVolume();
Eric Laurentfde16d52012-12-03 14:42:39 -0800928 }
929 })
930 .setNegativeButton(com.android.internal.R.string.no, null)
931 .setIconAttribute(android.R.attr.alertDialogIcon)
932 .create();
933 final WarningDialogReceiver warning = new WarningDialogReceiver(mContext,
934 sConfirmSafeVolumeDialog, this);
Eric Laurent0516a9e2012-09-19 11:53:03 -0700935
Eric Laurentfde16d52012-12-03 14:42:39 -0800936 sConfirmSafeVolumeDialog.setOnDismissListener(warning);
937 sConfirmSafeVolumeDialog.getWindow().setType(
938 WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
939 sConfirmSafeVolumeDialog.show();
940 }
941 updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700942 }
Eric Laurentfde16d52012-12-03 14:42:39 -0800943 resetTimeout();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700944 }
945
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800946 /**
947 * Lock on this VolumePanel instance as long as you use the returned ToneGenerator.
948 */
949 private ToneGenerator getOrCreateToneGenerator(int streamType) {
Christopher Tatec4b78d22012-05-22 13:57:58 -0700950 if (streamType == STREAM_MASTER) {
951 // For devices that use the master volume setting only but still want to
952 // play a volume-changed tone, direct the master volume pseudostream to
953 // the system stream's tone generator.
954 if (mPlayMasterStreamTones) {
955 streamType = AudioManager.STREAM_SYSTEM;
956 } else {
957 return null;
958 }
959 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800960 synchronized (this) {
961 if (mToneGenerators[streamType] == null) {
Eric Laurent733a42b2011-01-19 10:41:57 -0800962 try {
963 mToneGenerators[streamType] = new ToneGenerator(streamType, MAX_VOLUME);
964 } catch (RuntimeException e) {
965 if (LOGD) {
John Spurlock86005342014-05-23 11:58:00 -0400966 Log.d(mTag, "ToneGenerator constructor failed with "
Eric Laurent733a42b2011-01-19 10:41:57 -0800967 + "RuntimeException: " + e);
968 }
969 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800970 }
Eric Laurent733a42b2011-01-19 10:41:57 -0800971 return mToneGenerators[streamType];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800972 }
973 }
974
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800975
976 /**
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800977 * Switch between icons because Bluetooth music is same as music volume, but with
978 * different icons.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800979 */
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800980 private void setMusicIcon(int resId, int resMuteId) {
981 StreamControl sc = mStreamControls.get(AudioManager.STREAM_MUSIC);
982 if (sc != null) {
983 sc.iconRes = resId;
984 sc.iconMuteRes = resMuteId;
985 sc.icon.setImageResource(isMuted(sc.streamType) ? sc.iconMuteRes : sc.iconRes);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800986 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800987 }
988
989 protected void onFreeResources() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800990 synchronized (this) {
991 for (int i = mToneGenerators.length - 1; i >= 0; i--) {
992 if (mToneGenerators[i] != null) {
993 mToneGenerators[i].release();
994 }
995 mToneGenerators[i] = null;
996 }
997 }
998 }
999
1000 @Override
1001 public void handleMessage(Message msg) {
1002 switch (msg.what) {
1003
1004 case MSG_VOLUME_CHANGED: {
1005 onVolumeChanged(msg.arg1, msg.arg2);
1006 break;
1007 }
1008
Mike Lockwoodce952c82011-11-14 10:47:42 -08001009 case MSG_MUTE_CHANGED: {
1010 onMuteChanged(msg.arg1, msg.arg2);
1011 break;
1012 }
1013
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001014 case MSG_FREE_RESOURCES: {
1015 onFreeResources();
1016 break;
1017 }
1018
1019 case MSG_STOP_SOUNDS: {
1020 onStopSounds();
1021 break;
1022 }
1023
1024 case MSG_PLAY_SOUND: {
1025 onPlaySound(msg.arg1, msg.arg2);
1026 break;
1027 }
1028
1029 case MSG_VIBRATE: {
1030 onVibrate();
1031 break;
1032 }
1033
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001034 case MSG_TIMEOUT: {
John Spurlock86005342014-05-23 11:58:00 -04001035 if (isShowing()) {
1036 if (mDialog != null) {
1037 mDialog.dismiss();
1038 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001039 mActiveStreamType = -1;
1040 }
Eric Laurentfde16d52012-12-03 14:42:39 -08001041 synchronized (sConfirmSafeVolumeLock) {
1042 if (sConfirmSafeVolumeDialog != null) {
1043 sConfirmSafeVolumeDialog.dismiss();
1044 }
1045 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001046 break;
1047 }
1048 case MSG_RINGER_MODE_CHANGED: {
John Spurlock86005342014-05-23 11:58:00 -04001049 if (isShowing()) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001050 updateStates();
1051 }
1052 break;
1053 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001054
1055 case MSG_REMOTE_VOLUME_CHANGED: {
1056 onRemoteVolumeChanged(msg.arg1, msg.arg2);
1057 break;
1058 }
1059
1060 case MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN:
1061 onRemoteVolumeUpdateIfShown();
1062 break;
1063
1064 case MSG_SLIDER_VISIBILITY_CHANGED:
1065 onSliderVisibilityChanged(msg.arg1, msg.arg2);
1066 break;
Eric Laurentc34dcc12012-09-10 13:51:52 -07001067
1068 case MSG_DISPLAY_SAFE_VOLUME_WARNING:
Eric Laurentfde16d52012-12-03 14:42:39 -08001069 onDisplaySafeVolumeWarning(msg.arg1);
Eric Laurentc34dcc12012-09-10 13:51:52 -07001070 break;
John Spurlock86005342014-05-23 11:58:00 -04001071
1072 case MSG_LAYOUT_DIRECTION:
1073 setLayoutDirection(msg.arg1);
1074 break;
1075
1076 case MSG_ZEN_MODE_CHANGED:
1077 updateZenMode(msg.arg1 != 0);
1078 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001079 }
1080 }
1081
John Spurlock86005342014-05-23 11:58:00 -04001082 public void resetTimeout() {
1083 if (LOGD) Log.d(mTag, "resetTimeout at " + System.currentTimeMillis());
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001084 removeMessages(MSG_TIMEOUT);
John Spurlock86005342014-05-23 11:58:00 -04001085 sendEmptyMessageDelayed(MSG_TIMEOUT, TIMEOUT_DELAY);
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001086 }
1087
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001088 private void forceTimeout() {
1089 removeMessages(MSG_TIMEOUT);
John Spurlock86005342014-05-23 11:58:00 -04001090 sendEmptyMessage(MSG_TIMEOUT);
1091 }
1092
1093 public ZenModeController getZenController() {
1094 return mZenController;
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001095 }
1096
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001097 private final OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
1098 @Override
1099 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
1100 final Object tag = seekBar.getTag();
1101 if (fromUser && tag instanceof StreamControl) {
1102 StreamControl sc = (StreamControl) tag;
1103 if (getStreamVolume(sc.streamType) != progress) {
1104 setStreamVolume(sc.streamType, progress, 0);
1105 }
1106 }
1107 resetTimeout();
1108 }
1109
1110 @Override
1111 public void onStartTrackingTouch(SeekBar seekBar) {
1112 }
1113
1114 @Override
1115 public void onStopTrackingTouch(SeekBar seekBar) {
1116 final Object tag = seekBar.getTag();
1117 if (tag instanceof StreamControl) {
1118 StreamControl sc = (StreamControl) tag;
1119 // Because remote volume updates are asynchronous, AudioService
1120 // might have received a new remote volume value since the
1121 // finger adjusted the slider. So when the progress of the
1122 // slider isn't being tracked anymore, adjust the slider to the
1123 // last "published" remote volume value, so the UI reflects the
1124 // actual volume.
1125 if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
1126 seekBar.setProgress(getStreamVolume(AudioService.STREAM_REMOTE_MUSIC));
1127 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001128 }
1129 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001130 };
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001131
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001132 private final View.OnClickListener mClickListener = new View.OnClickListener() {
1133 @Override
1134 public void onClick(View v) {
John Spurlock86005342014-05-23 11:58:00 -04001135 if (v == mExpandButton && mZenController != null) {
1136 final boolean newZen = !mZenController.isZen();
1137 mZenController.setZen(newZen);
1138 if (newZen) {
1139 expand();
1140 } else {
1141 collapse();
1142 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001143 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001144 resetTimeout();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001145 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001146 };
John Spurlock86005342014-05-23 11:58:00 -04001147
1148 private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
1149 public void onZenChanged(boolean zen) {
1150 updateZenMode(zen);
1151 }
1152 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001153}